Fix queue name
[akkoma] / test / web / activity_pub / activity_pub_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
6 use Pleroma.Web.ConnCase
7 import Pleroma.Factory
8 alias Pleroma.Web.ActivityPub.{UserView, ObjectView}
9 alias Pleroma.{Object, Repo, Activity, User, Instances}
10
11 setup_all do
12 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
13 :ok
14 end
15
16 describe "/relay" do
17 test "with the relay active, it returns the relay user", %{conn: conn} do
18 res =
19 conn
20 |> get(activity_pub_path(conn, :relay))
21 |> json_response(200)
22
23 assert res["id"] =~ "/relay"
24 end
25
26 test "with the relay disabled, it returns 404", %{conn: conn} do
27 Pleroma.Config.put([:instance, :allow_relay], false)
28
29 conn
30 |> get(activity_pub_path(conn, :relay))
31 |> json_response(404)
32 |> assert
33
34 Pleroma.Config.put([:instance, :allow_relay], true)
35 end
36 end
37
38 describe "/users/:nickname" do
39 test "it returns a json representation of the user", %{conn: conn} do
40 user = insert(:user)
41
42 conn =
43 conn
44 |> put_req_header("accept", "application/activity+json")
45 |> get("/users/#{user.nickname}")
46
47 user = Repo.get(User, user.id)
48
49 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
50 end
51 end
52
53 describe "/object/:uuid" do
54 test "it returns a json representation of the object", %{conn: conn} do
55 note = insert(:note)
56 uuid = String.split(note.data["id"], "/") |> List.last()
57
58 conn =
59 conn
60 |> put_req_header("accept", "application/activity+json")
61 |> get("/objects/#{uuid}")
62
63 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
64 end
65
66 test "it returns 404 for non-public messages", %{conn: conn} do
67 note = insert(:direct_note)
68 uuid = String.split(note.data["id"], "/") |> List.last()
69
70 conn =
71 conn
72 |> put_req_header("accept", "application/activity+json")
73 |> get("/objects/#{uuid}")
74
75 assert json_response(conn, 404)
76 end
77
78 test "it returns 404 for tombstone objects", %{conn: conn} do
79 tombstone = insert(:tombstone)
80 uuid = String.split(tombstone.data["id"], "/") |> List.last()
81
82 conn =
83 conn
84 |> put_req_header("accept", "application/activity+json")
85 |> get("/objects/#{uuid}")
86
87 assert json_response(conn, 404)
88 end
89 end
90
91 describe "/object/:uuid/likes" do
92 test "it returns the like activities in a collection", %{conn: conn} do
93 like = insert(:like_activity)
94 uuid = String.split(like.data["object"], "/") |> List.last()
95
96 result =
97 conn
98 |> put_req_header("accept", "application/activity+json")
99 |> get("/objects/#{uuid}/likes")
100 |> json_response(200)
101
102 assert List.first(result["first"]["orderedItems"])["id"] == like.data["id"]
103 end
104 end
105
106 describe "/activities/:uuid" do
107 test "it returns a json representation of the activity", %{conn: conn} do
108 activity = insert(:note_activity)
109 uuid = String.split(activity.data["id"], "/") |> List.last()
110
111 conn =
112 conn
113 |> put_req_header("accept", "application/activity+json")
114 |> get("/activities/#{uuid}")
115
116 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
117 end
118
119 test "it returns 404 for non-public activities", %{conn: conn} do
120 activity = insert(:direct_note_activity)
121 uuid = String.split(activity.data["id"], "/") |> List.last()
122
123 conn =
124 conn
125 |> put_req_header("accept", "application/activity+json")
126 |> get("/activities/#{uuid}")
127
128 assert json_response(conn, 404)
129 end
130 end
131
132 describe "/inbox" do
133 test "it inserts an incoming activity into the database", %{conn: conn} do
134 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
135
136 conn =
137 conn
138 |> assign(:valid_signature, true)
139 |> put_req_header("content-type", "application/activity+json")
140 |> post("/inbox", data)
141
142 assert "ok" == json_response(conn, 200)
143 :timer.sleep(500)
144 assert Activity.get_by_ap_id(data["id"])
145 end
146
147 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
148 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
149
150 sender_url = data["actor"]
151 Instances.set_consistently_unreachable(sender_url)
152 refute Instances.reachable?(sender_url)
153
154 conn =
155 conn
156 |> assign(:valid_signature, true)
157 |> put_req_header("content-type", "application/activity+json")
158 |> post("/inbox", data)
159
160 assert "ok" == json_response(conn, 200)
161 assert Instances.reachable?(sender_url)
162 end
163 end
164
165 describe "/users/:nickname/inbox" do
166 test "it inserts an incoming activity into the database", %{conn: conn} do
167 user = insert(:user)
168
169 data =
170 File.read!("test/fixtures/mastodon-post-activity.json")
171 |> Poison.decode!()
172 |> Map.put("bcc", [user.ap_id])
173
174 conn =
175 conn
176 |> assign(:valid_signature, true)
177 |> put_req_header("content-type", "application/activity+json")
178 |> post("/users/#{user.nickname}/inbox", data)
179
180 assert "ok" == json_response(conn, 200)
181 :timer.sleep(500)
182 assert Activity.get_by_ap_id(data["id"])
183 end
184
185 test "it rejects reads from other users", %{conn: conn} do
186 user = insert(:user)
187 otheruser = insert(:user)
188
189 conn =
190 conn
191 |> assign(:user, otheruser)
192 |> put_req_header("accept", "application/activity+json")
193 |> get("/users/#{user.nickname}/inbox")
194
195 assert json_response(conn, 403)
196 end
197
198 test "it returns a note activity in a collection", %{conn: conn} do
199 note_activity = insert(:direct_note_activity)
200 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
201
202 conn =
203 conn
204 |> assign(:user, user)
205 |> put_req_header("accept", "application/activity+json")
206 |> get("/users/#{user.nickname}/inbox")
207
208 assert response(conn, 200) =~ note_activity.data["object"]["content"]
209 end
210
211 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
212 user = insert(:user)
213
214 data =
215 File.read!("test/fixtures/mastodon-post-activity.json")
216 |> Poison.decode!()
217 |> Map.put("bcc", [user.ap_id])
218
219 sender_host = URI.parse(data["actor"]).host
220 Instances.set_consistently_unreachable(sender_host)
221 refute Instances.reachable?(sender_host)
222
223 conn =
224 conn
225 |> assign(:valid_signature, true)
226 |> put_req_header("content-type", "application/activity+json")
227 |> post("/users/#{user.nickname}/inbox", data)
228
229 assert "ok" == json_response(conn, 200)
230 assert Instances.reachable?(sender_host)
231 end
232 end
233
234 describe "/users/:nickname/outbox" do
235 test "it returns a note activity in a collection", %{conn: conn} do
236 note_activity = insert(:note_activity)
237 user = User.get_cached_by_ap_id(note_activity.data["actor"])
238
239 conn =
240 conn
241 |> put_req_header("accept", "application/activity+json")
242 |> get("/users/#{user.nickname}/outbox")
243
244 assert response(conn, 200) =~ note_activity.data["object"]["content"]
245 end
246
247 test "it returns an announce activity in a collection", %{conn: conn} do
248 announce_activity = insert(:announce_activity)
249 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
250
251 conn =
252 conn
253 |> put_req_header("accept", "application/activity+json")
254 |> get("/users/#{user.nickname}/outbox")
255
256 assert response(conn, 200) =~ announce_activity.data["object"]
257 end
258
259 test "it rejects posts from other users", %{conn: conn} do
260 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
261 user = insert(:user)
262 otheruser = insert(:user)
263
264 conn =
265 conn
266 |> assign(:user, otheruser)
267 |> put_req_header("content-type", "application/activity+json")
268 |> post("/users/#{user.nickname}/outbox", data)
269
270 assert json_response(conn, 403)
271 end
272
273 test "it inserts an incoming create activity into the database", %{conn: conn} do
274 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
275 user = insert(:user)
276
277 conn =
278 conn
279 |> assign(:user, user)
280 |> put_req_header("content-type", "application/activity+json")
281 |> post("/users/#{user.nickname}/outbox", data)
282
283 result = json_response(conn, 201)
284 assert Activity.get_by_ap_id(result["id"])
285 end
286
287 test "it rejects an incoming activity with bogus type", %{conn: conn} do
288 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
289 user = insert(:user)
290
291 data =
292 data
293 |> Map.put("type", "BadType")
294
295 conn =
296 conn
297 |> assign(:user, user)
298 |> put_req_header("content-type", "application/activity+json")
299 |> post("/users/#{user.nickname}/outbox", data)
300
301 assert json_response(conn, 400)
302 end
303
304 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
305 note_activity = insert(:note_activity)
306 user = User.get_cached_by_ap_id(note_activity.data["actor"])
307
308 data = %{
309 type: "Delete",
310 object: %{
311 id: note_activity.data["object"]["id"]
312 }
313 }
314
315 conn =
316 conn
317 |> assign(:user, user)
318 |> put_req_header("content-type", "application/activity+json")
319 |> post("/users/#{user.nickname}/outbox", data)
320
321 result = json_response(conn, 201)
322 assert Activity.get_by_ap_id(result["id"])
323
324 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
325 assert object
326 assert object.data["type"] == "Tombstone"
327 end
328
329 test "it rejects delete activity of object from other actor", %{conn: conn} do
330 note_activity = insert(:note_activity)
331 user = insert(:user)
332
333 data = %{
334 type: "Delete",
335 object: %{
336 id: note_activity.data["object"]["id"]
337 }
338 }
339
340 conn =
341 conn
342 |> assign(:user, user)
343 |> put_req_header("content-type", "application/activity+json")
344 |> post("/users/#{user.nickname}/outbox", data)
345
346 assert json_response(conn, 400)
347 end
348
349 test "it increases like count when receiving a like action", %{conn: conn} do
350 note_activity = insert(:note_activity)
351 user = User.get_cached_by_ap_id(note_activity.data["actor"])
352
353 data = %{
354 type: "Like",
355 object: %{
356 id: note_activity.data["object"]["id"]
357 }
358 }
359
360 conn =
361 conn
362 |> assign(:user, user)
363 |> put_req_header("content-type", "application/activity+json")
364 |> post("/users/#{user.nickname}/outbox", data)
365
366 result = json_response(conn, 201)
367 assert Activity.get_by_ap_id(result["id"])
368
369 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
370 assert object
371 assert object.data["like_count"] == 1
372 end
373 end
374
375 describe "/users/:nickname/followers" do
376 test "it returns the followers in a collection", %{conn: conn} do
377 user = insert(:user)
378 user_two = insert(:user)
379 User.follow(user, user_two)
380
381 result =
382 conn
383 |> get("/users/#{user_two.nickname}/followers")
384 |> json_response(200)
385
386 assert result["first"]["orderedItems"] == [user.ap_id]
387 end
388
389 test "it returns returns empty if the user has 'hide_followers' set", %{conn: conn} do
390 user = insert(:user)
391 user_two = insert(:user, %{info: %{hide_followers: true}})
392 User.follow(user, user_two)
393
394 result =
395 conn
396 |> get("/users/#{user_two.nickname}/followers")
397 |> json_response(200)
398
399 assert result["first"]["orderedItems"] == []
400 assert result["totalItems"] == 1
401 end
402
403 test "it works for more than 10 users", %{conn: conn} do
404 user = insert(:user)
405
406 Enum.each(1..15, fn _ ->
407 other_user = insert(:user)
408 User.follow(other_user, user)
409 end)
410
411 result =
412 conn
413 |> get("/users/#{user.nickname}/followers")
414 |> json_response(200)
415
416 assert length(result["first"]["orderedItems"]) == 10
417 assert result["first"]["totalItems"] == 15
418 assert result["totalItems"] == 15
419
420 result =
421 conn
422 |> get("/users/#{user.nickname}/followers?page=2")
423 |> json_response(200)
424
425 assert length(result["orderedItems"]) == 5
426 assert result["totalItems"] == 15
427 end
428 end
429
430 describe "/users/:nickname/following" do
431 test "it returns the following in a collection", %{conn: conn} do
432 user = insert(:user)
433 user_two = insert(:user)
434 User.follow(user, user_two)
435
436 result =
437 conn
438 |> get("/users/#{user.nickname}/following")
439 |> json_response(200)
440
441 assert result["first"]["orderedItems"] == [user_two.ap_id]
442 end
443
444 test "it returns returns empty if the user has 'hide_follows' set", %{conn: conn} do
445 user = insert(:user, %{info: %{hide_follows: true}})
446 user_two = insert(:user)
447 User.follow(user, user_two)
448
449 result =
450 conn
451 |> get("/users/#{user.nickname}/following")
452 |> json_response(200)
453
454 assert result["first"]["orderedItems"] == []
455 assert result["totalItems"] == 1
456 end
457
458 test "it works for more than 10 users", %{conn: conn} do
459 user = insert(:user)
460
461 Enum.each(1..15, fn _ ->
462 user = Repo.get(User, user.id)
463 other_user = insert(:user)
464 User.follow(user, other_user)
465 end)
466
467 result =
468 conn
469 |> get("/users/#{user.nickname}/following")
470 |> json_response(200)
471
472 assert length(result["first"]["orderedItems"]) == 10
473 assert result["first"]["totalItems"] == 15
474 assert result["totalItems"] == 15
475
476 result =
477 conn
478 |> get("/users/#{user.nickname}/following?page=2")
479 |> json_response(200)
480
481 assert length(result["orderedItems"]) == 5
482 assert result["totalItems"] == 15
483 end
484 end
485 end