Merge remote-tracking branch 'remotes/origin/develop' into 1560-non-federating-instan...
[akkoma] / test / web / activity_pub / activity_pub_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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 use Oban.Testing, repo: Pleroma.Repo
8
9 import Pleroma.Factory
10 alias Pleroma.Activity
11 alias Pleroma.Delivery
12 alias Pleroma.Instances
13 alias Pleroma.Object
14 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ObjectView
17 alias Pleroma.Web.ActivityPub.Relay
18 alias Pleroma.Web.ActivityPub.UserView
19 alias Pleroma.Web.ActivityPub.Utils
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Workers.ReceiverWorker
22
23 setup_all do
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 :ok
26 end
27
28 clear_config_all([:instance, :federating]) do
29 Pleroma.Config.put([:instance, :federating], true)
30 end
31
32 describe "/relay" do
33 clear_config([:instance, :allow_relay])
34
35 test "with the relay active, it returns the relay user", %{conn: conn} do
36 res =
37 conn
38 |> get(activity_pub_path(conn, :relay))
39 |> json_response(200)
40
41 assert res["id"] =~ "/relay"
42 end
43
44 test "with the relay disabled, it returns 404", %{conn: conn} do
45 Pleroma.Config.put([:instance, :allow_relay], false)
46
47 conn
48 |> get(activity_pub_path(conn, :relay))
49 |> json_response(404)
50 |> assert
51 end
52 end
53
54 describe "/internal/fetch" do
55 test "it returns the internal fetch user", %{conn: conn} do
56 res =
57 conn
58 |> get(activity_pub_path(conn, :internal_fetch))
59 |> json_response(200)
60
61 assert res["id"] =~ "/fetch"
62 end
63 end
64
65 describe "/users/:nickname" do
66 test "it returns a json representation of the user with accept application/json", %{
67 conn: conn
68 } do
69 user = insert(:user)
70
71 conn =
72 conn
73 |> put_req_header("accept", "application/json")
74 |> get("/users/#{user.nickname}")
75
76 user = User.get_cached_by_id(user.id)
77
78 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
79 end
80
81 test "it returns a json representation of the user with accept application/activity+json", %{
82 conn: conn
83 } do
84 user = insert(:user)
85
86 conn =
87 conn
88 |> put_req_header("accept", "application/activity+json")
89 |> get("/users/#{user.nickname}")
90
91 user = User.get_cached_by_id(user.id)
92
93 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
94 end
95
96 test "it returns a json representation of the user with accept application/ld+json", %{
97 conn: conn
98 } do
99 user = insert(:user)
100
101 conn =
102 conn
103 |> put_req_header(
104 "accept",
105 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
106 )
107 |> get("/users/#{user.nickname}")
108
109 user = User.get_cached_by_id(user.id)
110
111 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
112 end
113
114 test "it returns 404 for remote users", %{
115 conn: conn
116 } do
117 user = insert(:user, local: false, nickname: "remoteuser@example.com")
118
119 conn =
120 conn
121 |> put_req_header("accept", "application/json")
122 |> get("/users/#{user.nickname}.json")
123
124 assert json_response(conn, 404)
125 end
126 end
127
128 describe "/object/:uuid" do
129 test "it returns a json representation of the object with accept application/json", %{
130 conn: conn
131 } do
132 note = insert(:note)
133 uuid = String.split(note.data["id"], "/") |> List.last()
134
135 conn =
136 conn
137 |> put_req_header("accept", "application/json")
138 |> get("/objects/#{uuid}")
139
140 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
141 end
142
143 test "it returns a json representation of the object with accept application/activity+json",
144 %{conn: conn} do
145 note = insert(:note)
146 uuid = String.split(note.data["id"], "/") |> List.last()
147
148 conn =
149 conn
150 |> put_req_header("accept", "application/activity+json")
151 |> get("/objects/#{uuid}")
152
153 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
154 end
155
156 test "it returns a json representation of the object with accept application/ld+json", %{
157 conn: conn
158 } do
159 note = insert(:note)
160 uuid = String.split(note.data["id"], "/") |> List.last()
161
162 conn =
163 conn
164 |> put_req_header(
165 "accept",
166 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
167 )
168 |> get("/objects/#{uuid}")
169
170 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
171 end
172
173 test "it returns 404 for non-public messages", %{conn: conn} do
174 note = insert(:direct_note)
175 uuid = String.split(note.data["id"], "/") |> List.last()
176
177 conn =
178 conn
179 |> put_req_header("accept", "application/activity+json")
180 |> get("/objects/#{uuid}")
181
182 assert json_response(conn, 404)
183 end
184
185 test "it returns 404 for tombstone objects", %{conn: conn} do
186 tombstone = insert(:tombstone)
187 uuid = String.split(tombstone.data["id"], "/") |> List.last()
188
189 conn =
190 conn
191 |> put_req_header("accept", "application/activity+json")
192 |> get("/objects/#{uuid}")
193
194 assert json_response(conn, 404)
195 end
196
197 test "it caches a response", %{conn: conn} do
198 note = insert(:note)
199 uuid = String.split(note.data["id"], "/") |> List.last()
200
201 conn1 =
202 conn
203 |> put_req_header("accept", "application/activity+json")
204 |> get("/objects/#{uuid}")
205
206 assert json_response(conn1, :ok)
207 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
208
209 conn2 =
210 conn
211 |> put_req_header("accept", "application/activity+json")
212 |> get("/objects/#{uuid}")
213
214 assert json_response(conn1, :ok) == json_response(conn2, :ok)
215 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
216 end
217
218 test "cached purged after object deletion", %{conn: conn} do
219 note = insert(:note)
220 uuid = String.split(note.data["id"], "/") |> List.last()
221
222 conn1 =
223 conn
224 |> put_req_header("accept", "application/activity+json")
225 |> get("/objects/#{uuid}")
226
227 assert json_response(conn1, :ok)
228 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
229
230 Object.delete(note)
231
232 conn2 =
233 conn
234 |> put_req_header("accept", "application/activity+json")
235 |> get("/objects/#{uuid}")
236
237 assert "Not found" == json_response(conn2, :not_found)
238 end
239 end
240
241 describe "/activities/:uuid" do
242 test "it returns a json representation of the activity", %{conn: conn} do
243 activity = insert(:note_activity)
244 uuid = String.split(activity.data["id"], "/") |> List.last()
245
246 conn =
247 conn
248 |> put_req_header("accept", "application/activity+json")
249 |> get("/activities/#{uuid}")
250
251 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
252 end
253
254 test "it returns 404 for non-public activities", %{conn: conn} do
255 activity = insert(:direct_note_activity)
256 uuid = String.split(activity.data["id"], "/") |> List.last()
257
258 conn =
259 conn
260 |> put_req_header("accept", "application/activity+json")
261 |> get("/activities/#{uuid}")
262
263 assert json_response(conn, 404)
264 end
265
266 test "it caches a response", %{conn: conn} do
267 activity = insert(:note_activity)
268 uuid = String.split(activity.data["id"], "/") |> List.last()
269
270 conn1 =
271 conn
272 |> put_req_header("accept", "application/activity+json")
273 |> get("/activities/#{uuid}")
274
275 assert json_response(conn1, :ok)
276 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
277
278 conn2 =
279 conn
280 |> put_req_header("accept", "application/activity+json")
281 |> get("/activities/#{uuid}")
282
283 assert json_response(conn1, :ok) == json_response(conn2, :ok)
284 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
285 end
286
287 test "cached purged after activity deletion", %{conn: conn} do
288 user = insert(:user)
289 {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"})
290
291 uuid = String.split(activity.data["id"], "/") |> List.last()
292
293 conn1 =
294 conn
295 |> put_req_header("accept", "application/activity+json")
296 |> get("/activities/#{uuid}")
297
298 assert json_response(conn1, :ok)
299 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
300
301 Activity.delete_all_by_object_ap_id(activity.object.data["id"])
302
303 conn2 =
304 conn
305 |> put_req_header("accept", "application/activity+json")
306 |> get("/activities/#{uuid}")
307
308 assert "Not found" == json_response(conn2, :not_found)
309 end
310 end
311
312 describe "/inbox" do
313 test "it inserts an incoming activity into the database", %{conn: conn} do
314 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
315
316 conn =
317 conn
318 |> assign(:valid_signature, true)
319 |> put_req_header("content-type", "application/activity+json")
320 |> post("/inbox", data)
321
322 assert "ok" == json_response(conn, 200)
323
324 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
325 assert Activity.get_by_ap_id(data["id"])
326 end
327
328 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
329 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
330
331 sender_url = data["actor"]
332 Instances.set_consistently_unreachable(sender_url)
333 refute Instances.reachable?(sender_url)
334
335 conn =
336 conn
337 |> assign(:valid_signature, true)
338 |> put_req_header("content-type", "application/activity+json")
339 |> post("/inbox", data)
340
341 assert "ok" == json_response(conn, 200)
342 assert Instances.reachable?(sender_url)
343 end
344 end
345
346 describe "/users/:nickname/inbox" do
347 setup do
348 data =
349 File.read!("test/fixtures/mastodon-post-activity.json")
350 |> Poison.decode!()
351
352 [data: data]
353 end
354
355 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
356 user = insert(:user)
357 data = Map.put(data, "bcc", [user.ap_id])
358
359 conn =
360 conn
361 |> assign(:valid_signature, true)
362 |> put_req_header("content-type", "application/activity+json")
363 |> post("/users/#{user.nickname}/inbox", data)
364
365 assert "ok" == json_response(conn, 200)
366 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
367 assert Activity.get_by_ap_id(data["id"])
368 end
369
370 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
371 user = insert(:user)
372
373 data =
374 Map.put(data, "to", user.ap_id)
375 |> Map.delete("cc")
376
377 conn =
378 conn
379 |> assign(:valid_signature, true)
380 |> put_req_header("content-type", "application/activity+json")
381 |> post("/users/#{user.nickname}/inbox", data)
382
383 assert "ok" == json_response(conn, 200)
384 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
385 assert Activity.get_by_ap_id(data["id"])
386 end
387
388 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
389 user = insert(:user)
390
391 data =
392 Map.put(data, "cc", user.ap_id)
393 |> Map.delete("to")
394
395 conn =
396 conn
397 |> assign(:valid_signature, true)
398 |> put_req_header("content-type", "application/activity+json")
399 |> post("/users/#{user.nickname}/inbox", data)
400
401 assert "ok" == json_response(conn, 200)
402 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
403 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
404 assert user.ap_id in activity.recipients
405 end
406
407 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
408 user = insert(:user)
409
410 data =
411 Map.put(data, "bcc", user.ap_id)
412 |> Map.delete("to")
413 |> Map.delete("cc")
414
415 conn =
416 conn
417 |> assign(:valid_signature, true)
418 |> put_req_header("content-type", "application/activity+json")
419 |> post("/users/#{user.nickname}/inbox", data)
420
421 assert "ok" == json_response(conn, 200)
422 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
423 assert Activity.get_by_ap_id(data["id"])
424 end
425
426 test "it accepts announces with to as string instead of array", %{conn: conn} do
427 user = insert(:user)
428
429 data = %{
430 "@context" => "https://www.w3.org/ns/activitystreams",
431 "actor" => "http://mastodon.example.org/users/admin",
432 "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
433 "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
434 "to" => "https://www.w3.org/ns/activitystreams#Public",
435 "cc" => [user.ap_id],
436 "type" => "Announce"
437 }
438
439 conn =
440 conn
441 |> assign(:valid_signature, true)
442 |> put_req_header("content-type", "application/activity+json")
443 |> post("/users/#{user.nickname}/inbox", data)
444
445 assert "ok" == json_response(conn, 200)
446 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
447 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
448 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
449 end
450
451 test "it accepts messages from actors that are followed by the user", %{
452 conn: conn,
453 data: data
454 } do
455 recipient = insert(:user)
456 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
457
458 {:ok, recipient} = User.follow(recipient, actor)
459
460 object =
461 data["object"]
462 |> Map.put("attributedTo", actor.ap_id)
463
464 data =
465 data
466 |> Map.put("actor", actor.ap_id)
467 |> Map.put("object", object)
468
469 conn =
470 conn
471 |> assign(:valid_signature, true)
472 |> put_req_header("content-type", "application/activity+json")
473 |> post("/users/#{recipient.nickname}/inbox", data)
474
475 assert "ok" == json_response(conn, 200)
476 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
477 assert Activity.get_by_ap_id(data["id"])
478 end
479
480 test "it rejects reads from other users", %{conn: conn} do
481 user = insert(:user)
482 otheruser = insert(:user)
483
484 conn =
485 conn
486 |> assign(:user, otheruser)
487 |> put_req_header("accept", "application/activity+json")
488 |> get("/users/#{user.nickname}/inbox")
489
490 assert json_response(conn, 403)
491 end
492
493 test "it doesn't crash without an authenticated user", %{conn: conn} do
494 user = insert(:user)
495
496 conn =
497 conn
498 |> put_req_header("accept", "application/activity+json")
499 |> get("/users/#{user.nickname}/inbox")
500
501 assert json_response(conn, 403)
502 end
503
504 test "it returns a note activity in a collection", %{conn: conn} do
505 note_activity = insert(:direct_note_activity)
506 note_object = Object.normalize(note_activity)
507 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
508
509 conn =
510 conn
511 |> assign(:user, user)
512 |> put_req_header("accept", "application/activity+json")
513 |> get("/users/#{user.nickname}/inbox?page=true")
514
515 assert response(conn, 200) =~ note_object.data["content"]
516 end
517
518 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
519 user = insert(:user)
520 data = Map.put(data, "bcc", [user.ap_id])
521
522 sender_host = URI.parse(data["actor"]).host
523 Instances.set_consistently_unreachable(sender_host)
524 refute Instances.reachable?(sender_host)
525
526 conn =
527 conn
528 |> assign(:valid_signature, true)
529 |> put_req_header("content-type", "application/activity+json")
530 |> post("/users/#{user.nickname}/inbox", data)
531
532 assert "ok" == json_response(conn, 200)
533 assert Instances.reachable?(sender_host)
534 end
535
536 test "it removes all follower collections but actor's", %{conn: conn} do
537 [actor, recipient] = insert_pair(:user)
538
539 data =
540 File.read!("test/fixtures/activitypub-client-post-activity.json")
541 |> Poison.decode!()
542
543 object = Map.put(data["object"], "attributedTo", actor.ap_id)
544
545 data =
546 data
547 |> Map.put("id", Utils.generate_object_id())
548 |> Map.put("actor", actor.ap_id)
549 |> Map.put("object", object)
550 |> Map.put("cc", [
551 recipient.follower_address,
552 actor.follower_address
553 ])
554 |> Map.put("to", [
555 recipient.ap_id,
556 recipient.follower_address,
557 "https://www.w3.org/ns/activitystreams#Public"
558 ])
559
560 conn
561 |> assign(:valid_signature, true)
562 |> put_req_header("content-type", "application/activity+json")
563 |> post("/users/#{recipient.nickname}/inbox", data)
564 |> json_response(200)
565
566 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
567
568 activity = Activity.get_by_ap_id(data["id"])
569
570 assert activity.id
571 assert actor.follower_address in activity.recipients
572 assert actor.follower_address in activity.data["cc"]
573
574 refute recipient.follower_address in activity.recipients
575 refute recipient.follower_address in activity.data["cc"]
576 refute recipient.follower_address in activity.data["to"]
577 end
578 end
579
580 describe "GET /users/:nickname/outbox" do
581 test "it will not bomb when there is no activity", %{conn: conn} do
582 user = insert(:user)
583
584 conn =
585 conn
586 |> put_req_header("accept", "application/activity+json")
587 |> get("/users/#{user.nickname}/outbox")
588
589 result = json_response(conn, 200)
590 assert user.ap_id <> "/outbox" == result["id"]
591 end
592
593 test "it returns a note activity in a collection", %{conn: conn} do
594 note_activity = insert(:note_activity)
595 note_object = Object.normalize(note_activity)
596 user = User.get_cached_by_ap_id(note_activity.data["actor"])
597
598 conn =
599 conn
600 |> put_req_header("accept", "application/activity+json")
601 |> get("/users/#{user.nickname}/outbox?page=true")
602
603 assert response(conn, 200) =~ note_object.data["content"]
604 end
605
606 test "it returns an announce activity in a collection", %{conn: conn} do
607 announce_activity = insert(:announce_activity)
608 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
609
610 conn =
611 conn
612 |> put_req_header("accept", "application/activity+json")
613 |> get("/users/#{user.nickname}/outbox?page=true")
614
615 assert response(conn, 200) =~ announce_activity.data["object"]
616 end
617 end
618
619 describe "POST /users/:nickname/outbox" do
620 test "it rejects posts from other users", %{conn: conn} do
621 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
622 user = insert(:user)
623 otheruser = insert(:user)
624
625 conn =
626 conn
627 |> assign(:user, otheruser)
628 |> put_req_header("content-type", "application/activity+json")
629 |> post("/users/#{user.nickname}/outbox", data)
630
631 assert json_response(conn, 403)
632 end
633
634 test "it inserts an incoming create activity into the database", %{conn: conn} do
635 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
636 user = insert(:user)
637
638 conn =
639 conn
640 |> assign(:user, user)
641 |> put_req_header("content-type", "application/activity+json")
642 |> post("/users/#{user.nickname}/outbox", data)
643
644 result = json_response(conn, 201)
645
646 assert Activity.get_by_ap_id(result["id"])
647 end
648
649 test "it rejects an incoming activity with bogus type", %{conn: conn} do
650 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
651 user = insert(:user)
652
653 data =
654 data
655 |> Map.put("type", "BadType")
656
657 conn =
658 conn
659 |> assign(:user, user)
660 |> put_req_header("content-type", "application/activity+json")
661 |> post("/users/#{user.nickname}/outbox", data)
662
663 assert json_response(conn, 400)
664 end
665
666 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
667 note_activity = insert(:note_activity)
668 note_object = Object.normalize(note_activity)
669 user = User.get_cached_by_ap_id(note_activity.data["actor"])
670
671 data = %{
672 type: "Delete",
673 object: %{
674 id: note_object.data["id"]
675 }
676 }
677
678 conn =
679 conn
680 |> assign(:user, user)
681 |> put_req_header("content-type", "application/activity+json")
682 |> post("/users/#{user.nickname}/outbox", data)
683
684 result = json_response(conn, 201)
685 assert Activity.get_by_ap_id(result["id"])
686
687 assert object = Object.get_by_ap_id(note_object.data["id"])
688 assert object.data["type"] == "Tombstone"
689 end
690
691 test "it rejects delete activity of object from other actor", %{conn: conn} do
692 note_activity = insert(:note_activity)
693 note_object = Object.normalize(note_activity)
694 user = insert(:user)
695
696 data = %{
697 type: "Delete",
698 object: %{
699 id: note_object.data["id"]
700 }
701 }
702
703 conn =
704 conn
705 |> assign(:user, user)
706 |> put_req_header("content-type", "application/activity+json")
707 |> post("/users/#{user.nickname}/outbox", data)
708
709 assert json_response(conn, 400)
710 end
711
712 test "it increases like count when receiving a like action", %{conn: conn} do
713 note_activity = insert(:note_activity)
714 note_object = Object.normalize(note_activity)
715 user = User.get_cached_by_ap_id(note_activity.data["actor"])
716
717 data = %{
718 type: "Like",
719 object: %{
720 id: note_object.data["id"]
721 }
722 }
723
724 conn =
725 conn
726 |> assign(:user, user)
727 |> put_req_header("content-type", "application/activity+json")
728 |> post("/users/#{user.nickname}/outbox", data)
729
730 result = json_response(conn, 201)
731 assert Activity.get_by_ap_id(result["id"])
732
733 assert object = Object.get_by_ap_id(note_object.data["id"])
734 assert object.data["like_count"] == 1
735 end
736 end
737
738 describe "/relay/followers" do
739 test "it returns relay followers", %{conn: conn} do
740 relay_actor = Relay.get_actor()
741 user = insert(:user)
742 User.follow(user, relay_actor)
743
744 result =
745 conn
746 |> assign(:relay, true)
747 |> get("/relay/followers")
748 |> json_response(200)
749
750 assert result["first"]["orderedItems"] == [user.ap_id]
751 end
752 end
753
754 describe "/relay/following" do
755 test "it returns relay following", %{conn: conn} do
756 result =
757 conn
758 |> assign(:relay, true)
759 |> get("/relay/following")
760 |> json_response(200)
761
762 assert result["first"]["orderedItems"] == []
763 end
764 end
765
766 describe "/users/:nickname/followers" do
767 test "it returns the followers in a collection", %{conn: conn} do
768 user = insert(:user)
769 user_two = insert(:user)
770 User.follow(user, user_two)
771
772 result =
773 conn
774 |> get("/users/#{user_two.nickname}/followers")
775 |> json_response(200)
776
777 assert result["first"]["orderedItems"] == [user.ap_id]
778 end
779
780 test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
781 user = insert(:user)
782 user_two = insert(:user, hide_followers: true)
783 User.follow(user, user_two)
784
785 result =
786 conn
787 |> get("/users/#{user_two.nickname}/followers")
788 |> json_response(200)
789
790 assert is_binary(result["first"])
791 end
792
793 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
794 %{conn: conn} do
795 user = insert(:user, hide_followers: true)
796
797 result =
798 conn
799 |> get("/users/#{user.nickname}/followers?page=1")
800
801 assert result.status == 403
802 assert result.resp_body == ""
803 end
804
805 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
806 %{conn: conn} do
807 user = insert(:user, hide_followers: true)
808 other_user = insert(:user)
809 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
810
811 result =
812 conn
813 |> assign(:user, user)
814 |> get("/users/#{user.nickname}/followers?page=1")
815 |> json_response(200)
816
817 assert result["totalItems"] == 1
818 assert result["orderedItems"] == [other_user.ap_id]
819 end
820
821 test "it works for more than 10 users", %{conn: conn} do
822 user = insert(:user)
823
824 Enum.each(1..15, fn _ ->
825 other_user = insert(:user)
826 User.follow(other_user, user)
827 end)
828
829 result =
830 conn
831 |> get("/users/#{user.nickname}/followers")
832 |> json_response(200)
833
834 assert length(result["first"]["orderedItems"]) == 10
835 assert result["first"]["totalItems"] == 15
836 assert result["totalItems"] == 15
837
838 result =
839 conn
840 |> get("/users/#{user.nickname}/followers?page=2")
841 |> json_response(200)
842
843 assert length(result["orderedItems"]) == 5
844 assert result["totalItems"] == 15
845 end
846 end
847
848 describe "/users/:nickname/following" do
849 test "it returns the following in a collection", %{conn: conn} do
850 user = insert(:user)
851 user_two = insert(:user)
852 User.follow(user, user_two)
853
854 result =
855 conn
856 |> get("/users/#{user.nickname}/following")
857 |> json_response(200)
858
859 assert result["first"]["orderedItems"] == [user_two.ap_id]
860 end
861
862 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
863 user = insert(:user, hide_follows: true)
864 user_two = insert(:user)
865 User.follow(user, user_two)
866
867 result =
868 conn
869 |> get("/users/#{user.nickname}/following")
870 |> json_response(200)
871
872 assert is_binary(result["first"])
873 end
874
875 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
876 %{conn: conn} do
877 user = insert(:user, hide_follows: true)
878
879 result =
880 conn
881 |> get("/users/#{user.nickname}/following?page=1")
882
883 assert result.status == 403
884 assert result.resp_body == ""
885 end
886
887 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
888 %{conn: conn} do
889 user = insert(:user, hide_follows: true)
890 other_user = insert(:user)
891 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
892
893 result =
894 conn
895 |> assign(:user, user)
896 |> get("/users/#{user.nickname}/following?page=1")
897 |> json_response(200)
898
899 assert result["totalItems"] == 1
900 assert result["orderedItems"] == [other_user.ap_id]
901 end
902
903 test "it works for more than 10 users", %{conn: conn} do
904 user = insert(:user)
905
906 Enum.each(1..15, fn _ ->
907 user = User.get_cached_by_id(user.id)
908 other_user = insert(:user)
909 User.follow(user, other_user)
910 end)
911
912 result =
913 conn
914 |> get("/users/#{user.nickname}/following")
915 |> json_response(200)
916
917 assert length(result["first"]["orderedItems"]) == 10
918 assert result["first"]["totalItems"] == 15
919 assert result["totalItems"] == 15
920
921 result =
922 conn
923 |> get("/users/#{user.nickname}/following?page=2")
924 |> json_response(200)
925
926 assert length(result["orderedItems"]) == 5
927 assert result["totalItems"] == 15
928 end
929 end
930
931 describe "delivery tracking" do
932 test "it tracks a signed object fetch", %{conn: conn} do
933 user = insert(:user, local: false)
934 activity = insert(:note_activity)
935 object = Object.normalize(activity)
936
937 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
938
939 conn
940 |> put_req_header("accept", "application/activity+json")
941 |> assign(:user, user)
942 |> get(object_path)
943 |> json_response(200)
944
945 assert Delivery.get(object.id, user.id)
946 end
947
948 test "it tracks a signed activity fetch", %{conn: conn} do
949 user = insert(:user, local: false)
950 activity = insert(:note_activity)
951 object = Object.normalize(activity)
952
953 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
954
955 conn
956 |> put_req_header("accept", "application/activity+json")
957 |> assign(:user, user)
958 |> get(activity_path)
959 |> json_response(200)
960
961 assert Delivery.get(object.id, user.id)
962 end
963
964 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
965 user = insert(:user, local: false)
966 other_user = insert(:user, local: false)
967 activity = insert(:note_activity)
968 object = Object.normalize(activity)
969
970 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
971
972 conn
973 |> put_req_header("accept", "application/activity+json")
974 |> assign(:user, user)
975 |> get(object_path)
976 |> json_response(200)
977
978 build_conn()
979 |> put_req_header("accept", "application/activity+json")
980 |> assign(:user, other_user)
981 |> get(object_path)
982 |> json_response(200)
983
984 assert Delivery.get(object.id, user.id)
985 assert Delivery.get(object.id, other_user.id)
986 end
987
988 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
989 user = insert(:user, local: false)
990 other_user = insert(:user, local: false)
991 activity = insert(:note_activity)
992 object = Object.normalize(activity)
993
994 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
995
996 conn
997 |> put_req_header("accept", "application/activity+json")
998 |> assign(:user, user)
999 |> get(activity_path)
1000 |> json_response(200)
1001
1002 build_conn()
1003 |> put_req_header("accept", "application/activity+json")
1004 |> assign(:user, other_user)
1005 |> get(activity_path)
1006 |> json_response(200)
1007
1008 assert Delivery.get(object.id, user.id)
1009 assert Delivery.get(object.id, other_user.id)
1010 end
1011 end
1012
1013 describe "Additional ActivityPub C2S endpoints" do
1014 test "/api/ap/whoami", %{conn: conn} do
1015 user = insert(:user)
1016
1017 conn =
1018 conn
1019 |> assign(:user, user)
1020 |> get("/api/ap/whoami")
1021
1022 user = User.get_cached_by_id(user.id)
1023
1024 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1025 end
1026
1027 clear_config([:media_proxy])
1028 clear_config([Pleroma.Upload])
1029
1030 test "uploadMedia", %{conn: conn} do
1031 user = insert(:user)
1032
1033 desc = "Description of the image"
1034
1035 image = %Plug.Upload{
1036 content_type: "image/jpg",
1037 path: Path.absname("test/fixtures/image.jpg"),
1038 filename: "an_image.jpg"
1039 }
1040
1041 conn =
1042 conn
1043 |> assign(:user, user)
1044 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1045
1046 assert object = json_response(conn, :created)
1047 assert object["name"] == desc
1048 assert object["type"] == "Document"
1049 assert object["actor"] == user.ap_id
1050 end
1051 end
1052
1053 describe "when instance is not federating," do
1054 clear_config([:instance, :federating]) do
1055 Pleroma.Config.put([:instance, :federating], false)
1056 end
1057
1058 test "returns 404 for GET routes", %{conn: conn} do
1059 user = insert(:user)
1060 conn = put_req_header(conn, "accept", "application/json")
1061
1062 get_uris = [
1063 "/users/#{user.nickname}",
1064 "/internal/fetch",
1065 "/relay",
1066 "/relay/following",
1067 "/relay/followers"
1068 ]
1069
1070 for get_uri <- get_uris do
1071 conn
1072 |> get(get_uri)
1073 |> json_response(404)
1074
1075 conn
1076 |> assign(:user, user)
1077 |> get(get_uri)
1078 |> json_response(404)
1079 end
1080 end
1081
1082 test "returns 404 for activity-related POST routes", %{conn: conn} do
1083 user = insert(:user)
1084
1085 conn =
1086 conn
1087 |> assign(:valid_signature, true)
1088 |> put_req_header("content-type", "application/activity+json")
1089
1090 post_activity_data =
1091 "test/fixtures/mastodon-post-activity.json"
1092 |> File.read!()
1093 |> Poison.decode!()
1094
1095 post_activity_uris = [
1096 "/inbox",
1097 "/relay/inbox",
1098 "/users/#{user.nickname}/inbox"
1099 ]
1100
1101 for post_activity_uri <- post_activity_uris do
1102 conn
1103 |> post(post_activity_uri, post_activity_data)
1104 |> json_response(404)
1105
1106 conn
1107 |> assign(:user, user)
1108 |> post(post_activity_uri, post_activity_data)
1109 |> json_response(404)
1110 end
1111 end
1112 end
1113 end