Commit | Line | Data |
---|---|---|
57c6473a JW |
1 | # GNU MediaGoblin -- federated, autonomous media hosting |
2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. | |
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/>. | |
247a3b78 | 16 | import urllib |
ee9956c3 | 17 | import json |
57c6473a | 18 | |
5c2ece74 | 19 | import pytest |
247a3b78 | 20 | import mock |
21 | ||
967df5ef JT |
22 | from webtest import AppError |
23 | ||
57c6473a | 24 | from mediagoblin import mg_globals |
247a3b78 | 25 | from .resources import GOOD_JPG |
51ab5192 | 26 | from mediagoblin.db.models import User, MediaEntry |
ee9956c3 JT |
27 | from mediagoblin.tests.tools import fixture_add_user |
28 | from mediagoblin.moderation.tools import take_away_privileges | |
51ab5192 | 29 | from .resources import GOOD_JPG |
ee9956c3 | 30 | |
57c6473a | 31 | class TestAPI(object): |
247a3b78 | 32 | |
ee9956c3 JT |
33 | @pytest.fixture(autouse=True) |
34 | def setup(self, test_app): | |
35 | self.test_app = test_app | |
57c6473a | 36 | self.db = mg_globals.database |
51ab5192 | 37 | |
ee9956c3 JT |
38 | self.user = fixture_add_user(privileges=[u'active', u'uploader']) |
39 | ||
51ab5192 JT |
40 | def _activity_to_feed(self, test_app, activity, headers=None): |
41 | """ Posts an activity to the user's feed """ | |
42 | if headers: | |
43 | headers.setdefault("Content-Type", "application/json") | |
44 | else: | |
45 | headers = {"Content-Type": "application/json"} | |
46 | ||
47 | with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required): | |
48 | response = test_app.post( | |
49 | "/api/user/{0}/feed".format(self.user.username), | |
50 | json.dumps(activity), | |
51 | headers=headers | |
52 | ) | |
53 | ||
54 | return response, json.loads(response.body) | |
55 | ||
56 | def _upload_image(self, test_app, image): | |
57 | """ Uploads and image to MediaGoblin via pump.io API """ | |
58 | data = open(image, "rb").read() | |
59 | headers = { | |
60 | "Content-Type": "image/jpeg", | |
61 | "Content-Length": str(len(data)) | |
62 | } | |
63 | ||
64 | ||
65 | with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required): | |
66 | response = test_app.post( | |
67 | "/api/user/{0}/uploads".format(self.user.username), | |
68 | data, | |
69 | headers=headers | |
70 | ) | |
71 | image = json.loads(response.body) | |
72 | ||
73 | return response, image | |
74 | ||
75 | def _post_image_to_feed(self, test_app, image): | |
76 | """ Posts an already uploaded image to feed """ | |
77 | activity = { | |
78 | "verb": "post", | |
79 | "object": image, | |
80 | } | |
81 | ||
82 | return self._activity_to_feed(test_app, activity) | |
83 | ||
84 | ||
967df5ef JT |
85 | def mocked_oauth_required(self, *args, **kwargs): |
86 | """ Mocks mediagoblin.decorator.oauth_required to always validate """ | |
87 | ||
88 | def fake_controller(controller, request, *args, **kwargs): | |
89 | request.user = User.query.filter_by(id=self.user.id).first() | |
90 | return controller(request, *args, **kwargs) | |
91 | ||
92 | def oauth_required(c): | |
93 | return lambda *args, **kwargs: fake_controller(c, *args, **kwargs) | |
94 | ||
95 | return oauth_required | |
96 | ||
ee9956c3 JT |
97 | def test_can_post_image(self, test_app): |
98 | """ Tests that an image can be posted to the API """ | |
99 | # First request we need to do is to upload the image | |
51ab5192 | 100 | response, image = self._upload_image(test_app, GOOD_JPG) |
ee9956c3 | 101 | |
51ab5192 JT |
102 | # I should have got certain things back |
103 | assert response.status_code == 200 | |
ee9956c3 | 104 | |
51ab5192 JT |
105 | assert "id" in image |
106 | assert "fullImage" in image | |
107 | assert "url" in image["fullImage"] | |
108 | assert "url" in image | |
109 | assert "author" in image | |
110 | assert "published" in image | |
111 | assert "updated" in image | |
112 | assert image["objectType"] == "image" | |
247a3b78 | 113 | |
51ab5192 JT |
114 | # Check that we got the response we're expecting |
115 | response, _ = self._post_image_to_feed(test_app, image) | |
116 | assert response.status_code == 200 | |
57c6473a | 117 | |
51ab5192 JT |
118 | def test_upload_image_with_filename(self, test_app): |
119 | """ Tests that you can upload an image with filename and description """ | |
120 | response, data = self._upload_image(test_app, GOOD_JPG) | |
121 | response, data = self._post_image_to_feed(test_app, data) | |
ee9956c3 | 122 | |
51ab5192 JT |
123 | image = data["object"] |
124 | ||
125 | # Now we need to add a title and description | |
126 | title = "My image ^_^" | |
127 | description = "This is my super awesome image :D" | |
128 | license = "CC-BY-SA" | |
129 | ||
130 | image["displayName"] = title | |
131 | image["content"] = description | |
132 | image["license"] = license | |
133 | ||
134 | activity = {"verb": "update", "object": image} | |
135 | ||
136 | with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required): | |
ee9956c3 JT |
137 | response = test_app.post( |
138 | "/api/user/{0}/feed".format(self.user.username), | |
51ab5192 JT |
139 | json.dumps(activity), |
140 | headers={"Content-Type": "application/json"} | |
ee9956c3 JT |
141 | ) |
142 | ||
51ab5192 JT |
143 | image = json.loads(response.body)["object"] |
144 | ||
145 | # Check everything has been set on the media correctly | |
146 | media = MediaEntry.query.filter_by(id=image["id"]).first() | |
147 | assert media.title == title | |
148 | assert media.description == description | |
149 | assert media.license == license | |
150 | ||
151 | # Check we're being given back everything we should on an update | |
152 | assert image["id"] == media.id | |
153 | assert image["displayName"] == title | |
154 | assert image["content"] == description | |
155 | assert image["license"] == license | |
156 | ||
ee9956c3 JT |
157 | |
158 | def test_only_uploaders_post_image(self, test_app): | |
159 | """ Test that only uploaders can upload images """ | |
160 | # Remove uploader permissions from user | |
161 | take_away_privileges(self.user.username, u"uploader") | |
162 | ||
163 | # Now try and upload a image | |
164 | data = open(GOOD_JPG, "rb").read() | |
165 | headers = { | |
166 | "Content-Type": "image/jpeg", | |
167 | "Content-Length": str(len(data)), | |
168 | } | |
169 | ||
967df5ef JT |
170 | with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required): |
171 | with pytest.raises(AppError) as excinfo: | |
172 | response = test_app.post( | |
173 | "/api/user/{0}/uploads".format(self.user.username), | |
174 | data, | |
175 | headers=headers | |
176 | ) | |
57c6473a | 177 | |
ee9956c3 | 178 | # Assert that we've got a 403 |
967df5ef | 179 | assert "403 FORBIDDEN" in excinfo.value.message |
51ab5192 | 180 | |
3c8bd177 JT |
181 | def test_object_endpoint(self, test_app): |
182 | """ Tests that object can be looked up at endpoint """ | |
183 | # Post an image | |
184 | response, data = self._upload_image(test_app, GOOD_JPG) | |
185 | response, data = self._post_image_to_feed(test_app, data) | |
186 | ||
187 | # Now lookup image to check that endpoint works. | |
188 | image = data["object"] | |
189 | ||
190 | assert "links" in image | |
191 | assert "self" in image["links"] | |
192 | ||
193 | # Get URI and strip testing host off | |
194 | object_uri = image["links"]["self"]["href"] | |
195 | object_uri = object_uri.replace("http://localhost:80", "") | |
196 | ||
197 | with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required): | |
198 | request = test_app.get(object_uri) | |
199 | ||
200 | image = json.loads(request.body) | |
201 | entry = MediaEntry.query.filter_by(id=image["id"]).first() | |
202 | ||
203 | assert request.status_code == 200 | |
204 | assert entry.id == image["id"] | |
205 | ||
206 | assert "image" in image | |
207 | assert "fullImage" in image | |
208 | assert "pump_io" in image | |
209 | assert "links" in image | |
51ab5192 JT |
210 | |
211 | def test_post_comment(self, test_app): | |
212 | """ Tests that I can post an comment media """ | |
213 | # Upload some media to comment on | |
214 | response, data = self._upload_image(test_app, GOOD_JPG) | |
215 | response, data = self._post_image_to_feed(test_app, data) | |
216 | ||
217 | content = "Hai this is a comment on this lovely picture ^_^" | |
218 | ||
219 | activity = { | |
220 | "verb": "post", | |
221 | "object": { | |
222 | "objectType": "comment", | |
223 | "content": content, | |
224 | "inReplyTo": data["object"], | |
225 | } | |
226 | } | |
227 | ||
228 | response, comment_data = self._activity_to_feed(test_app, activity) | |
229 | assert response.status_code == 200 | |
230 | ||
231 | # Find the objects in the database | |
232 | media = MediaEntry.query.filter_by(id=data["object"]["id"]).first() | |
233 | comment = media.get_comments()[0] | |
234 | ||
235 | # Tests that it matches in the database | |
236 | assert comment.author == self.user.id | |
237 | assert comment.content == content | |
238 | ||
239 | # Test that the response is what we should be given | |
240 | assert comment.id == comment_data["object"]["id"] | |
241 | assert comment.content == comment_data["object"]["content"] | |
242 | ||
243 | def test_profile(self, test_app): | |
244 | """ Tests profile endpoint """ | |
245 | uri = "/api/user/{0}/profile".format(self.user.username) | |
246 | with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required): | |
247 | response = test_app.get(uri) | |
248 | profile = json.loads(response.body) | |
249 | ||
250 | assert response.status_code == 200 | |
251 | ||
252 | assert profile["preferredUsername"] == self.user.username | |
253 | assert profile["objectType"] == "person" | |
254 | ||
255 | assert "links" in profile |