1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
6 use Pleroma.Web.ConnCase
7 use Oban.Testing, repo: Pleroma.Repo
10 alias Pleroma.Activity
12 alias Pleroma.Delivery
13 alias Pleroma.Instances
15 alias Pleroma.Tests.ObanHelpers
17 alias Pleroma.Web.ActivityPub.ObjectView
18 alias Pleroma.Web.ActivityPub.Relay
19 alias Pleroma.Web.ActivityPub.UserView
20 alias Pleroma.Web.ActivityPub.Utils
21 alias Pleroma.Web.CommonAPI
22 alias Pleroma.Workers.ReceiverWorker
25 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
29 clear_config([:instance, :federating]) do
30 Config.put([:instance, :federating], true)
34 clear_config([:instance, :allow_relay])
36 test "with the relay active, it returns the relay user", %{conn: conn} do
39 |> get(activity_pub_path(conn, :relay))
42 assert res["id"] =~ "/relay"
45 test "with the relay disabled, it returns 404", %{conn: conn} do
46 Config.put([:instance, :allow_relay], false)
49 |> get(activity_pub_path(conn, :relay))
53 test "on non-federating instance, it returns 404", %{conn: conn} do
54 Config.put([:instance, :federating], false)
58 |> assign(:user, user)
59 |> get(activity_pub_path(conn, :relay))
64 describe "/internal/fetch" do
65 test "it returns the internal fetch user", %{conn: conn} do
68 |> get(activity_pub_path(conn, :internal_fetch))
71 assert res["id"] =~ "/fetch"
74 test "on non-federating instance, it returns 404", %{conn: conn} do
75 Config.put([:instance, :federating], false)
79 |> assign(:user, user)
80 |> get(activity_pub_path(conn, :internal_fetch))
85 describe "/users/:nickname" do
86 test "it returns a json representation of the user with accept application/json", %{
93 |> put_req_header("accept", "application/json")
94 |> get("/users/#{user.nickname}")
96 user = User.get_cached_by_id(user.id)
98 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
101 test "it returns a json representation of the user with accept application/activity+json", %{
108 |> put_req_header("accept", "application/activity+json")
109 |> get("/users/#{user.nickname}")
111 user = User.get_cached_by_id(user.id)
113 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
116 test "it returns a json representation of the user with accept application/ld+json", %{
125 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
127 |> get("/users/#{user.nickname}")
129 user = User.get_cached_by_id(user.id)
131 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
134 test "it returns 404 for remote users", %{
137 user = insert(:user, local: false, nickname: "remoteuser@example.com")
141 |> put_req_header("accept", "application/json")
142 |> get("/users/#{user.nickname}.json")
144 assert json_response(conn, 404)
147 test "it returns error when user is not found", %{conn: conn} do
150 |> put_req_header("accept", "application/json")
151 |> get("/users/jimm")
152 |> json_response(404)
154 assert response == "Not found"
157 test "it requires authentication if instance is NOT federating", %{
166 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
169 ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user)
173 describe "/objects/:uuid" do
174 test "it returns a json representation of the object with accept application/json", %{
178 uuid = String.split(note.data["id"], "/") |> List.last()
182 |> put_req_header("accept", "application/json")
183 |> get("/objects/#{uuid}")
185 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
188 test "it returns a json representation of the object with accept application/activity+json",
191 uuid = String.split(note.data["id"], "/") |> List.last()
195 |> put_req_header("accept", "application/activity+json")
196 |> get("/objects/#{uuid}")
198 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
201 test "it returns a json representation of the object with accept application/ld+json", %{
205 uuid = String.split(note.data["id"], "/") |> List.last()
211 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
213 |> get("/objects/#{uuid}")
215 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
218 test "it returns 404 for non-public messages", %{conn: conn} do
219 note = insert(:direct_note)
220 uuid = String.split(note.data["id"], "/") |> List.last()
224 |> put_req_header("accept", "application/activity+json")
225 |> get("/objects/#{uuid}")
227 assert json_response(conn, 404)
230 test "it returns 404 for tombstone objects", %{conn: conn} do
231 tombstone = insert(:tombstone)
232 uuid = String.split(tombstone.data["id"], "/") |> List.last()
236 |> put_req_header("accept", "application/activity+json")
237 |> get("/objects/#{uuid}")
239 assert json_response(conn, 404)
242 test "it caches a response", %{conn: conn} do
244 uuid = String.split(note.data["id"], "/") |> List.last()
248 |> put_req_header("accept", "application/activity+json")
249 |> get("/objects/#{uuid}")
251 assert json_response(conn1, :ok)
252 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
256 |> put_req_header("accept", "application/activity+json")
257 |> get("/objects/#{uuid}")
259 assert json_response(conn1, :ok) == json_response(conn2, :ok)
260 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
263 test "cached purged after object deletion", %{conn: conn} do
265 uuid = String.split(note.data["id"], "/") |> List.last()
269 |> put_req_header("accept", "application/activity+json")
270 |> get("/objects/#{uuid}")
272 assert json_response(conn1, :ok)
273 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
279 |> put_req_header("accept", "application/activity+json")
280 |> get("/objects/#{uuid}")
282 assert "Not found" == json_response(conn2, :not_found)
285 test "it requires authentication if instance is NOT federating", %{
290 uuid = String.split(note.data["id"], "/") |> List.last()
292 conn = put_req_header(conn, "accept", "application/activity+json")
294 ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user)
298 describe "/activities/:uuid" do
299 test "it returns a json representation of the activity", %{conn: conn} do
300 activity = insert(:note_activity)
301 uuid = String.split(activity.data["id"], "/") |> List.last()
305 |> put_req_header("accept", "application/activity+json")
306 |> get("/activities/#{uuid}")
308 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
311 test "it returns 404 for non-public activities", %{conn: conn} do
312 activity = insert(:direct_note_activity)
313 uuid = String.split(activity.data["id"], "/") |> List.last()
317 |> put_req_header("accept", "application/activity+json")
318 |> get("/activities/#{uuid}")
320 assert json_response(conn, 404)
323 test "it caches a response", %{conn: conn} do
324 activity = insert(:note_activity)
325 uuid = String.split(activity.data["id"], "/") |> List.last()
329 |> put_req_header("accept", "application/activity+json")
330 |> get("/activities/#{uuid}")
332 assert json_response(conn1, :ok)
333 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
337 |> put_req_header("accept", "application/activity+json")
338 |> get("/activities/#{uuid}")
340 assert json_response(conn1, :ok) == json_response(conn2, :ok)
341 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
344 test "cached purged after activity deletion", %{conn: conn} do
346 {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"})
348 uuid = String.split(activity.data["id"], "/") |> List.last()
352 |> put_req_header("accept", "application/activity+json")
353 |> get("/activities/#{uuid}")
355 assert json_response(conn1, :ok)
356 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
358 Activity.delete_all_by_object_ap_id(activity.object.data["id"])
362 |> put_req_header("accept", "application/activity+json")
363 |> get("/activities/#{uuid}")
365 assert "Not found" == json_response(conn2, :not_found)
368 test "it requires authentication if instance is NOT federating", %{
372 activity = insert(:note_activity)
373 uuid = String.split(activity.data["id"], "/") |> List.last()
375 conn = put_req_header(conn, "accept", "application/activity+json")
377 ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user)
382 test "it inserts an incoming activity into the database", %{conn: conn} do
383 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
387 |> assign(:valid_signature, true)
388 |> put_req_header("content-type", "application/activity+json")
389 |> post("/inbox", data)
391 assert "ok" == json_response(conn, 200)
393 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
394 assert Activity.get_by_ap_id(data["id"])
397 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
398 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
400 sender_url = data["actor"]
401 Instances.set_consistently_unreachable(sender_url)
402 refute Instances.reachable?(sender_url)
406 |> assign(:valid_signature, true)
407 |> put_req_header("content-type", "application/activity+json")
408 |> post("/inbox", data)
410 assert "ok" == json_response(conn, 200)
411 assert Instances.reachable?(sender_url)
414 test "accept follow activity", %{conn: conn} do
415 Pleroma.Config.put([:instance, :federating], true)
416 relay = Relay.get_actor()
418 assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
420 followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
421 relay = refresh_record(relay)
424 File.read!("test/fixtures/relay/accept-follow.json")
425 |> String.replace("{{ap_id}}", relay.ap_id)
426 |> String.replace("{{activity_id}}", activity.data["id"])
430 |> assign(:valid_signature, true)
431 |> put_req_header("content-type", "application/activity+json")
432 |> post("/inbox", accept)
433 |> json_response(200)
435 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
437 assert Pleroma.FollowingRelationship.following?(
442 Mix.shell(Mix.Shell.Process)
445 Mix.shell(Mix.Shell.IO)
448 :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
449 assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
452 test "without valid signature, " <>
453 "it only accepts Create activities and requires enabled federation",
455 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
456 non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
458 conn = put_req_header(conn, "content-type", "application/activity+json")
460 Config.put([:instance, :federating], false)
463 |> post("/inbox", data)
464 |> json_response(403)
467 |> post("/inbox", non_create_data)
468 |> json_response(403)
470 Config.put([:instance, :federating], true)
472 ret_conn = post(conn, "/inbox", data)
473 assert "ok" == json_response(ret_conn, 200)
476 |> post("/inbox", non_create_data)
477 |> json_response(400)
481 describe "/users/:nickname/inbox" do
484 File.read!("test/fixtures/mastodon-post-activity.json")
490 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
492 data = Map.put(data, "bcc", [user.ap_id])
496 |> assign(:valid_signature, true)
497 |> put_req_header("content-type", "application/activity+json")
498 |> post("/users/#{user.nickname}/inbox", data)
500 assert "ok" == json_response(conn, 200)
501 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
502 assert Activity.get_by_ap_id(data["id"])
505 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
509 Map.put(data, "to", user.ap_id)
514 |> assign(:valid_signature, true)
515 |> put_req_header("content-type", "application/activity+json")
516 |> post("/users/#{user.nickname}/inbox", data)
518 assert "ok" == json_response(conn, 200)
519 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
520 assert Activity.get_by_ap_id(data["id"])
523 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
527 Map.put(data, "cc", user.ap_id)
532 |> assign(:valid_signature, true)
533 |> put_req_header("content-type", "application/activity+json")
534 |> post("/users/#{user.nickname}/inbox", data)
536 assert "ok" == json_response(conn, 200)
537 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
538 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
539 assert user.ap_id in activity.recipients
542 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
546 Map.put(data, "bcc", user.ap_id)
552 |> assign(:valid_signature, true)
553 |> put_req_header("content-type", "application/activity+json")
554 |> post("/users/#{user.nickname}/inbox", data)
556 assert "ok" == json_response(conn, 200)
557 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
558 assert Activity.get_by_ap_id(data["id"])
561 test "it accepts announces with to as string instead of array", %{conn: conn} do
565 "@context" => "https://www.w3.org/ns/activitystreams",
566 "actor" => "http://mastodon.example.org/users/admin",
567 "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
568 "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
569 "to" => "https://www.w3.org/ns/activitystreams#Public",
570 "cc" => [user.ap_id],
576 |> assign(:valid_signature, true)
577 |> put_req_header("content-type", "application/activity+json")
578 |> post("/users/#{user.nickname}/inbox", data)
580 assert "ok" == json_response(conn, 200)
581 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
582 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
583 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
586 test "it accepts messages from actors that are followed by the user", %{
590 recipient = insert(:user)
591 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
593 {:ok, recipient} = User.follow(recipient, actor)
597 |> Map.put("attributedTo", actor.ap_id)
601 |> Map.put("actor", actor.ap_id)
602 |> Map.put("object", object)
606 |> assign(:valid_signature, true)
607 |> put_req_header("content-type", "application/activity+json")
608 |> post("/users/#{recipient.nickname}/inbox", data)
610 assert "ok" == json_response(conn, 200)
611 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
612 assert Activity.get_by_ap_id(data["id"])
615 test "it rejects reads from other users", %{conn: conn} do
617 other_user = insert(:user)
621 |> assign(:user, other_user)
622 |> put_req_header("accept", "application/activity+json")
623 |> get("/users/#{user.nickname}/inbox")
625 assert json_response(conn, 403)
628 test "it returns a note activity in a collection", %{conn: conn} do
629 note_activity = insert(:direct_note_activity)
630 note_object = Object.normalize(note_activity)
631 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
635 |> assign(:user, user)
636 |> put_req_header("accept", "application/activity+json")
637 |> get("/users/#{user.nickname}/inbox?page=true")
639 assert response(conn, 200) =~ note_object.data["content"]
642 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
644 data = Map.put(data, "bcc", [user.ap_id])
646 sender_host = URI.parse(data["actor"]).host
647 Instances.set_consistently_unreachable(sender_host)
648 refute Instances.reachable?(sender_host)
652 |> assign(:valid_signature, true)
653 |> put_req_header("content-type", "application/activity+json")
654 |> post("/users/#{user.nickname}/inbox", data)
656 assert "ok" == json_response(conn, 200)
657 assert Instances.reachable?(sender_host)
660 test "it removes all follower collections but actor's", %{conn: conn} do
661 [actor, recipient] = insert_pair(:user)
664 File.read!("test/fixtures/activitypub-client-post-activity.json")
667 object = Map.put(data["object"], "attributedTo", actor.ap_id)
671 |> Map.put("id", Utils.generate_object_id())
672 |> Map.put("actor", actor.ap_id)
673 |> Map.put("object", object)
675 recipient.follower_address,
676 actor.follower_address
680 recipient.follower_address,
681 "https://www.w3.org/ns/activitystreams#Public"
685 |> assign(:valid_signature, true)
686 |> put_req_header("content-type", "application/activity+json")
687 |> post("/users/#{recipient.nickname}/inbox", data)
688 |> json_response(200)
690 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
692 activity = Activity.get_by_ap_id(data["id"])
695 assert actor.follower_address in activity.recipients
696 assert actor.follower_address in activity.data["cc"]
698 refute recipient.follower_address in activity.recipients
699 refute recipient.follower_address in activity.data["cc"]
700 refute recipient.follower_address in activity.data["to"]
703 test "it requires authentication", %{conn: conn} do
705 conn = put_req_header(conn, "accept", "application/activity+json")
707 ret_conn = get(conn, "/users/#{user.nickname}/inbox")
708 assert json_response(ret_conn, 403)
712 |> assign(:user, user)
713 |> get("/users/#{user.nickname}/inbox")
715 assert json_response(ret_conn, 200)
719 describe "GET /users/:nickname/outbox" do
720 test "it returns 200 even if there're no activities", %{conn: conn} do
725 |> assign(:user, user)
726 |> put_req_header("accept", "application/activity+json")
727 |> get("/users/#{user.nickname}/outbox")
729 result = json_response(conn, 200)
730 assert user.ap_id <> "/outbox" == result["id"]
733 test "it returns a note activity in a collection", %{conn: conn} do
734 note_activity = insert(:note_activity)
735 note_object = Object.normalize(note_activity)
736 user = User.get_cached_by_ap_id(note_activity.data["actor"])
740 |> assign(:user, user)
741 |> put_req_header("accept", "application/activity+json")
742 |> get("/users/#{user.nickname}/outbox?page=true")
744 assert response(conn, 200) =~ note_object.data["content"]
747 test "it returns an announce activity in a collection", %{conn: conn} do
748 announce_activity = insert(:announce_activity)
749 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
753 |> assign(:user, user)
754 |> put_req_header("accept", "application/activity+json")
755 |> get("/users/#{user.nickname}/outbox?page=true")
757 assert response(conn, 200) =~ announce_activity.data["object"]
760 test "it requires authentication if instance is NOT federating", %{
764 conn = put_req_header(conn, "accept", "application/activity+json")
766 ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user)
770 describe "POST /users/:nickname/outbox" do
771 test "it rejects posts from other users / unauuthenticated users", %{conn: conn} do
772 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
774 other_user = insert(:user)
775 conn = put_req_header(conn, "content-type", "application/activity+json")
778 |> post("/users/#{user.nickname}/outbox", data)
779 |> json_response(403)
782 |> assign(:user, other_user)
783 |> post("/users/#{user.nickname}/outbox", data)
784 |> json_response(403)
787 test "it inserts an incoming create activity into the database", %{conn: conn} do
788 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
793 |> assign(:user, user)
794 |> put_req_header("content-type", "application/activity+json")
795 |> post("/users/#{user.nickname}/outbox", data)
797 result = json_response(conn, 201)
799 assert Activity.get_by_ap_id(result["id"])
802 test "it rejects an incoming activity with bogus type", %{conn: conn} do
803 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
808 |> Map.put("type", "BadType")
812 |> assign(:user, user)
813 |> put_req_header("content-type", "application/activity+json")
814 |> post("/users/#{user.nickname}/outbox", data)
816 assert json_response(conn, 400)
819 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
820 note_activity = insert(:note_activity)
821 note_object = Object.normalize(note_activity)
822 user = User.get_cached_by_ap_id(note_activity.data["actor"])
827 id: note_object.data["id"]
833 |> assign(:user, user)
834 |> put_req_header("content-type", "application/activity+json")
835 |> post("/users/#{user.nickname}/outbox", data)
837 result = json_response(conn, 201)
838 assert Activity.get_by_ap_id(result["id"])
840 assert object = Object.get_by_ap_id(note_object.data["id"])
841 assert object.data["type"] == "Tombstone"
844 test "it rejects delete activity of object from other actor", %{conn: conn} do
845 note_activity = insert(:note_activity)
846 note_object = Object.normalize(note_activity)
852 id: note_object.data["id"]
858 |> assign(:user, user)
859 |> put_req_header("content-type", "application/activity+json")
860 |> post("/users/#{user.nickname}/outbox", data)
862 assert json_response(conn, 400)
865 test "it increases like count when receiving a like action", %{conn: conn} do
866 note_activity = insert(:note_activity)
867 note_object = Object.normalize(note_activity)
868 user = User.get_cached_by_ap_id(note_activity.data["actor"])
873 id: note_object.data["id"]
879 |> assign(:user, user)
880 |> put_req_header("content-type", "application/activity+json")
881 |> post("/users/#{user.nickname}/outbox", data)
883 result = json_response(conn, 201)
884 assert Activity.get_by_ap_id(result["id"])
886 assert object = Object.get_by_ap_id(note_object.data["id"])
887 assert object.data["like_count"] == 1
891 describe "/relay/followers" do
892 test "it returns relay followers", %{conn: conn} do
893 relay_actor = Relay.get_actor()
895 User.follow(user, relay_actor)
899 |> get("/relay/followers")
900 |> json_response(200)
902 assert result["first"]["orderedItems"] == [user.ap_id]
905 test "on non-federating instance, it returns 404", %{conn: conn} do
906 Config.put([:instance, :federating], false)
910 |> assign(:user, user)
911 |> get("/relay/followers")
912 |> json_response(404)
916 describe "/relay/following" do
917 test "it returns relay following", %{conn: conn} do
920 |> get("/relay/following")
921 |> json_response(200)
923 assert result["first"]["orderedItems"] == []
926 test "on non-federating instance, it returns 404", %{conn: conn} do
927 Config.put([:instance, :federating], false)
931 |> assign(:user, user)
932 |> get("/relay/following")
933 |> json_response(404)
937 describe "/users/:nickname/followers" do
938 test "it returns the followers in a collection", %{conn: conn} do
940 user_two = insert(:user)
941 User.follow(user, user_two)
945 |> assign(:user, user_two)
946 |> get("/users/#{user_two.nickname}/followers")
947 |> json_response(200)
949 assert result["first"]["orderedItems"] == [user.ap_id]
952 test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
954 user_two = insert(:user, hide_followers: true)
955 User.follow(user, user_two)
959 |> assign(:user, user)
960 |> get("/users/#{user_two.nickname}/followers")
961 |> json_response(200)
963 assert is_binary(result["first"])
966 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
969 other_user = insert(:user, hide_followers: true)
973 |> assign(:user, user)
974 |> get("/users/#{other_user.nickname}/followers?page=1")
976 assert result.status == 403
977 assert result.resp_body == ""
980 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
982 user = insert(:user, hide_followers: true)
983 other_user = insert(:user)
984 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
988 |> assign(:user, user)
989 |> get("/users/#{user.nickname}/followers?page=1")
990 |> json_response(200)
992 assert result["totalItems"] == 1
993 assert result["orderedItems"] == [other_user.ap_id]
996 test "it works for more than 10 users", %{conn: conn} do
999 Enum.each(1..15, fn _ ->
1000 other_user = insert(:user)
1001 User.follow(other_user, user)
1006 |> assign(:user, user)
1007 |> get("/users/#{user.nickname}/followers")
1008 |> json_response(200)
1010 assert length(result["first"]["orderedItems"]) == 10
1011 assert result["first"]["totalItems"] == 15
1012 assert result["totalItems"] == 15
1016 |> assign(:user, user)
1017 |> get("/users/#{user.nickname}/followers?page=2")
1018 |> json_response(200)
1020 assert length(result["orderedItems"]) == 5
1021 assert result["totalItems"] == 15
1024 test "returns 403 if requester is not logged in", %{conn: conn} do
1025 user = insert(:user)
1028 |> get("/users/#{user.nickname}/followers")
1029 |> json_response(403)
1033 describe "/users/:nickname/following" do
1034 test "it returns the following in a collection", %{conn: conn} do
1035 user = insert(:user)
1036 user_two = insert(:user)
1037 User.follow(user, user_two)
1041 |> assign(:user, user)
1042 |> get("/users/#{user.nickname}/following")
1043 |> json_response(200)
1045 assert result["first"]["orderedItems"] == [user_two.ap_id]
1048 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
1049 user = insert(:user)
1050 user_two = insert(:user, hide_follows: true)
1051 User.follow(user, user_two)
1055 |> assign(:user, user)
1056 |> get("/users/#{user_two.nickname}/following")
1057 |> json_response(200)
1059 assert is_binary(result["first"])
1062 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
1064 user = insert(:user)
1065 user_two = insert(:user, hide_follows: true)
1069 |> assign(:user, user)
1070 |> get("/users/#{user_two.nickname}/following?page=1")
1072 assert result.status == 403
1073 assert result.resp_body == ""
1076 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
1078 user = insert(:user, hide_follows: true)
1079 other_user = insert(:user)
1080 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
1084 |> assign(:user, user)
1085 |> get("/users/#{user.nickname}/following?page=1")
1086 |> json_response(200)
1088 assert result["totalItems"] == 1
1089 assert result["orderedItems"] == [other_user.ap_id]
1092 test "it works for more than 10 users", %{conn: conn} do
1093 user = insert(:user)
1095 Enum.each(1..15, fn _ ->
1096 user = User.get_cached_by_id(user.id)
1097 other_user = insert(:user)
1098 User.follow(user, other_user)
1103 |> assign(:user, user)
1104 |> get("/users/#{user.nickname}/following")
1105 |> json_response(200)
1107 assert length(result["first"]["orderedItems"]) == 10
1108 assert result["first"]["totalItems"] == 15
1109 assert result["totalItems"] == 15
1113 |> assign(:user, user)
1114 |> get("/users/#{user.nickname}/following?page=2")
1115 |> json_response(200)
1117 assert length(result["orderedItems"]) == 5
1118 assert result["totalItems"] == 15
1121 test "returns 403 if requester is not logged in", %{conn: conn} do
1122 user = insert(:user)
1125 |> get("/users/#{user.nickname}/following")
1126 |> json_response(403)
1130 describe "delivery tracking" do
1131 test "it tracks a signed object fetch", %{conn: conn} do
1132 user = insert(:user, local: false)
1133 activity = insert(:note_activity)
1134 object = Object.normalize(activity)
1136 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1139 |> put_req_header("accept", "application/activity+json")
1140 |> assign(:user, user)
1142 |> json_response(200)
1144 assert Delivery.get(object.id, user.id)
1147 test "it tracks a signed activity fetch", %{conn: conn} do
1148 user = insert(:user, local: false)
1149 activity = insert(:note_activity)
1150 object = Object.normalize(activity)
1152 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1155 |> put_req_header("accept", "application/activity+json")
1156 |> assign(:user, user)
1157 |> get(activity_path)
1158 |> json_response(200)
1160 assert Delivery.get(object.id, user.id)
1163 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1164 user = insert(:user, local: false)
1165 other_user = insert(:user, local: false)
1166 activity = insert(:note_activity)
1167 object = Object.normalize(activity)
1169 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1172 |> put_req_header("accept", "application/activity+json")
1173 |> assign(:user, user)
1175 |> json_response(200)
1178 |> put_req_header("accept", "application/activity+json")
1179 |> assign(:user, other_user)
1181 |> json_response(200)
1183 assert Delivery.get(object.id, user.id)
1184 assert Delivery.get(object.id, other_user.id)
1187 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1188 user = insert(:user, local: false)
1189 other_user = insert(:user, local: false)
1190 activity = insert(:note_activity)
1191 object = Object.normalize(activity)
1193 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1196 |> put_req_header("accept", "application/activity+json")
1197 |> assign(:user, user)
1198 |> get(activity_path)
1199 |> json_response(200)
1202 |> put_req_header("accept", "application/activity+json")
1203 |> assign(:user, other_user)
1204 |> get(activity_path)
1205 |> json_response(200)
1207 assert Delivery.get(object.id, user.id)
1208 assert Delivery.get(object.id, other_user.id)
1212 describe "Additional ActivityPub C2S endpoints" do
1213 test "GET /api/ap/whoami", %{conn: conn} do
1214 user = insert(:user)
1218 |> assign(:user, user)
1219 |> get("/api/ap/whoami")
1221 user = User.get_cached_by_id(user.id)
1223 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1226 |> get("/api/ap/whoami")
1227 |> json_response(403)
1230 clear_config([:media_proxy])
1231 clear_config([Pleroma.Upload])
1233 test "POST /api/ap/upload_media", %{conn: conn} do
1234 user = insert(:user)
1236 desc = "Description of the image"
1238 image = %Plug.Upload{
1239 content_type: "image/jpg",
1240 path: Path.absname("test/fixtures/image.jpg"),
1241 filename: "an_image.jpg"
1246 |> assign(:user, user)
1247 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1249 assert object = json_response(conn, :created)
1250 assert object["name"] == desc
1251 assert object["type"] == "Document"
1252 assert object["actor"] == user.ap_id
1255 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1256 |> json_response(403)