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 | ||
18 | import pkg_resources | |
19 | import os, shutil | |
20 | ||
623bee73 | 21 | from paste.deploy import loadapp |
c5678c1a CAW |
22 | from webtest import TestApp |
23 | ||
91b89bde | 24 | from mediagoblin import mg_globals |
152a3bfa | 25 | from mediagoblin.tools import testing |
421129b6 | 26 | from mediagoblin.init.config import read_mediagoblin_config |
3aa4c668 | 27 | from mediagoblin.decorators import _make_safe |
c5678c1a | 28 | from mediagoblin.db.open import setup_connection_and_db_from_config |
56dc1c9d | 29 | from mediagoblin.meddleware import BaseMeddleware |
9754802d | 30 | from mediagoblin.auth.lib import bcrypt_gen_password_hash |
c5678c1a CAW |
31 | |
32 | ||
cfd2cbf3 | 33 | MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__' |
623bee73 | 34 | TEST_SERVER_CONFIG = pkg_resources.resource_filename( |
5c441e75 | 35 | 'mediagoblin.tests', 'test_paste.ini') |
c5678c1a | 36 | TEST_APP_CONFIG = pkg_resources.resource_filename( |
623bee73 | 37 | 'mediagoblin.tests', 'test_mgoblin_app.ini') |
c5678c1a CAW |
38 | TEST_USER_DEV = pkg_resources.resource_filename( |
39 | 'mediagoblin.tests', 'test_user_dev') | |
40 | MGOBLIN_APP = None | |
41 | ||
42 | USER_DEV_DIRECTORIES_TO_SETUP = [ | |
43 | 'media/public', 'media/queue', | |
44 | 'beaker/sessions/data', 'beaker/sessions/lock'] | |
45 | ||
29f1333e CAW |
46 | BAD_CELERY_MESSAGE = """\ |
47 | Sorry, you *absolutely* must run nosetests with the | |
073b61fe E |
48 | mediagoblin.init.celery.from_tests module. Like so: |
49 | $ CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_tests ./bin/nosetests""" | |
29f1333e | 50 | |
c5678c1a CAW |
51 | |
52 | class BadCeleryEnviron(Exception): pass | |
53 | ||
54 | ||
56dc1c9d | 55 | class TestingMeddleware(BaseMeddleware): |
33d11e99 | 56 | """ |
ce5ae8da | 57 | Meddleware for the Unit tests |
33d11e99 E |
58 | |
59 | It might make sense to perform some tests on all | |
60 | requests/responses. Or prepare them in a special | |
61 | manner. For example all html responses could be tested | |
62 | for being valid html *after* being rendered. | |
63 | ||
64 | This module is getting inserted at the front of the | |
ce5ae8da | 65 | meddleware list, which means: requests are handed here |
33d11e99 E |
66 | first, responses last. So this wraps up the "normal" |
67 | app. | |
68 | ||
69 | If you need to add a test, either add it directly to | |
70 | the appropiate process_request or process_response, or | |
71 | create a new method and call it from process_*. | |
72 | """ | |
73 | ||
33d11e99 E |
74 | def process_response(self, request, response): |
75 | # All following tests should be for html only! | |
76 | if response.content_type != "text/html": | |
77 | # Get out early | |
78 | return | |
79 | ||
80 | # If the template contains a reference to | |
81 | # /mgoblin_static/ instead of using | |
82 | # /request.staticdirect(), error out here. | |
83 | # This could probably be implemented as a grep on | |
84 | # the shipped templates easier... | |
85 | if response.text.find("/mgoblin_static/") >= 0: | |
86 | raise AssertionError( | |
87 | "Response HTML contains reference to /mgoblin_static/ " | |
88 | "instead of staticdirect. Request was for: " | |
89 | + request.full_path) | |
90 | ||
91 | return | |
92 | ||
93 | ||
29f1333e | 94 | def suicide_if_bad_celery_environ(): |
eaca7874 | 95 | if not os.environ.get('CELERY_CONFIG_MODULE') == \ |
073b61fe | 96 | 'mediagoblin.init.celery.from_tests': |
29f1333e CAW |
97 | raise BadCeleryEnviron(BAD_CELERY_MESSAGE) |
98 | ||
99 | ||
100 | def get_test_app(dump_old_app=True): | |
101 | suicide_if_bad_celery_environ() | |
623bee73 | 102 | |
0419d0da | 103 | # Make sure we've turned on testing |
152a3bfa | 104 | testing._activate_testing() |
0419d0da | 105 | |
9ea5c28b | 106 | # Leave this imported as it sets up celery. |
073b61fe | 107 | from mediagoblin.init.celery import from_tests |
9ea5c28b | 108 | |
623bee73 | 109 | global MGOBLIN_APP |
c5678c1a CAW |
110 | |
111 | # Just return the old app if that exists and it's okay to set up | |
112 | # and return | |
113 | if MGOBLIN_APP and not dump_old_app: | |
114 | return MGOBLIN_APP | |
115 | ||
116 | # Remove and reinstall user_dev directories | |
117 | if os.path.exists(TEST_USER_DEV): | |
118 | shutil.rmtree(TEST_USER_DEV) | |
119 | ||
120 | for directory in USER_DEV_DIRECTORIES_TO_SETUP: | |
121 | full_dir = os.path.join(TEST_USER_DEV, directory) | |
122 | os.makedirs(full_dir) | |
123 | ||
124 | # Get app config | |
623bee73 CAW |
125 | global_config, validation_result = read_mediagoblin_config(TEST_APP_CONFIG) |
126 | app_config = global_config['mediagoblin'] | |
c5678c1a CAW |
127 | |
128 | # Wipe database | |
129 | # @@: For now we're dropping collections, but we could also just | |
130 | # collection.remove() ? | |
623bee73 | 131 | connection, db = setup_connection_and_db_from_config(app_config) |
cfd2cbf3 | 132 | assert db.name == MEDIAGOBLIN_TEST_DB_NAME |
c5678c1a CAW |
133 | |
134 | collections_to_wipe = [ | |
135 | collection | |
136 | for collection in db.collection_names() | |
137 | if not collection.startswith('system.')] | |
138 | ||
139 | for collection in collections_to_wipe: | |
140 | db.drop_collection(collection) | |
141 | ||
c5678c1a CAW |
142 | # TODO: Drop and recreate indexes |
143 | ||
144 | # setup app and return | |
0a791a94 | 145 | test_app = loadapp( |
623bee73 CAW |
146 | 'config:' + TEST_SERVER_CONFIG) |
147 | ||
ce5ae8da | 148 | # Insert the TestingMeddleware, which can do some |
91b89bde | 149 | # sanity checks on every request/response. |
34b0874d E |
150 | # Doing it this way is probably not the cleanest way. |
151 | # We'll fix it, when we have plugins! | |
ce5ae8da | 152 | mg_globals.app.meddleware.insert(0, TestingMeddleware(mg_globals.app)) |
91b89bde | 153 | |
623bee73 CAW |
154 | app = TestApp(test_app) |
155 | MGOBLIN_APP = app | |
156 | ||
623bee73 | 157 | return app |
3aa4c668 CAW |
158 | |
159 | ||
160 | def setup_fresh_app(func): | |
161 | """ | |
162 | Decorator to setup a fresh test application for this function. | |
163 | ||
164 | Cleans out test buckets and passes in a new, fresh test_app. | |
165 | """ | |
166 | def wrapper(*args, **kwargs): | |
167 | test_app = get_test_app() | |
152a3bfa | 168 | testing.clear_test_buckets() |
3aa4c668 CAW |
169 | return func(test_app, *args, **kwargs) |
170 | ||
171 | return _make_safe(wrapper, func) | |
85663692 CAW |
172 | |
173 | ||
174 | def install_fixtures_simple(db, fixtures): | |
175 | """ | |
176 | Very simply install fixtures in the database | |
177 | """ | |
178 | for collection_name, collection_fixtures in fixtures.iteritems(): | |
179 | collection = db[collection_name] | |
180 | for fixture in collection_fixtures: | |
181 | collection.insert(fixture) | |
182 | ||
183 | ||
184 | def assert_db_meets_expected(db, expected): | |
185 | """ | |
186 | Assert a database contains the things we expect it to. | |
187 | ||
188 | Objects are found via '_id', so you should make sure your document | |
189 | has an _id. | |
190 | ||
191 | Args: | |
192 | - db: pymongo or mongokit database connection | |
193 | - expected: the data we expect. Formatted like: | |
194 | {'collection_name': [ | |
195 | {'_id': 'foo', | |
196 | 'some_field': 'some_value'},]} | |
197 | """ | |
198 | for collection_name, collection_data in expected.iteritems(): | |
199 | collection = db[collection_name] | |
200 | for expected_document in collection_data: | |
201 | document = collection.find_one({'_id': expected_document['_id']}) | |
202 | assert document is not None # make sure it exists | |
203 | assert document == expected_document # make sure it matches | |
9754802d E |
204 | |
205 | ||
206 | def fixture_add_user(username = u'chris', password = 'toast', | |
207 | active_user = True): | |
208 | test_user = mg_globals.database.User() | |
209 | test_user.username = username | |
210 | test_user.email = username + u'@example.com' | |
211 | if password is not None: | |
212 | test_user.pw_hash = bcrypt_gen_password_hash(password) | |
213 | if active_user: | |
214 | test_user.email_verified = True | |
215 | test_user.status = u'active' | |
216 | ||
217 | test_user.save() | |
218 | ||
219 | return test_user |