Commit | Line | Data |
---|---|---|
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 | 18 | import sys |
1e03504e | 19 | import os |
c5678c1a | 20 | import pkg_resources |
1e03504e JK |
21 | import shutil |
22 | ||
23 | from functools import wraps | |
c5678c1a | 24 | |
623bee73 | 25 | from paste.deploy import loadapp |
c5678c1a CAW |
26 | from webtest import TestApp |
27 | ||
91b89bde | 28 | from mediagoblin import mg_globals |
e9b4e500 | 29 | from mediagoblin.db.models import User, MediaEntry, Collection |
152a3bfa | 30 | from mediagoblin.tools import testing |
421129b6 | 31 | from mediagoblin.init.config import read_mediagoblin_config |
39dc3bf8 | 32 | from mediagoblin.db.base import Session |
56dc1c9d | 33 | from mediagoblin.meddleware import BaseMeddleware |
9754802d | 34 | from mediagoblin.auth.lib import bcrypt_gen_password_hash |
d693f6bd | 35 | from mediagoblin.gmg_commands.dbupdate import run_dbupdate |
c5678c1a CAW |
36 | |
37 | ||
cfd2cbf3 | 38 | MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__' |
623bee73 | 39 | TEST_SERVER_CONFIG = pkg_resources.resource_filename( |
5c441e75 | 40 | 'mediagoblin.tests', 'test_paste.ini') |
c5678c1a | 41 | TEST_APP_CONFIG = pkg_resources.resource_filename( |
623bee73 | 42 | 'mediagoblin.tests', 'test_mgoblin_app.ini') |
c5678c1a CAW |
43 | TEST_USER_DEV = pkg_resources.resource_filename( |
44 | 'mediagoblin.tests', 'test_user_dev') | |
6588acc1 | 45 | |
c5678c1a | 46 | |
9e1fa239 | 47 | USER_DEV_DIRECTORIES_TO_SETUP = ['media/public', 'media/queue'] |
c5678c1a | 48 | |
c5678c1a | 49 | |
56dc1c9d | 50 | class 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 |
89 | def 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 |
143 | def 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 | ||
153 | def 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 | 175 | def 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 |
200 | def 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 |
216 | def 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 |