Change codebase to query or create correct User model
[mediagoblin.git] / mediagoblin / tests / tools.py
CommitLineData
c5678c1a 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
c5678c1a
CAW
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
1e03504e 18import os
c5678c1a 19import pkg_resources
1e03504e
JK
20import shutil
21
386c9c7c 22import six
c5678c1a 23
623bee73 24from paste.deploy import loadapp
c5678c1a
CAW
25from webtest import TestApp
26
91b89bde 27from mediagoblin import mg_globals
d88fcb03 28from mediagoblin.db.models import User, LocalUser, MediaEntry, Collection, MediaComment, \
f751d346 29 CommentSubscription, CommentNotification, Privilege, CommentReport, Client, \
5ddc85e0 30 RequestToken, AccessToken, Activity, Generator
152a3bfa 31from mediagoblin.tools import testing
421129b6 32from mediagoblin.init.config import read_mediagoblin_config
39dc3bf8 33from mediagoblin.db.base import Session
56dc1c9d 34from mediagoblin.meddleware import BaseMeddleware
fa723291 35from mediagoblin.auth import gen_password_hash
d693f6bd 36from mediagoblin.gmg_commands.dbupdate import run_dbupdate
f751d346 37from mediagoblin.tools.crypto import random_string
c5678c1a 38
dfd66b78 39from datetime import datetime
40
c5678c1a 41
cfd2cbf3 42MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__'
623bee73 43TEST_SERVER_CONFIG = pkg_resources.resource_filename(
5c441e75 44 'mediagoblin.tests', 'test_paste.ini')
c5678c1a 45TEST_APP_CONFIG = pkg_resources.resource_filename(
623bee73 46 'mediagoblin.tests', 'test_mgoblin_app.ini')
6588acc1 47
c5678c1a 48
9e1fa239 49USER_DEV_DIRECTORIES_TO_SETUP = ['media/public', 'media/queue']
c5678c1a 50
c5678c1a 51
56dc1c9d 52class TestingMeddleware(BaseMeddleware):
33d11e99 53 """
ce5ae8da 54 Meddleware for the Unit tests
a11aa2d1 55
33d11e99
E
56 It might make sense to perform some tests on all
57 requests/responses. Or prepare them in a special
58 manner. For example all html responses could be tested
59 for being valid html *after* being rendered.
60
61 This module is getting inserted at the front of the
ce5ae8da 62 meddleware list, which means: requests are handed here
33d11e99
E
63 first, responses last. So this wraps up the "normal"
64 app.
65
66 If you need to add a test, either add it directly to
67 the appropiate process_request or process_response, or
68 create a new method and call it from process_*.
69 """
70
33d11e99
E
71 def process_response(self, request, response):
72 # All following tests should be for html only!
74af60bb 73 if getattr(response, 'content_type', None) != "text/html":
33d11e99
E
74 # Get out early
75 return
76
77 # If the template contains a reference to
78 # /mgoblin_static/ instead of using
79 # /request.staticdirect(), error out here.
80 # This could probably be implemented as a grep on
81 # the shipped templates easier...
82 if response.text.find("/mgoblin_static/") >= 0:
83 raise AssertionError(
84 "Response HTML contains reference to /mgoblin_static/ "
85 "instead of staticdirect. Request was for: "
86 + request.full_path)
87
88 return
89
90
5c2ece74
CAW
91def get_app(request, paste_config=None, mgoblin_config=None):
92 """Create a MediaGoblin app for testing.
93
94 Args:
95 - request: Not an http request, but a pytest fixture request. We
96 use this to make temporary directories that pytest
97 automatically cleans up as needed.
98 - paste_config: particular paste config used by this application.
99 - mgoblin_config: particular mediagoblin config used by this
100 application.
101 """
6588acc1
CAW
102 paste_config = paste_config or TEST_SERVER_CONFIG
103 mgoblin_config = mgoblin_config or TEST_APP_CONFIG
104
5c2ece74
CAW
105 # This is the directory we're copying the paste/mgoblin config stuff into
106 run_dir = request.config._tmpdirhandler.mktemp(
107 'mgoblin_app', numbered=True)
491029bc 108 user_dev_dir = run_dir.mkdir('user_dev').strpath
5c2ece74
CAW
109
110 new_paste_config = run_dir.join('paste.ini').strpath
111 new_mgoblin_config = run_dir.join('mediagoblin.ini').strpath
112 shutil.copyfile(paste_config, new_paste_config)
113 shutil.copyfile(mgoblin_config, new_mgoblin_config)
114
9c768866
BS
115 Session.rollback()
116 Session.remove()
117
5c2ece74 118 # install user_dev directories
c5678c1a 119 for directory in USER_DEV_DIRECTORIES_TO_SETUP:
5c2ece74 120 full_dir = os.path.join(user_dev_dir, directory)
c5678c1a
CAW
121 os.makedirs(full_dir)
122
123 # Get app config
5c2ece74 124 global_config, validation_result = read_mediagoblin_config(new_mgoblin_config)
623bee73 125 app_config = global_config['mediagoblin']
c5678c1a 126
d693f6bd 127 # Run database setup/migrations
30520c92 128 run_dbupdate(app_config, global_config)
c5678c1a
CAW
129
130 # setup app and return
0a791a94 131 test_app = loadapp(
5c2ece74 132 'config:' + new_paste_config)
623bee73 133
ce5ae8da 134 # Insert the TestingMeddleware, which can do some
91b89bde 135 # sanity checks on every request/response.
34b0874d
E
136 # Doing it this way is probably not the cleanest way.
137 # We'll fix it, when we have plugins!
ce5ae8da 138 mg_globals.app.meddleware.insert(0, TestingMeddleware(mg_globals.app))
91b89bde 139
623bee73 140 app = TestApp(test_app)
623bee73 141 return app
3aa4c668
CAW
142
143
85663692
CAW
144def install_fixtures_simple(db, fixtures):
145 """
146 Very simply install fixtures in the database
147 """
386c9c7c 148 for collection_name, collection_fixtures in six.iteritems(fixtures):
85663692
CAW
149 collection = db[collection_name]
150 for fixture in collection_fixtures:
151 collection.insert(fixture)
152
153
154def assert_db_meets_expected(db, expected):
155 """
156 Assert a database contains the things we expect it to.
157
5c2b8486
SS
158 Objects are found via 'id', so you should make sure your document
159 has an id.
85663692
CAW
160
161 Args:
162 - db: pymongo or mongokit database connection
163 - expected: the data we expect. Formatted like:
164 {'collection_name': [
5c2b8486 165 {'id': 'foo',
85663692
CAW
166 'some_field': 'some_value'},]}
167 """
386c9c7c 168 for collection_name, collection_data in six.iteritems(expected):
85663692
CAW
169 collection = db[collection_name]
170 for expected_document in collection_data:
44082b12 171 document = collection.query.filter_by(id=expected_document['id']).first()
85663692
CAW
172 assert document is not None # make sure it exists
173 assert document == expected_document # make sure it matches
9754802d
E
174
175
a5cf95c5 176def fixture_add_user(username=u'chris', password=u'toast',
e1561d04 177 privileges=[], wants_comment_notification=True):
4fc0a289 178 # Reuse existing user or create a new one
d88fcb03 179 test_user = User.query.filter(LocalUser.username==username).first()
4fc0a289 180 if test_user is None:
d88fcb03 181 test_user = LocalUser()
9754802d
E
182 test_user.username = username
183 test_user.email = username + u'@example.com'
184 if password is not None:
fa723291 185 test_user.pw_hash = gen_password_hash(password)
2d7b6bde 186 test_user.wants_comment_notification = wants_comment_notification
e1561d04 187 for privilege in privileges:
188 query = Privilege.query.filter(Privilege.privilege_name==privilege)
189 if query.count():
190 test_user.all_privileges.append(query.one())
2d7b6bde 191
9754802d 192 test_user.save()
37ef4c66 193 # Reload
d88fcb03 194 test_user = User.query.filter(LocalUser.username==username).first()
37ef4c66
E
195
196 # ... and detach from session:
37ef4c66
E
197 Session.expunge(test_user)
198
9754802d 199 return test_user
cd75b228
E
200
201
2d7b6bde
JW
202def fixture_comment_subscription(entry, notify=True, send_email=None):
203 if send_email is None:
204 uploader = User.query.filter_by(id=entry.uploader).first()
205 send_email = uploader.wants_comment_notification
206
207 cs = CommentSubscription(
208 media_entry_id=entry.id,
209 user_id=entry.uploader,
210 notify=notify,
211 send_email=send_email)
212
213 cs.save()
214
215 cs = CommentSubscription.query.filter_by(id=cs.id).first()
216
217 Session.expunge(cs)
218
219 return cs
220
221
222def fixture_add_comment_notification(entry_id, subject_id, user_id,
223 seen=False):
224 cn = CommentNotification(user_id=user_id,
225 seen=seen,
226 subject_id=subject_id)
227 cn.save()
228
229 cn = CommentNotification.query.filter_by(id=cn.id).first()
230
231 Session.expunge(cn)
232
233 return cn
234
235
e9b4e500 236def fixture_media_entry(title=u"Some title", slug=None,
2d7b6bde
JW
237 uploader=None, save=True, gen_slug=True,
238 state=u'unprocessed', fake_upload=True,
239 expunge=True):
c3de34d4
CAW
240 """
241 Add a media entry for testing purposes.
242
243 Caution: if you're adding multiple entries with fake_upload=True,
244 make sure you save between them... otherwise you'll hit an
245 IntegrityError from multiple newly-added-MediaEntries adding
246 FileKeynames at once. :)
247 """
2d7b6bde
JW
248 if uploader is None:
249 uploader = fixture_add_user().id
250
e9b4e500
E
251 entry = MediaEntry()
252 entry.title = title
253 entry.slug = slug
2d7b6bde 254 entry.uploader = uploader
e9b4e500 255 entry.media_type = u'image'
2d7b6bde
JW
256 entry.state = state
257
258 if fake_upload:
259 entry.media_files = {'thumb': ['a', 'b', 'c.jpg'],
260 'medium': ['d', 'e', 'f.png'],
261 'original': ['g', 'h', 'i.png']}
262 entry.media_type = u'mediagoblin.media_types.image'
c121a7d3 263
e9b4e500
E
264 if gen_slug:
265 entry.generate_slug()
2d7b6bde 266
e9b4e500
E
267 if save:
268 entry.save()
269
2d7b6bde
JW
270 if expunge:
271 entry = MediaEntry.query.filter_by(id=entry.id).first()
272
273 Session.expunge(entry)
274
e9b4e500
E
275 return entry
276
277
cd75b228
E
278def fixture_add_collection(name=u"My first Collection", user=None):
279 if user is None:
280 user = fixture_add_user()
281 coll = Collection.query.filter_by(creator=user.id, title=name).first()
282 if coll is not None:
283 return coll
284 coll = Collection()
285 coll.creator = user.id
286 coll.title = name
287 coll.generate_slug()
288 coll.save()
289
290 # Reload
291 Session.refresh(coll)
292
293 # ... and detach from session:
294 Session.expunge(coll)
295
296 return coll
491029bc 297
2d7b6bde
JW
298def fixture_add_comment(author=None, media_entry=None, comment=None):
299 if author is None:
300 author = fixture_add_user().id
301
302 if media_entry is None:
303 media_entry = fixture_media_entry().id
304
305 if comment is None:
306 comment = \
307 'Auto-generated test comment by user #{0} on media #{0}'.format(
308 author, media_entry)
309
310 comment = MediaComment(author=author,
311 media_entry=media_entry,
312 content=comment)
313
314 comment.save()
315
316 Session.expunge(comment)
317
318 return comment
319
dfd66b78 320def fixture_add_comment_report(comment=None, reported_user=None,
321 reporter=None, created=None, report_content=None):
322 if comment is None:
323 comment = fixture_add_comment()
324
325 if reported_user is None:
326 reported_user = fixture_add_user()
327
328 if reporter is None:
329 reporter = fixture_add_user()
330
331 if created is None:
332 created=datetime.now()
333
334 if report_content is None:
335 report_content = \
6acf4ee6 336 'Auto-generated test report'
dfd66b78 337
338 comment_report = CommentReport(comment=comment,
339 reported_user = reported_user,
340 reporter = reporter,
341 created = created,
342 report_content=report_content)
343
344 comment_report.save()
345
346 Session.expunge(comment_report)
347
348 return comment_report
5ddc85e0
JT
349
350def fixture_add_activity(obj, verb="post", target=None, generator=None, actor=None):
351 if generator is None:
352 generator = Generator(
353 name="GNU MediaGoblin",
354 object_type="service"
355 )
356 generator.save()
357
358 if actor is None:
359 actor = fixture_add_user()
360
361 activity = Activity(
362 verb=verb,
363 actor=actor.id,
364 generator=generator.id,
365 )
366
367 activity.set_object(obj)
368
369 if target is not None:
370 activity.set_target(target)
371
372 activity.save()
373 return activity