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