Add more tests for federation APIs
[mediagoblin.git] / mediagoblin / tests / test_api.py
CommitLineData
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 16import urllib
ee9956c3 17import json
57c6473a 18
5c2ece74 19import pytest
247a3b78 20import mock
21
967df5ef
JT
22from webtest import AppError
23
57c6473a 24from mediagoblin import mg_globals
247a3b78 25from .resources import GOOD_JPG
51ab5192 26from mediagoblin.db.models import User, MediaEntry
ee9956c3
JT
27from mediagoblin.tests.tools import fixture_add_user
28from mediagoblin.moderation.tools import take_away_privileges
51ab5192 29from .resources import GOOD_JPG
ee9956c3 30
57c6473a 31class 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
JT
180
181
182 def test_post_comment(self, test_app):
183 """ Tests that I can post an comment media """
184 # Upload some media to comment on
185 response, data = self._upload_image(test_app, GOOD_JPG)
186 response, data = self._post_image_to_feed(test_app, data)
187
188 content = "Hai this is a comment on this lovely picture ^_^"
189
190 activity = {
191 "verb": "post",
192 "object": {
193 "objectType": "comment",
194 "content": content,
195 "inReplyTo": data["object"],
196 }
197 }
198
199 response, comment_data = self._activity_to_feed(test_app, activity)
200 assert response.status_code == 200
201
202 # Find the objects in the database
203 media = MediaEntry.query.filter_by(id=data["object"]["id"]).first()
204 comment = media.get_comments()[0]
205
206 # Tests that it matches in the database
207 assert comment.author == self.user.id
208 assert comment.content == content
209
210 # Test that the response is what we should be given
211 assert comment.id == comment_data["object"]["id"]
212 assert comment.content == comment_data["object"]["content"]
213
214 def test_profile(self, test_app):
215 """ Tests profile endpoint """
216 uri = "/api/user/{0}/profile".format(self.user.username)
217 with mock.patch("mediagoblin.decorators.oauth_required", new_callable=self.mocked_oauth_required):
218 response = test_app.get(uri)
219 profile = json.loads(response.body)
220
221 assert response.status_code == 200
222
223 assert profile["preferredUsername"] == self.user.username
224 assert profile["objectType"] == "person"
225
226 assert "links" in profile