Merge branch 'master' of git://gitorious.org/mediagoblin/mediagoblin
[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
c121a7d3 18import sys
1e03504e 19import os
c5678c1a 20import pkg_resources
1e03504e
JK
21import shutil
22
23from functools import wraps
c5678c1a 24
623bee73 25from paste.deploy import loadapp
c5678c1a
CAW
26from webtest import TestApp
27
91b89bde 28from mediagoblin import mg_globals
e9b4e500 29from mediagoblin.db.models import User, MediaEntry, Collection
152a3bfa 30from mediagoblin.tools import testing
421129b6 31from mediagoblin.init.config import read_mediagoblin_config
39dc3bf8 32from mediagoblin.db.base import Session
56dc1c9d 33from mediagoblin.meddleware import BaseMeddleware
9754802d 34from mediagoblin.auth.lib import bcrypt_gen_password_hash
d693f6bd 35from mediagoblin.gmg_commands.dbupdate import run_dbupdate
c5678c1a
CAW
36
37
cfd2cbf3 38MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__'
623bee73 39TEST_SERVER_CONFIG = pkg_resources.resource_filename(
5c441e75 40 'mediagoblin.tests', 'test_paste.ini')
c5678c1a 41TEST_APP_CONFIG = pkg_resources.resource_filename(
623bee73 42 'mediagoblin.tests', 'test_mgoblin_app.ini')
c5678c1a
CAW
43TEST_USER_DEV = pkg_resources.resource_filename(
44 'mediagoblin.tests', 'test_user_dev')
6588acc1 45
c5678c1a 46
9e1fa239 47USER_DEV_DIRECTORIES_TO_SETUP = ['media/public', 'media/queue']
c5678c1a 48
c5678c1a 49
56dc1c9d 50class TestingMeddleware(BaseMeddleware):
33d11e99 51 """
ce5ae8da 52 Meddleware for the Unit tests
a11aa2d1 53
33d11e99
E
54 It might make sense to perform some tests on all
55 requests/responses. Or prepare them in a special
56 manner. For example all html responses could be tested
57 for being valid html *after* being rendered.
58
59 This module is getting inserted at the front of the
ce5ae8da 60 meddleware list, which means: requests are handed here
33d11e99
E
61 first, responses last. So this wraps up the "normal"
62 app.
63
64 If you need to add a test, either add it directly to
65 the appropiate process_request or process_response, or
66 create a new method and call it from process_*.
67 """
68
33d11e99
E
69 def process_response(self, request, response):
70 # All following tests should be for html only!
74af60bb 71 if getattr(response, 'content_type', None) != "text/html":
33d11e99
E
72 # Get out early
73 return
74
75 # If the template contains a reference to
76 # /mgoblin_static/ instead of using
77 # /request.staticdirect(), error out here.
78 # This could probably be implemented as a grep on
79 # the shipped templates easier...
80 if response.text.find("/mgoblin_static/") >= 0:
81 raise AssertionError(
82 "Response HTML contains reference to /mgoblin_static/ "
83 "instead of staticdirect. Request was for: "
84 + request.full_path)
85
86 return
87
88
5c2ece74
CAW
89def get_app(request, paste_config=None, mgoblin_config=None):
90 """Create a MediaGoblin app for testing.
91
92 Args:
93 - request: Not an http request, but a pytest fixture request. We
94 use this to make temporary directories that pytest
95 automatically cleans up as needed.
96 - paste_config: particular paste config used by this application.
97 - mgoblin_config: particular mediagoblin config used by this
98 application.
99 """
6588acc1
CAW
100 paste_config = paste_config or TEST_SERVER_CONFIG
101 mgoblin_config = mgoblin_config or TEST_APP_CONFIG
102
5c2ece74
CAW
103 # This is the directory we're copying the paste/mgoblin config stuff into
104 run_dir = request.config._tmpdirhandler.mktemp(
105 'mgoblin_app', numbered=True)
106 user_dev_dir = run_dir.mkdir('test_user_dev').strpath
107
108 new_paste_config = run_dir.join('paste.ini').strpath
109 new_mgoblin_config = run_dir.join('mediagoblin.ini').strpath
110 shutil.copyfile(paste_config, new_paste_config)
111 shutil.copyfile(mgoblin_config, new_mgoblin_config)
112
9c768866
BS
113 Session.rollback()
114 Session.remove()
115
5c2ece74 116 # install user_dev directories
c5678c1a 117 for directory in USER_DEV_DIRECTORIES_TO_SETUP:
5c2ece74 118 full_dir = os.path.join(user_dev_dir, directory)
c5678c1a
CAW
119 os.makedirs(full_dir)
120
121 # Get app config
5c2ece74 122 global_config, validation_result = read_mediagoblin_config(new_mgoblin_config)
623bee73 123 app_config = global_config['mediagoblin']
c5678c1a 124
d693f6bd 125 # Run database setup/migrations
30520c92 126 run_dbupdate(app_config, global_config)
c5678c1a
CAW
127
128 # setup app and return
0a791a94 129 test_app = loadapp(
5c2ece74 130 'config:' + new_paste_config)
623bee73 131
ce5ae8da 132 # Insert the TestingMeddleware, which can do some
91b89bde 133 # sanity checks on every request/response.
34b0874d
E
134 # Doing it this way is probably not the cleanest way.
135 # We'll fix it, when we have plugins!
ce5ae8da 136 mg_globals.app.meddleware.insert(0, TestingMeddleware(mg_globals.app))
91b89bde 137
623bee73 138 app = TestApp(test_app)
6588acc1 139
623bee73 140 return app
3aa4c668
CAW
141
142
85663692
CAW
143def install_fixtures_simple(db, fixtures):
144 """
145 Very simply install fixtures in the database
146 """
147 for collection_name, collection_fixtures in fixtures.iteritems():
148 collection = db[collection_name]
149 for fixture in collection_fixtures:
150 collection.insert(fixture)
151
152
153def assert_db_meets_expected(db, expected):
154 """
155 Assert a database contains the things we expect it to.
156
5c2b8486
SS
157 Objects are found via 'id', so you should make sure your document
158 has an id.
85663692
CAW
159
160 Args:
161 - db: pymongo or mongokit database connection
162 - expected: the data we expect. Formatted like:
163 {'collection_name': [
5c2b8486 164 {'id': 'foo',
85663692
CAW
165 'some_field': 'some_value'},]}
166 """
167 for collection_name, collection_data in expected.iteritems():
168 collection = db[collection_name]
169 for expected_document in collection_data:
5c2b8486 170 document = collection.find_one({'id': expected_document['id']})
85663692
CAW
171 assert document is not None # make sure it exists
172 assert document == expected_document # make sure it matches
9754802d
E
173
174
a5cf95c5 175def fixture_add_user(username=u'chris', password=u'toast',
bee079f3 176 active_user=True):
4fc0a289
SS
177 # Reuse existing user or create a new one
178 test_user = User.query.filter_by(username=username).first()
179 if test_user is None:
180 test_user = User()
9754802d
E
181 test_user.username = username
182 test_user.email = username + u'@example.com'
183 if password is not None:
184 test_user.pw_hash = bcrypt_gen_password_hash(password)
185 if active_user:
186 test_user.email_verified = True
187 test_user.status = u'active'
188
189 test_user.save()
190
37ef4c66 191 # Reload
a5cf95c5 192 test_user = User.query.filter_by(username=username).first()
37ef4c66
E
193
194 # ... and detach from session:
37ef4c66
E
195 Session.expunge(test_user)
196
9754802d 197 return test_user
cd75b228
E
198
199
e9b4e500
E
200def fixture_media_entry(title=u"Some title", slug=None,
201 uploader=None, save=True, gen_slug=True):
202 entry = MediaEntry()
203 entry.title = title
204 entry.slug = slug
205 entry.uploader = uploader or fixture_add_user().id
206 entry.media_type = u'image'
c121a7d3 207
e9b4e500
E
208 if gen_slug:
209 entry.generate_slug()
210 if save:
211 entry.save()
212
213 return entry
214
215
cd75b228
E
216def fixture_add_collection(name=u"My first Collection", user=None):
217 if user is None:
218 user = fixture_add_user()
219 coll = Collection.query.filter_by(creator=user.id, title=name).first()
220 if coll is not None:
221 return coll
222 coll = Collection()
223 coll.creator = user.id
224 coll.title = name
225 coll.generate_slug()
226 coll.save()
227
228 # Reload
229 Session.refresh(coll)
230
231 # ... and detach from session:
232 Session.expunge(coll)
233
234 return coll