|
1 #!/usr/bin/env python |
|
2 |
|
3 #Copyright 2008 Adam A. Crossland |
|
4 # |
|
5 #Licensed under the Apache License, Version 2.0 (the "License"); |
|
6 #you may not use this file except in compliance with the License. |
|
7 #You may obtain a copy of the License at |
|
8 # |
|
9 #http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 #Unless required by applicable law or agreed to in writing, software |
|
12 #distributed under the License is distributed on an "AS IS" BASIS, |
|
13 #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 #See the License for the specific language governing permissions and |
|
15 #limitations under the License. |
|
16 |
|
17 import sys |
|
18 import os.path |
|
19 |
|
20 APPENGINE_PATH = '../../../../google/google_appengine' |
|
21 |
|
22 # Add app-engine related libraries to your path |
|
23 paths = [ |
|
24 APPENGINE_PATH, |
|
25 os.path.join(APPENGINE_PATH, 'lib', 'django'), |
|
26 os.path.join(APPENGINE_PATH, 'lib', 'webob'), |
|
27 os.path.join(APPENGINE_PATH, 'lib', 'yaml', 'lib') |
|
28 ] |
|
29 for path in paths: |
|
30 if not os.path.exists(path): |
|
31 raise 'Path does not exist: %s' % path |
|
32 sys.path = paths + sys.path |
|
33 |
|
34 import unittest |
|
35 from google.appengine.api import apiproxy_stub_map |
|
36 from google.appengine.api import datastore_file_stub |
|
37 from google.appengine.api import mail_stub |
|
38 from google.appengine.api import user_service_stub |
|
39 from google.appengine.ext import webapp |
|
40 from google.appengine.ext import db |
|
41 from google.appengine.api.memcache import memcache_stub |
|
42 from taggable import * |
|
43 |
|
44 APP_ID = u'taggable' |
|
45 AUTH_DOMAIN = 'gmail.com' |
|
46 LOGGED_IN_USER = 'me@example.com' # set to '' for no logged in user |
|
47 |
|
48 BLOG_NAME='test_blog' |
|
49 |
|
50 class BlogIndex(db.Model): |
|
51 "A global counter used to provide the index of the next blog post." |
|
52 index = db.IntegerProperty(required=True, default=0) |
|
53 "The next available index for a Post." |
|
54 |
|
55 class Post(Taggable, db.Model): |
|
56 index = db.IntegerProperty(required=True, default=0) |
|
57 body = db.TextProperty(required = True) |
|
58 title = db.StringProperty() |
|
59 added = db.DateTimeProperty(auto_now_add=True) |
|
60 added_month = db.IntegerProperty() |
|
61 added_year = db.IntegerProperty() |
|
62 edited = db.DateTimeProperty() |
|
63 |
|
64 def __init__(self, parent=None, key_name=None, app=None, **entity_values): |
|
65 db.Model.__init__(self, parent, key_name, app, **entity_values) |
|
66 Taggable.__init__(self) |
|
67 |
|
68 def get_all_posts(): |
|
69 return db.GqlQuery("SELECT * from Post ORDER BY added DESC") |
|
70 Get_All_Posts = staticmethod(get_all_posts) |
|
71 |
|
72 @classmethod |
|
73 def get_posts(cls, start_index=0, count=10): |
|
74 start_index = int(start_index) # Just make sure that we have an int |
|
75 posts = Post.gql('WHERE index <= :1 ORDER BY index DESC', start_index).fetch(count + 1) |
|
76 if len(posts) > count: |
|
77 posts = posts[:count] |
|
78 |
|
79 return posts |
|
80 |
|
81 @classmethod |
|
82 def new_post(cls, new_title=None, new_body=None, new_tags=[]): |
|
83 new_post = None |
|
84 if new_title is not None and new_body is not None: |
|
85 def txn(): |
|
86 blog_index = BlogIndex.get_by_key_name(BLOG_NAME) |
|
87 if blog_index is None: |
|
88 blog_index = BlogIndex(key_name=BLOG_NAME) |
|
89 new_index = blog_index.index |
|
90 blog_index.index += 1 |
|
91 blog_index.put() |
|
92 |
|
93 new_post_key_name = BLOG_NAME + str(new_index) |
|
94 new_post = Post(key_name=new_post_key_name, parent=blog_index, |
|
95 index = new_index, title = new_title, |
|
96 body = new_body) |
|
97 new_post.put() |
|
98 |
|
99 return new_post |
|
100 new_post = db.run_in_transaction(txn) |
|
101 |
|
102 new_post.tags = new_tags |
|
103 new_post.put() |
|
104 else: |
|
105 raise Exception("Must supply both new_title and new_body when creating a new Post.") |
|
106 |
|
107 return new_post |
|
108 |
|
109 def delete(self): |
|
110 # Perform any actions that are required to maintain data integrity |
|
111 # when this Post is delete. |
|
112 # Disassociate this Post from any Tag |
|
113 self.set_tags([]) |
|
114 |
|
115 # Finally, call the real delete |
|
116 db.Model.delete(self) |
|
117 |
|
118 class MyTest(unittest.TestCase): |
|
119 |
|
120 def setUp(self): |
|
121 # Start with a fresh api proxy. |
|
122 apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() |
|
123 |
|
124 # Use a fresh stub datastore. |
|
125 stub = datastore_file_stub.DatastoreFileStub(APP_ID, '/dev/null', '/dev/null') |
|
126 apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub) |
|
127 |
|
128 # Use a fresh memcache stub. |
|
129 apiproxy_stub_map.apiproxy.RegisterStub('memcache', memcache_stub.MemcacheServiceStub()) |
|
130 |
|
131 # Use a fresh stub UserService. |
|
132 apiproxy_stub_map.apiproxy.RegisterStub( |
|
133 'user', user_service_stub.UserServiceStub()) |
|
134 os.environ['AUTH_DOMAIN'] = AUTH_DOMAIN |
|
135 os.environ['USER_EMAIL'] = LOGGED_IN_USER |
|
136 os.environ['APPLICATION_ID'] = APP_ID |
|
137 |
|
138 def testSimpleTagAdding(self): |
|
139 new_post = Post.new_post(new_title='test post 1', new_body='This is a test post. Please ignore.') |
|
140 assert new_post is not None |
|
141 |
|
142 new_post.tags = "test, testing, tests" |
|
143 self.assertEqual(len(new_post.tags), 3) |
|
144 |
|
145 def testComplexTagAdding(self): |
|
146 new_post = Post.new_post(new_title='test post 1', new_body='This is a test post. Please ignore.') |
|
147 assert new_post is not None |
|
148 |
|
149 new_post.tags = " test, testing, tests,,,tag with spaces" |
|
150 self.assertEqual(len(new_post.tags), 4) |
|
151 |
|
152 tag = new_post.tags[3] |
|
153 assert tag is not None |
|
154 self.assertEqual(tag.tag, 'tag with spaces') |
|
155 self.assertEqual(tag.tagged_count, 1) |
|
156 |
|
157 tag2 = Tag.get_by_name('tag with spaces') |
|
158 assert tag2 is not None |
|
159 self.assertEqual(tag.tag, 'tag with spaces') |
|
160 self.assertEqual(tag.tagged_count, 1) |
|
161 |
|
162 def testTagDeletion(self): |
|
163 new_post = Post.new_post(new_title='test post 2', new_body='This is a test post. Please continue to ignore.') |
|
164 assert new_post is not None |
|
165 |
|
166 new_post.tags = "test, testing, tests" |
|
167 self.assertEqual(len(new_post.tags), 3) |
|
168 |
|
169 new_post.tags = "test" |
|
170 self.assertEqual(len(new_post.tags), 1) |
|
171 |
|
172 def testTagCounts(self): |
|
173 new_post3 = Post.new_post(new_title='test post 3', new_body='This is a test post. Please continue to ignore.') |
|
174 assert new_post3 is not None |
|
175 new_post3.tags = "foo, bar, baz" |
|
176 new_post4 = Post.new_post(new_title='test post 4', new_body='This is a test post. Please continue to ignore.') |
|
177 assert new_post4 is not None |
|
178 new_post4.tags = "bar, baz, bletch" |
|
179 new_post5 = Post.new_post(new_title='test post 5', new_body='This is a test post. Please continue to ignore.') |
|
180 assert new_post5 is not None |
|
181 new_post5.tags = "baz, bletch, quux" |
|
182 |
|
183 foo_tag = Tag.get_by_name('foo') |
|
184 assert foo_tag is not None |
|
185 self.assertEqual(foo_tag.tagged_count, 1) |
|
186 |
|
187 bar_tag = Tag.get_by_name('bar') |
|
188 assert bar_tag is not None |
|
189 self.assertEqual(bar_tag.tagged_count, 2) |
|
190 |
|
191 baz_tag = Tag.get_by_name('baz') |
|
192 assert baz_tag is not None |
|
193 self.assertEqual(baz_tag.tagged_count, 3) |
|
194 |
|
195 bletch_tag = Tag.get_by_name('bletch') |
|
196 assert bletch_tag is not None |
|
197 self.assertEqual(bletch_tag.tagged_count, 2) |
|
198 |
|
199 quux_tag = Tag.get_by_name('quux') |
|
200 assert quux_tag is not None |
|
201 self.assertEqual(quux_tag.tagged_count, 1) |
|
202 |
|
203 new_post3.tags = 'bar, baz' |
|
204 foo_tag = Tag.get_by_name('foo') |
|
205 assert foo_tag is not None |
|
206 self.assertEqual(len(new_post3.tags), 2) |
|
207 self.assertEqual(foo_tag.tagged_count, 0) |
|
208 |
|
209 def testTagGetTagsForKey(self): |
|
210 new_post = Post.new_post(new_title='test post 6', new_body='This is a test post. Please continue to ignore.', new_tags='foo,bar,bletch,quux') |
|
211 assert new_post is not None |
|
212 |
|
213 tags = Tag.get_tags_for_key(new_post.key()) |
|
214 assert tags is not None |
|
215 self.assertEqual(type(tags), type([])) |
|
216 self.assertEqual(len(tags), 4) |
|
217 |
|
218 def testTagGetByName(self): |
|
219 new_post = Post.new_post(new_title='test post 6', new_body='This is a test post. Please continue to ignore.', new_tags='foo,bar,bletch,quux') |
|
220 assert new_post is not None |
|
221 |
|
222 quux_tag = Tag.get_by_name('quux') |
|
223 assert quux_tag is not None |
|
224 |
|
225 zizzle_tag = Tag.get_by_name('zizzle') |
|
226 assert zizzle_tag is None |
|
227 |
|
228 def testTagsString(self): |
|
229 new_post = Post.new_post(new_title='test post 6', new_body='This is a test post. Please continue to ignore.', new_tags=' pal,poll ,,pip,pony') |
|
230 assert new_post is not None |
|
231 self.assertEqual(new_post.tags_string(), "pal,poll,pip,pony") |
|
232 new_post.tag_separator = "|" |
|
233 self.assertEqual(new_post.tags_string(), "pal|poll|pip|pony") |
|
234 new_post.tag_separator = " , " |
|
235 self.assertEqual(new_post.tags_string(), "pal , poll , pip , pony") |
|
236 |
|
237 new_post.tag_separator = ", " |
|
238 new_post.tags = "pal, pill, pip" |
|
239 self.assertEqual(len(new_post.tags), 3) |
|
240 self.assertEqual(new_post.tags_string(), "pal, pill, pip") |
|
241 |
|
242 if __name__ == '__main__': |
|
243 unittest.main() |