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
11 alias Pleroma.Delivery
12 alias Pleroma.Instances
14 alias Pleroma.Tests.ObanHelpers
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
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
28 clear_config_all([:instance, :federating],
29 do: Pleroma.Config.put([:instance, :federating], true)
33 clear_config([:instance, :allow_relay])
35 test "with the relay active, it returns the relay user", %{conn: conn} do
38 |> get(activity_pub_path(conn, :relay))
41 assert res["id"] =~ "/relay"
44 test "with the relay disabled, it returns 404", %{conn: conn} do
45 Pleroma.Config.put([:instance, :allow_relay], false)
48 |> get(activity_pub_path(conn, :relay))
54 describe "/internal/fetch" do
55 test "it returns the internal fetch user", %{conn: conn} do
58 |> get(activity_pub_path(conn, :internal_fetch))
61 assert res["id"] =~ "/fetch"
65 describe "/users/:nickname" do
66 test "it returns a json representation of the user with accept application/json", %{
73 |> put_req_header("accept", "application/json")
74 |> get("/users/#{user.nickname}")
76 user = User.get_cached_by_id(user.id)
78 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
81 test "it returns a json representation of the user with accept application/activity+json", %{
88 |> put_req_header("accept", "application/activity+json")
89 |> get("/users/#{user.nickname}")
91 user = User.get_cached_by_id(user.id)
93 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
96 test "it returns a json representation of the user with accept application/ld+json", %{
105 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
107 |> get("/users/#{user.nickname}")
109 user = User.get_cached_by_id(user.id)
111 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
114 test "it returns 404 for remote users", %{
117 user = insert(:user, local: false, nickname: "remoteuser@example.com")
121 |> put_req_header("accept", "application/json")
122 |> get("/users/#{user.nickname}.json")
124 assert json_response(conn, 404)
128 describe "/object/:uuid" do
129 test "it returns a json representation of the object with accept application/json", %{
133 uuid = String.split(note.data["id"], "/") |> List.last()
137 |> put_req_header("accept", "application/json")
138 |> get("/objects/#{uuid}")
140 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
143 test "it returns a json representation of the object with accept application/activity+json",
146 uuid = String.split(note.data["id"], "/") |> List.last()
150 |> put_req_header("accept", "application/activity+json")
151 |> get("/objects/#{uuid}")
153 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
156 test "it returns a json representation of the object with accept application/ld+json", %{
160 uuid = String.split(note.data["id"], "/") |> List.last()
166 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
168 |> get("/objects/#{uuid}")
170 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
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()
179 |> put_req_header("accept", "application/activity+json")
180 |> get("/objects/#{uuid}")
182 assert json_response(conn, 404)
185 test "it returns 404 for tombstone objects", %{conn: conn} do
186 tombstone = insert(:tombstone)
187 uuid = String.split(tombstone.data["id"], "/") |> List.last()
191 |> put_req_header("accept", "application/activity+json")
192 |> get("/objects/#{uuid}")
194 assert json_response(conn, 404)
197 test "it caches a response", %{conn: conn} do
199 uuid = String.split(note.data["id"], "/") |> List.last()
203 |> put_req_header("accept", "application/activity+json")
204 |> get("/objects/#{uuid}")
206 assert json_response(conn1, :ok)
207 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
211 |> put_req_header("accept", "application/activity+json")
212 |> get("/objects/#{uuid}")
214 assert json_response(conn1, :ok) == json_response(conn2, :ok)
215 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
218 test "cached purged after object deletion", %{conn: conn} do
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(conn1, :ok)
228 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
234 |> put_req_header("accept", "application/activity+json")
235 |> get("/objects/#{uuid}")
237 assert "Not found" == json_response(conn2, :not_found)
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()
248 |> put_req_header("accept", "application/activity+json")
249 |> get("/activities/#{uuid}")
251 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
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()
260 |> put_req_header("accept", "application/activity+json")
261 |> get("/activities/#{uuid}")
263 assert json_response(conn, 404)
266 test "it caches a response", %{conn: conn} do
267 activity = insert(:note_activity)
268 uuid = String.split(activity.data["id"], "/") |> List.last()
272 |> put_req_header("accept", "application/activity+json")
273 |> get("/activities/#{uuid}")
275 assert json_response(conn1, :ok)
276 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
280 |> put_req_header("accept", "application/activity+json")
281 |> get("/activities/#{uuid}")
283 assert json_response(conn1, :ok) == json_response(conn2, :ok)
284 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
287 test "cached purged after activity deletion", %{conn: conn} do
289 {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"})
291 uuid = String.split(activity.data["id"], "/") |> List.last()
295 |> put_req_header("accept", "application/activity+json")
296 |> get("/activities/#{uuid}")
298 assert json_response(conn1, :ok)
299 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
301 Activity.delete_all_by_object_ap_id(activity.object.data["id"])
305 |> put_req_header("accept", "application/activity+json")
306 |> get("/activities/#{uuid}")
308 assert "Not found" == json_response(conn2, :not_found)
313 clear_config([:instance, :user_bio_length])
315 test "it inserts an incoming activity into the database", %{conn: conn} do
316 Pleroma.Config.put([:instance, :user_bio_length], 1)
318 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
322 |> assign(:valid_signature, true)
323 |> put_req_header("content-type", "application/activity+json")
324 |> post("/inbox", data)
326 assert "ok" == json_response(conn, 200)
328 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
329 assert Activity.get_by_ap_id(data["id"])
332 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
333 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
335 sender_url = data["actor"]
336 Instances.set_consistently_unreachable(sender_url)
337 refute Instances.reachable?(sender_url)
341 |> assign(:valid_signature, true)
342 |> put_req_header("content-type", "application/activity+json")
343 |> post("/inbox", data)
345 assert "ok" == json_response(conn, 200)
346 assert Instances.reachable?(sender_url)
349 test "accept follow activity", %{conn: conn} do
350 Pleroma.Config.put([:instance, :federating], true)
351 relay = Relay.get_actor()
353 assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
355 followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
356 relay = refresh_record(relay)
359 File.read!("test/fixtures/relay/accept-follow.json")
360 |> String.replace("{{ap_id}}", relay.ap_id)
361 |> String.replace("{{activity_id}}", activity.data["id"])
365 |> assign(:valid_signature, true)
366 |> put_req_header("content-type", "application/activity+json")
367 |> post("/inbox", accept)
368 |> json_response(200)
370 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
372 assert Pleroma.FollowingRelationship.following?(
377 Mix.shell(Mix.Shell.Process)
380 Mix.shell(Mix.Shell.IO)
383 :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
384 assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
388 describe "/users/:nickname/inbox" do
391 File.read!("test/fixtures/mastodon-post-activity.json")
397 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
399 data = Map.put(data, "bcc", [user.ap_id])
403 |> assign(:valid_signature, true)
404 |> put_req_header("content-type", "application/activity+json")
405 |> post("/users/#{user.nickname}/inbox", data)
407 assert "ok" == json_response(conn, 200)
408 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
409 assert Activity.get_by_ap_id(data["id"])
412 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
416 Map.put(data, "to", user.ap_id)
421 |> assign(:valid_signature, true)
422 |> put_req_header("content-type", "application/activity+json")
423 |> post("/users/#{user.nickname}/inbox", data)
425 assert "ok" == json_response(conn, 200)
426 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
427 assert Activity.get_by_ap_id(data["id"])
430 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
434 Map.put(data, "cc", user.ap_id)
439 |> assign(:valid_signature, true)
440 |> put_req_header("content-type", "application/activity+json")
441 |> post("/users/#{user.nickname}/inbox", data)
443 assert "ok" == json_response(conn, 200)
444 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
445 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
446 assert user.ap_id in activity.recipients
449 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
453 Map.put(data, "bcc", user.ap_id)
459 |> assign(:valid_signature, true)
460 |> put_req_header("content-type", "application/activity+json")
461 |> post("/users/#{user.nickname}/inbox", data)
463 assert "ok" == json_response(conn, 200)
464 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
465 assert Activity.get_by_ap_id(data["id"])
468 test "it accepts announces with to as string instead of array", %{conn: conn} do
472 "@context" => "https://www.w3.org/ns/activitystreams",
473 "actor" => "http://mastodon.example.org/users/admin",
474 "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
475 "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
476 "to" => "https://www.w3.org/ns/activitystreams#Public",
477 "cc" => [user.ap_id],
483 |> assign(:valid_signature, true)
484 |> put_req_header("content-type", "application/activity+json")
485 |> post("/users/#{user.nickname}/inbox", data)
487 assert "ok" == json_response(conn, 200)
488 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
489 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
490 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
493 test "it accepts messages from actors that are followed by the user", %{
497 recipient = insert(:user)
498 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
500 {:ok, recipient} = User.follow(recipient, actor)
504 |> Map.put("attributedTo", actor.ap_id)
508 |> Map.put("actor", actor.ap_id)
509 |> Map.put("object", object)
513 |> assign(:valid_signature, true)
514 |> put_req_header("content-type", "application/activity+json")
515 |> post("/users/#{recipient.nickname}/inbox", data)
517 assert "ok" == json_response(conn, 200)
518 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
519 assert Activity.get_by_ap_id(data["id"])
522 test "it rejects reads from other users", %{conn: conn} do
524 otheruser = insert(:user)
528 |> assign(:user, otheruser)
529 |> put_req_header("accept", "application/activity+json")
530 |> get("/users/#{user.nickname}/inbox")
532 assert json_response(conn, 403)
535 test "it doesn't crash without an authenticated user", %{conn: conn} do
540 |> put_req_header("accept", "application/activity+json")
541 |> get("/users/#{user.nickname}/inbox")
543 assert json_response(conn, 403)
546 test "it returns a note activity in a collection", %{conn: conn} do
547 note_activity = insert(:direct_note_activity)
548 note_object = Object.normalize(note_activity)
549 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
553 |> assign(:user, user)
554 |> put_req_header("accept", "application/activity+json")
555 |> get("/users/#{user.nickname}/inbox?page=true")
557 assert response(conn, 200) =~ note_object.data["content"]
560 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
562 data = Map.put(data, "bcc", [user.ap_id])
564 sender_host = URI.parse(data["actor"]).host
565 Instances.set_consistently_unreachable(sender_host)
566 refute Instances.reachable?(sender_host)
570 |> assign(:valid_signature, true)
571 |> put_req_header("content-type", "application/activity+json")
572 |> post("/users/#{user.nickname}/inbox", data)
574 assert "ok" == json_response(conn, 200)
575 assert Instances.reachable?(sender_host)
578 test "it removes all follower collections but actor's", %{conn: conn} do
579 [actor, recipient] = insert_pair(:user)
582 File.read!("test/fixtures/activitypub-client-post-activity.json")
585 object = Map.put(data["object"], "attributedTo", actor.ap_id)
589 |> Map.put("id", Utils.generate_object_id())
590 |> Map.put("actor", actor.ap_id)
591 |> Map.put("object", object)
593 recipient.follower_address,
594 actor.follower_address
598 recipient.follower_address,
599 "https://www.w3.org/ns/activitystreams#Public"
603 |> assign(:valid_signature, true)
604 |> put_req_header("content-type", "application/activity+json")
605 |> post("/users/#{recipient.nickname}/inbox", data)
606 |> json_response(200)
608 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
610 activity = Activity.get_by_ap_id(data["id"])
613 assert actor.follower_address in activity.recipients
614 assert actor.follower_address in activity.data["cc"]
616 refute recipient.follower_address in activity.recipients
617 refute recipient.follower_address in activity.data["cc"]
618 refute recipient.follower_address in activity.data["to"]
622 describe "GET /users/:nickname/outbox" do
623 test "it paginates correctly", %{conn: conn} do
625 conn = assign(conn, :user, user)
626 outbox_endpoint = user.ap_id <> "/outbox"
630 {:ok, activity} = CommonAPI.post(user, %{"status" => "post #{i}"})
636 |> put_req_header("accept", "application/activity+json")
637 |> get(outbox_endpoint <> "?page=true")
638 |> json_response(200)
640 result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
641 assert length(result["orderedItems"]) == 20
642 assert length(result_ids) == 20
643 assert result["next"]
644 assert String.starts_with?(result["next"], outbox_endpoint)
648 |> put_req_header("accept", "application/activity+json")
649 |> get(result["next"])
650 |> json_response(200)
652 result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
653 assert length(result_next["orderedItems"]) == 6
654 assert length(result_next_ids) == 6
655 refute Enum.find(result_next_ids, fn x -> x in result_ids end)
656 refute Enum.find(result_ids, fn x -> x in result_next_ids end)
657 assert String.starts_with?(result["id"], outbox_endpoint)
661 |> put_req_header("accept", "application/activity+json")
662 |> get(result_next["id"])
663 |> json_response(200)
665 assert result_next == result_next_again
668 test "it returns 200 even if there're no activities", %{conn: conn} do
670 outbox_endpoint = user.ap_id <> "/outbox"
674 |> put_req_header("accept", "application/activity+json")
675 |> get(outbox_endpoint)
677 result = json_response(conn, 200)
678 assert outbox_endpoint == result["id"]
681 test "it returns a note activity in a collection", %{conn: conn} do
682 note_activity = insert(:note_activity)
683 note_object = Object.normalize(note_activity)
684 user = User.get_cached_by_ap_id(note_activity.data["actor"])
688 |> put_req_header("accept", "application/activity+json")
689 |> get("/users/#{user.nickname}/outbox?page=true")
691 assert response(conn, 200) =~ note_object.data["content"]
694 test "it returns an announce activity in a collection", %{conn: conn} do
695 announce_activity = insert(:announce_activity)
696 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
700 |> put_req_header("accept", "application/activity+json")
701 |> get("/users/#{user.nickname}/outbox?page=true")
703 assert response(conn, 200) =~ announce_activity.data["object"]
707 describe "POST /users/:nickname/outbox (C2S)" do
711 "@context" => "https://www.w3.org/ns/activitystreams",
713 "object" => %{"type" => "Note", "content" => "AP C2S test"},
714 "to" => "https://www.w3.org/ns/activitystreams#Public",
720 test "it rejects posts from other users / unauthenticated users", %{
725 otheruser = insert(:user)
729 |> assign(:user, otheruser)
730 |> put_req_header("content-type", "application/activity+json")
731 |> post("/users/#{user.nickname}/outbox", activity)
733 assert json_response(conn, 403)
736 test "it inserts an incoming create activity into the database", %{
744 |> assign(:user, user)
745 |> put_req_header("content-type", "application/activity+json")
746 |> post("/users/#{user.nickname}/outbox", activity)
747 |> json_response(201)
749 assert Activity.get_by_ap_id(result["id"])
750 assert result["object"]
751 assert %Object{data: object} = Object.normalize(result["object"])
752 assert object["content"] == activity["object"]["content"]
755 test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
760 |> put_in(["object", "type"], "Benis")
764 |> assign(:user, user)
765 |> put_req_header("content-type", "application/activity+json")
766 |> post("/users/#{user.nickname}/outbox", activity)
767 |> json_response(400)
770 test "it inserts an incoming sensitive activity into the database", %{
775 object = Map.put(activity["object"], "sensitive", true)
776 activity = Map.put(activity, "object", object)
780 |> assign(:user, user)
781 |> put_req_header("content-type", "application/activity+json")
782 |> post("/users/#{user.nickname}/outbox", activity)
783 |> json_response(201)
785 assert Activity.get_by_ap_id(result["id"])
786 assert result["object"]
787 assert %Object{data: object} = Object.normalize(result["object"])
788 assert object["sensitive"] == activity["object"]["sensitive"]
789 assert object["content"] == activity["object"]["content"]
792 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
794 activity = Map.put(activity, "type", "BadType")
798 |> assign(:user, user)
799 |> put_req_header("content-type", "application/activity+json")
800 |> post("/users/#{user.nickname}/outbox", activity)
802 assert json_response(conn, 400)
805 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
806 note_activity = insert(:note_activity)
807 note_object = Object.normalize(note_activity)
808 user = User.get_cached_by_ap_id(note_activity.data["actor"])
813 id: note_object.data["id"]
819 |> assign(:user, user)
820 |> put_req_header("content-type", "application/activity+json")
821 |> post("/users/#{user.nickname}/outbox", data)
823 result = json_response(conn, 201)
824 assert Activity.get_by_ap_id(result["id"])
826 assert object = Object.get_by_ap_id(note_object.data["id"])
827 assert object.data["type"] == "Tombstone"
830 test "it rejects delete activity of object from other actor", %{conn: conn} do
831 note_activity = insert(:note_activity)
832 note_object = Object.normalize(note_activity)
838 id: note_object.data["id"]
844 |> assign(:user, user)
845 |> put_req_header("content-type", "application/activity+json")
846 |> post("/users/#{user.nickname}/outbox", data)
848 assert json_response(conn, 400)
851 test "it increases like count when receiving a like action", %{conn: conn} do
852 note_activity = insert(:note_activity)
853 note_object = Object.normalize(note_activity)
854 user = User.get_cached_by_ap_id(note_activity.data["actor"])
859 id: note_object.data["id"]
865 |> assign(:user, user)
866 |> put_req_header("content-type", "application/activity+json")
867 |> post("/users/#{user.nickname}/outbox", data)
869 result = json_response(conn, 201)
870 assert Activity.get_by_ap_id(result["id"])
872 assert object = Object.get_by_ap_id(note_object.data["id"])
873 assert object.data["like_count"] == 1
877 describe "/relay/followers" do
878 test "it returns relay followers", %{conn: conn} do
879 relay_actor = Relay.get_actor()
881 User.follow(user, relay_actor)
885 |> assign(:relay, true)
886 |> get("/relay/followers")
887 |> json_response(200)
889 assert result["first"]["orderedItems"] == [user.ap_id]
893 describe "/relay/following" do
894 test "it returns relay following", %{conn: conn} do
897 |> assign(:relay, true)
898 |> get("/relay/following")
899 |> json_response(200)
901 assert result["first"]["orderedItems"] == []
905 describe "/users/:nickname/followers" do
906 test "it returns the followers in a collection", %{conn: conn} do
908 user_two = insert(:user)
909 User.follow(user, user_two)
913 |> get("/users/#{user_two.nickname}/followers")
914 |> json_response(200)
916 assert result["first"]["orderedItems"] == [user.ap_id]
919 test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
921 user_two = insert(:user, hide_followers: true)
922 User.follow(user, user_two)
926 |> get("/users/#{user_two.nickname}/followers")
927 |> json_response(200)
929 assert is_binary(result["first"])
932 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
934 user = insert(:user, hide_followers: true)
938 |> get("/users/#{user.nickname}/followers?page=1")
940 assert result.status == 403
941 assert result.resp_body == ""
944 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
946 user = insert(:user, hide_followers: true)
947 other_user = insert(:user)
948 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
952 |> assign(:user, user)
953 |> get("/users/#{user.nickname}/followers?page=1")
954 |> json_response(200)
956 assert result["totalItems"] == 1
957 assert result["orderedItems"] == [other_user.ap_id]
960 test "it works for more than 10 users", %{conn: conn} do
963 Enum.each(1..15, fn _ ->
964 other_user = insert(:user)
965 User.follow(other_user, user)
970 |> get("/users/#{user.nickname}/followers")
971 |> json_response(200)
973 assert length(result["first"]["orderedItems"]) == 10
974 assert result["first"]["totalItems"] == 15
975 assert result["totalItems"] == 15
979 |> get("/users/#{user.nickname}/followers?page=2")
980 |> json_response(200)
982 assert length(result["orderedItems"]) == 5
983 assert result["totalItems"] == 15
987 describe "/users/:nickname/following" do
988 test "it returns the following in a collection", %{conn: conn} do
990 user_two = insert(:user)
991 User.follow(user, user_two)
995 |> get("/users/#{user.nickname}/following")
996 |> json_response(200)
998 assert result["first"]["orderedItems"] == [user_two.ap_id]
1001 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
1002 user = insert(:user, hide_follows: true)
1003 user_two = insert(:user)
1004 User.follow(user, user_two)
1008 |> get("/users/#{user.nickname}/following")
1009 |> json_response(200)
1011 assert is_binary(result["first"])
1014 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
1016 user = insert(:user, hide_follows: true)
1020 |> get("/users/#{user.nickname}/following?page=1")
1022 assert result.status == 403
1023 assert result.resp_body == ""
1026 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
1028 user = insert(:user, hide_follows: true)
1029 other_user = insert(:user)
1030 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
1034 |> assign(:user, user)
1035 |> get("/users/#{user.nickname}/following?page=1")
1036 |> json_response(200)
1038 assert result["totalItems"] == 1
1039 assert result["orderedItems"] == [other_user.ap_id]
1042 test "it works for more than 10 users", %{conn: conn} do
1043 user = insert(:user)
1045 Enum.each(1..15, fn _ ->
1046 user = User.get_cached_by_id(user.id)
1047 other_user = insert(:user)
1048 User.follow(user, other_user)
1053 |> get("/users/#{user.nickname}/following")
1054 |> json_response(200)
1056 assert length(result["first"]["orderedItems"]) == 10
1057 assert result["first"]["totalItems"] == 15
1058 assert result["totalItems"] == 15
1062 |> get("/users/#{user.nickname}/following?page=2")
1063 |> json_response(200)
1065 assert length(result["orderedItems"]) == 5
1066 assert result["totalItems"] == 15
1070 describe "delivery tracking" do
1071 test "it tracks a signed object fetch", %{conn: conn} do
1072 user = insert(:user, local: false)
1073 activity = insert(:note_activity)
1074 object = Object.normalize(activity)
1076 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1079 |> put_req_header("accept", "application/activity+json")
1080 |> assign(:user, user)
1082 |> json_response(200)
1084 assert Delivery.get(object.id, user.id)
1087 test "it tracks a signed activity fetch", %{conn: conn} do
1088 user = insert(:user, local: false)
1089 activity = insert(:note_activity)
1090 object = Object.normalize(activity)
1092 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1095 |> put_req_header("accept", "application/activity+json")
1096 |> assign(:user, user)
1097 |> get(activity_path)
1098 |> json_response(200)
1100 assert Delivery.get(object.id, user.id)
1103 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1104 user = insert(:user, local: false)
1105 other_user = insert(:user, local: false)
1106 activity = insert(:note_activity)
1107 object = Object.normalize(activity)
1109 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1112 |> put_req_header("accept", "application/activity+json")
1113 |> assign(:user, user)
1115 |> json_response(200)
1118 |> put_req_header("accept", "application/activity+json")
1119 |> assign(:user, other_user)
1121 |> json_response(200)
1123 assert Delivery.get(object.id, user.id)
1124 assert Delivery.get(object.id, other_user.id)
1127 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1128 user = insert(:user, local: false)
1129 other_user = insert(:user, local: false)
1130 activity = insert(:note_activity)
1131 object = Object.normalize(activity)
1133 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1136 |> put_req_header("accept", "application/activity+json")
1137 |> assign(:user, user)
1138 |> get(activity_path)
1139 |> json_response(200)
1142 |> put_req_header("accept", "application/activity+json")
1143 |> assign(:user, other_user)
1144 |> get(activity_path)
1145 |> json_response(200)
1147 assert Delivery.get(object.id, user.id)
1148 assert Delivery.get(object.id, other_user.id)
1152 describe "Additionnal ActivityPub C2S endpoints" do
1153 test "/api/ap/whoami", %{conn: conn} do
1154 user = insert(:user)
1158 |> assign(:user, user)
1159 |> get("/api/ap/whoami")
1161 user = User.get_cached_by_id(user.id)
1163 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1166 clear_config([:media_proxy])
1167 clear_config([Pleroma.Upload])
1169 test "uploadMedia", %{conn: conn} do
1170 user = insert(:user)
1172 desc = "Description of the image"
1174 image = %Plug.Upload{
1175 content_type: "image/jpg",
1176 path: Path.absname("test/fixtures/image.jpg"),
1177 filename: "an_image.jpg"
1182 |> assign(:user, user)
1183 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1185 assert object = json_response(conn, :created)
1186 assert object["name"] == desc
1187 assert object["type"] == "Document"
1188 assert object["actor"] == user.ap_id