Commit | Line | Data |
---|---|---|
1975b5dd | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
12a100e4 | 2 | # Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. |
1975b5dd CM |
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 | import urlparse | |
9df37e8a | 18 | import pkg_resources |
1975b5dd | 19 | |
afe4e513 | 20 | from nose.tools import assert_equal, assert_true, assert_false |
1975b5dd CM |
21 | |
22 | from mediagoblin.auth import lib as auth_lib | |
0a78be3e | 23 | from mediagoblin.tests.tools import setup_fresh_app, get_test_app |
1975b5dd | 24 | from mediagoblin import mg_globals |
ae3bc7fa | 25 | from mediagoblin.tools import template, common |
1975b5dd | 26 | |
9df37e8a CM |
27 | GOOD_JPG = pkg_resources.resource_filename( |
28 | 'mediagoblin.tests', 'test_submission/good.jpg') | |
29 | GOOD_PNG = pkg_resources.resource_filename( | |
30 | 'mediagoblin.tests', 'test_submission/good.png') | |
31 | EVIL_FILE = pkg_resources.resource_filename( | |
32 | 'mediagoblin.tests', 'test_submission/evil') | |
33 | EVIL_JPG = pkg_resources.resource_filename( | |
34 | 'mediagoblin.tests', 'test_submission/evil.jpg') | |
35 | EVIL_PNG = pkg_resources.resource_filename( | |
36 | 'mediagoblin.tests', 'test_submission/evil.png') | |
75ce65cf | 37 | |
8ff4dec7 CFD |
38 | GOOD_TAG_STRING = 'yin,yang' |
39 | BAD_TAG_STRING = 'rage,' + 'f' * 26 + 'u' * 26 | |
40 | ||
1975b5dd | 41 | |
1975b5dd CM |
42 | class TestSubmission: |
43 | def setUp(self): | |
0a78be3e CM |
44 | self.test_app = get_test_app() |
45 | ||
75ce65cf CM |
46 | # TODO: Possibly abstract into a decorator like: |
47 | # @as_authenticated_user('chris') | |
1975b5dd CM |
48 | test_user = mg_globals.database.User() |
49 | test_user['username'] = u'chris' | |
50 | test_user['email'] = u'chris@example.com' | |
ad35dd49 CM |
51 | test_user['email_verified'] = True |
52 | test_user['status'] = u'active' | |
1975b5dd CM |
53 | test_user['pw_hash'] = auth_lib.bcrypt_gen_password_hash('toast') |
54 | test_user.save() | |
55 | ||
afe4e513 JW |
56 | self.test_user = test_user |
57 | ||
75ce65cf CM |
58 | self.test_app.post( |
59 | '/auth/login/', { | |
60 | 'username': u'chris', | |
61 | 'password': 'toast'}) | |
62 | ||
63 | def test_missing_fields(self): | |
ad35dd49 CM |
64 | # Test blank form |
65 | # --------------- | |
ae3bc7fa | 66 | template.clear_test_template_context() |
ad35dd49 CM |
67 | response = self.test_app.post( |
68 | '/submit/', {}) | |
ae3bc7fa | 69 | context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] |
ad35dd49 CM |
70 | form = context['submit_form'] |
71 | assert form.file.errors == [u'You must provide a file.'] | |
72 | ||
73 | # Test blank file | |
74 | # --------------- | |
ae3bc7fa | 75 | template.clear_test_template_context() |
ad35dd49 CM |
76 | response = self.test_app.post( |
77 | '/submit/', { | |
78 | 'title': 'test title'}) | |
ae3bc7fa | 79 | context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] |
ad35dd49 CM |
80 | form = context['submit_form'] |
81 | assert form.file.errors == [u'You must provide a file.'] | |
82 | ||
75ce65cf CM |
83 | |
84 | def test_normal_uploads(self): | |
75ce65cf | 85 | # Test JPG |
ad35dd49 | 86 | # -------- |
ae3bc7fa | 87 | template.clear_test_template_context() |
ad35dd49 CM |
88 | response = self.test_app.post( |
89 | '/submit/', { | |
90 | 'title': 'Normal upload 1' | |
91 | }, upload_files=[( | |
9df37e8a | 92 | 'file', GOOD_JPG)]) |
ad35dd49 CM |
93 | |
94 | # User should be redirected | |
95 | response.follow() | |
96 | assert_equal( | |
97 | urlparse.urlsplit(response.location)[2], | |
9df37e8a | 98 | '/u/chris/') |
ae3bc7fa | 99 | assert template.TEMPLATE_TEST_CONTEXT.has_key( |
9df37e8a | 100 | 'mediagoblin/user_pages/user.html') |
ad35dd49 | 101 | |
75ce65cf | 102 | # Test PNG |
ad35dd49 | 103 | # -------- |
ae3bc7fa | 104 | template.clear_test_template_context() |
ad35dd49 CM |
105 | response = self.test_app.post( |
106 | '/submit/', { | |
107 | 'title': 'Normal upload 2' | |
108 | }, upload_files=[( | |
9df37e8a | 109 | 'file', GOOD_PNG)]) |
ad35dd49 CM |
110 | |
111 | response.follow() | |
112 | assert_equal( | |
113 | urlparse.urlsplit(response.location)[2], | |
9df37e8a | 114 | '/u/chris/') |
ae3bc7fa | 115 | assert template.TEMPLATE_TEST_CONTEXT.has_key( |
9df37e8a | 116 | 'mediagoblin/user_pages/user.html') |
75ce65cf | 117 | |
8ff4dec7 CFD |
118 | def test_tags(self): |
119 | # Good tag string | |
120 | # -------- | |
ae3bc7fa | 121 | template.clear_test_template_context() |
8ff4dec7 CFD |
122 | response = self.test_app.post( |
123 | '/submit/', { | |
124 | 'title': 'Balanced Goblin', | |
125 | 'tags': GOOD_TAG_STRING | |
126 | }, upload_files=[( | |
127 | 'file', GOOD_JPG)]) | |
128 | ||
129 | # New media entry with correct tags should be created | |
130 | response.follow() | |
ae3bc7fa | 131 | context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html'] |
8ff4dec7 CFD |
132 | request = context['request'] |
133 | media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] | |
134 | assert_equal(media['tags'], | |
135 | [{'name': u'yin', 'slug': u'yin'}, | |
136 | {'name': u'yang', 'slug': u'yang'}]) | |
137 | ||
138 | # Test tags that are too long | |
139 | # --------------- | |
ae3bc7fa | 140 | template.clear_test_template_context() |
8ff4dec7 CFD |
141 | response = self.test_app.post( |
142 | '/submit/', { | |
143 | 'title': 'Balanced Goblin', | |
144 | 'tags': BAD_TAG_STRING | |
145 | }, upload_files=[( | |
146 | 'file', GOOD_JPG)]) | |
147 | ||
148 | # Too long error should be raised | |
ae3bc7fa | 149 | context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] |
8ff4dec7 CFD |
150 | form = context['submit_form'] |
151 | assert form.tags.errors == [ | |
152 | u'Tags must be shorter than 50 characters. Tags that are too long'\ | |
153 | ': ffffffffffffffffffffffffffuuuuuuuuuuuuuuuuuuuuuuuuuu'] | |
75ce65cf | 154 | |
afe4e513 | 155 | def test_delete(self): |
ae3bc7fa | 156 | template.clear_test_template_context() |
afe4e513 JW |
157 | response = self.test_app.post( |
158 | '/submit/', { | |
159 | 'title': 'Balanced Goblin', | |
160 | }, upload_files=[( | |
161 | 'file', GOOD_JPG)]) | |
162 | ||
163 | # Post image | |
164 | response.follow() | |
165 | ||
ae3bc7fa | 166 | request = template.TEMPLATE_TEST_CONTEXT[ |
afe4e513 JW |
167 | 'mediagoblin/user_pages/user.html']['request'] |
168 | ||
169 | media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] | |
170 | ||
171 | # Does media entry exist? | |
172 | assert_true(media) | |
173 | ||
174 | # Do not confirm deletion | |
175 | # --------------------------------------------------- | |
176 | response = self.test_app.post( | |
502073f2 | 177 | request.urlgen('mediagoblin.user_pages.media_confirm_delete', |
afe4e513 JW |
178 | # No work: user=media.uploader().username, |
179 | user=self.test_user['username'], | |
180 | media=media['_id']), | |
9a64272e CAW |
181 | # no value means no confirm |
182 | {}) | |
afe4e513 JW |
183 | |
184 | response.follow() | |
185 | ||
ae3bc7fa | 186 | request = template.TEMPLATE_TEST_CONTEXT[ |
afe4e513 JW |
187 | 'mediagoblin/user_pages/user.html']['request'] |
188 | ||
189 | media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] | |
190 | ||
191 | # Does media entry still exist? | |
192 | assert_true(media) | |
193 | ||
194 | # Confirm deletion | |
195 | # --------------------------------------------------- | |
196 | response = self.test_app.post( | |
502073f2 | 197 | request.urlgen('mediagoblin.user_pages.media_confirm_delete', |
afe4e513 JW |
198 | # No work: user=media.uploader().username, |
199 | user=self.test_user['username'], | |
200 | media=media['_id']), | |
9a64272e | 201 | {'confirm': 'y'}) |
afe4e513 JW |
202 | |
203 | response.follow() | |
204 | ||
ae3bc7fa | 205 | request = template.TEMPLATE_TEST_CONTEXT[ |
afe4e513 JW |
206 | 'mediagoblin/user_pages/user.html']['request'] |
207 | ||
208 | # Does media entry still exist? | |
209 | assert_false( | |
210 | request.db.MediaEntry.find( | |
211 | {'_id': media['_id']}).count()) | |
212 | ||
75ce65cf | 213 | def test_malicious_uploads(self): |
ad35dd49 CM |
214 | # Test non-suppoerted file with non-supported extension |
215 | # ----------------------------------------------------- | |
ae3bc7fa | 216 | template.clear_test_template_context() |
ad35dd49 CM |
217 | response = self.test_app.post( |
218 | '/submit/', { | |
68f3ffbe | 219 | 'title': 'Malicious Upload 1' |
ad35dd49 | 220 | }, upload_files=[( |
9df37e8a | 221 | 'file', EVIL_FILE)]) |
ad35dd49 | 222 | |
ae3bc7fa | 223 | context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] |
ad35dd49 CM |
224 | form = context['submit_form'] |
225 | assert form.file.errors == ['The file doesn\'t seem to be an image!'] | |
226 | ||
68f3ffbe CAW |
227 | # NOTE: The following 2 tests will ultimately fail, but they |
228 | # *will* pass the initial form submission step. Instead, | |
229 | # they'll be caught as failures during the processing step. | |
9df37e8a | 230 | |
75ce65cf | 231 | # Test non-supported file with .jpg extension |
ad35dd49 | 232 | # ------------------------------------------- |
ae3bc7fa | 233 | template.clear_test_template_context() |
68f3ffbe CAW |
234 | response = self.test_app.post( |
235 | '/submit/', { | |
236 | 'title': 'Malicious Upload 2' | |
237 | }, upload_files=[( | |
238 | 'file', EVIL_JPG)]) | |
239 | response.follow() | |
240 | assert_equal( | |
241 | urlparse.urlsplit(response.location)[2], | |
242 | '/u/chris/') | |
ad35dd49 | 243 | |
68f3ffbe CAW |
244 | entry = mg_globals.database.MediaEntry.find_one( |
245 | {'title': 'Malicious Upload 2'}) | |
246 | assert_equal(entry['state'], 'failed') | |
247 | assert_equal( | |
248 | entry['fail_error'], | |
249 | u'mediagoblin.process_media.errors:BadMediaFail') | |
ad35dd49 | 250 | |
75ce65cf | 251 | # Test non-supported file with .png extension |
ad35dd49 | 252 | # ------------------------------------------- |
ae3bc7fa | 253 | template.clear_test_template_context() |
68f3ffbe CAW |
254 | response = self.test_app.post( |
255 | '/submit/', { | |
256 | 'title': 'Malicious Upload 3' | |
257 | }, upload_files=[( | |
258 | 'file', EVIL_PNG)]) | |
259 | response.follow() | |
260 | assert_equal( | |
261 | urlparse.urlsplit(response.location)[2], | |
262 | '/u/chris/') | |
0a78be3e | 263 | |
68f3ffbe CAW |
264 | entry = mg_globals.database.MediaEntry.find_one( |
265 | {'title': 'Malicious Upload 3'}) | |
266 | assert_equal(entry['state'], 'failed') | |
267 | assert_equal( | |
268 | entry['fail_error'], | |
269 | u'mediagoblin.process_media.errors:BadMediaFail') |