1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 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.Delivery
11 alias Pleroma.Instances
13 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.Web.ActivityPub.ActivityPub
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.Web.Endpoint
22 alias Pleroma.Workers.ReceiverWorker
24 import Pleroma.Factory
26 require Pleroma.Constants
29 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
33 setup do: clear_config([:instance, :federating], true)
36 setup do: clear_config([:instance, :allow_relay])
38 test "with the relay active, it returns the relay user", %{conn: conn} do
41 |> get(activity_pub_path(conn, :relay))
44 assert res["id"] =~ "/relay"
47 test "with the relay disabled, it returns 404", %{conn: conn} do
48 clear_config([:instance, :allow_relay], false)
51 |> get(activity_pub_path(conn, :relay))
55 test "on non-federating instance, it returns 404", %{conn: conn} do
56 clear_config([:instance, :federating], false)
60 |> assign(:user, user)
61 |> get(activity_pub_path(conn, :relay))
66 describe "/internal/fetch" do
67 test "it returns the internal fetch user", %{conn: conn} do
70 |> get(activity_pub_path(conn, :internal_fetch))
73 assert res["id"] =~ "/fetch"
76 test "on non-federating instance, it returns 404", %{conn: conn} do
77 clear_config([:instance, :federating], false)
81 |> assign(:user, user)
82 |> get(activity_pub_path(conn, :internal_fetch))
87 describe "/users/:nickname" do
88 test "it returns a json representation of the user with accept application/json", %{
95 |> put_req_header("accept", "application/json")
96 |> get("/users/#{user.nickname}")
98 user = User.get_cached_by_id(user.id)
100 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
103 test "it returns a json representation of the user with accept application/activity+json", %{
110 |> put_req_header("accept", "application/activity+json")
111 |> get("/users/#{user.nickname}")
113 user = User.get_cached_by_id(user.id)
115 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
118 test "it returns a json representation of the user with accept application/ld+json", %{
127 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
129 |> get("/users/#{user.nickname}")
131 user = User.get_cached_by_id(user.id)
133 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
136 test "it returns 404 for remote users", %{
139 user = insert(:user, local: false, nickname: "remoteuser@example.com")
143 |> put_req_header("accept", "application/json")
144 |> get("/users/#{user.nickname}.json")
146 assert json_response(conn, 404)
149 test "it returns error when user is not found", %{conn: conn} do
152 |> put_req_header("accept", "application/json")
153 |> get("/users/jimm")
154 |> json_response(404)
156 assert response == "Not found"
160 describe "mastodon compatibility routes" do
161 test "it returns a json representation of the object with accept application/json", %{
168 "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
169 "actor" => Endpoint.url() <> "/users/raymoo",
170 "to" => [Pleroma.Constants.as_public()]
176 |> put_req_header("accept", "application/json")
177 |> get("/users/raymoo/statuses/999999999")
179 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object})
182 test "it returns a json representation of the activity with accept application/json", %{
189 "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
190 "actor" => Endpoint.url() <> "/users/raymoo",
191 "to" => [Pleroma.Constants.as_public()]
197 "id" => object.data["id"] <> "/activity",
199 "object" => object.data["id"],
200 "actor" => object.data["actor"],
201 "to" => object.data["to"]
203 |> ActivityPub.persist(local: true)
207 |> put_req_header("accept", "application/json")
208 |> get("/users/raymoo/statuses/999999999/activity")
210 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
214 describe "/objects/:uuid" do
215 test "it doesn't return a local-only object", %{conn: conn} do
217 {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
219 assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
221 object = Object.normalize(post, fetch: false)
222 uuid = String.split(object.data["id"], "/") |> List.last()
226 |> put_req_header("accept", "application/json")
227 |> get("/objects/#{uuid}")
229 assert json_response(conn, 404)
232 test "returns local-only objects when authenticated", %{conn: conn} do
234 {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
236 assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
238 object = Object.normalize(post, fetch: false)
239 uuid = String.split(object.data["id"], "/") |> List.last()
243 |> assign(:user, user)
244 |> put_req_header("accept", "application/activity+json")
245 |> get("/objects/#{uuid}")
247 assert json_response(response, 200) == ObjectView.render("object.json", %{object: object})
250 test "it returns a json representation of the object with accept application/json", %{
254 uuid = String.split(note.data["id"], "/") |> List.last()
258 |> put_req_header("accept", "application/json")
259 |> get("/objects/#{uuid}")
261 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
264 test "it returns a json representation of the object with accept application/activity+json",
267 uuid = String.split(note.data["id"], "/") |> List.last()
271 |> put_req_header("accept", "application/activity+json")
272 |> get("/objects/#{uuid}")
274 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
277 test "it returns a json representation of the object with accept application/ld+json", %{
281 uuid = String.split(note.data["id"], "/") |> List.last()
287 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
289 |> get("/objects/#{uuid}")
291 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
294 test "it returns 404 for non-public messages", %{conn: conn} do
295 note = insert(:direct_note)
296 uuid = String.split(note.data["id"], "/") |> List.last()
300 |> put_req_header("accept", "application/activity+json")
301 |> get("/objects/#{uuid}")
303 assert json_response(conn, 404)
306 test "returns visible non-public messages when authenticated", %{conn: conn} do
307 note = insert(:direct_note)
308 uuid = String.split(note.data["id"], "/") |> List.last()
309 user = User.get_by_ap_id(note.data["actor"])
310 marisa = insert(:user)
313 |> assign(:user, marisa)
314 |> put_req_header("accept", "application/activity+json")
315 |> get("/objects/#{uuid}")
316 |> json_response(404)
320 |> assign(:user, user)
321 |> put_req_header("accept", "application/activity+json")
322 |> get("/objects/#{uuid}")
323 |> json_response(200)
325 assert response == ObjectView.render("object.json", %{object: note})
328 test "it returns 404 for tombstone objects", %{conn: conn} do
329 tombstone = insert(:tombstone)
330 uuid = String.split(tombstone.data["id"], "/") |> List.last()
334 |> put_req_header("accept", "application/activity+json")
335 |> get("/objects/#{uuid}")
337 assert json_response(conn, 404)
340 test "it caches a response", %{conn: conn} do
342 uuid = String.split(note.data["id"], "/") |> List.last()
346 |> put_req_header("accept", "application/activity+json")
347 |> get("/objects/#{uuid}")
349 assert json_response(conn1, :ok)
350 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
354 |> put_req_header("accept", "application/activity+json")
355 |> get("/objects/#{uuid}")
357 assert json_response(conn1, :ok) == json_response(conn2, :ok)
358 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
361 test "cached purged after object deletion", %{conn: conn} do
363 uuid = String.split(note.data["id"], "/") |> List.last()
367 |> put_req_header("accept", "application/activity+json")
368 |> get("/objects/#{uuid}")
370 assert json_response(conn1, :ok)
371 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
377 |> put_req_header("accept", "application/activity+json")
378 |> get("/objects/#{uuid}")
380 assert "Not found" == json_response(conn2, :not_found)
384 describe "/activities/:uuid" do
385 test "it doesn't return a local-only activity", %{conn: conn} do
387 {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
389 assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
391 uuid = String.split(post.data["id"], "/") |> List.last()
395 |> put_req_header("accept", "application/json")
396 |> get("/activities/#{uuid}")
398 assert json_response(conn, 404)
401 test "returns local-only activities when authenticated", %{conn: conn} do
403 {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
405 assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
407 uuid = String.split(post.data["id"], "/") |> List.last()
411 |> assign(:user, user)
412 |> put_req_header("accept", "application/activity+json")
413 |> get("/activities/#{uuid}")
415 assert json_response(response, 200) == ObjectView.render("object.json", %{object: post})
418 test "it returns a json representation of the activity", %{conn: conn} do
419 activity = insert(:note_activity)
420 uuid = String.split(activity.data["id"], "/") |> List.last()
424 |> put_req_header("accept", "application/activity+json")
425 |> get("/activities/#{uuid}")
427 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
430 test "it returns 404 for non-public activities", %{conn: conn} do
431 activity = insert(:direct_note_activity)
432 uuid = String.split(activity.data["id"], "/") |> List.last()
436 |> put_req_header("accept", "application/activity+json")
437 |> get("/activities/#{uuid}")
439 assert json_response(conn, 404)
442 test "returns visible non-public messages when authenticated", %{conn: conn} do
443 note = insert(:direct_note_activity)
444 uuid = String.split(note.data["id"], "/") |> List.last()
445 user = User.get_by_ap_id(note.data["actor"])
446 marisa = insert(:user)
449 |> assign(:user, marisa)
450 |> put_req_header("accept", "application/activity+json")
451 |> get("/activities/#{uuid}")
452 |> json_response(404)
456 |> assign(:user, user)
457 |> put_req_header("accept", "application/activity+json")
458 |> get("/activities/#{uuid}")
459 |> json_response(200)
461 assert response == ObjectView.render("object.json", %{object: note})
464 test "it caches a response", %{conn: conn} do
465 activity = insert(:note_activity)
466 uuid = String.split(activity.data["id"], "/") |> List.last()
470 |> put_req_header("accept", "application/activity+json")
471 |> get("/activities/#{uuid}")
473 assert json_response(conn1, :ok)
474 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
478 |> put_req_header("accept", "application/activity+json")
479 |> get("/activities/#{uuid}")
481 assert json_response(conn1, :ok) == json_response(conn2, :ok)
482 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
485 test "cached purged after activity deletion", %{conn: conn} do
487 {:ok, activity} = CommonAPI.post(user, %{status: "cofe"})
489 uuid = String.split(activity.data["id"], "/") |> List.last()
493 |> put_req_header("accept", "application/activity+json")
494 |> get("/activities/#{uuid}")
496 assert json_response(conn1, :ok)
497 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
499 Activity.delete_all_by_object_ap_id(activity.object.data["id"])
503 |> put_req_header("accept", "application/activity+json")
504 |> get("/activities/#{uuid}")
506 assert "Not found" == json_response(conn2, :not_found)
511 test "it inserts an incoming activity into the database", %{conn: conn} do
512 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
516 |> assign(:valid_signature, true)
517 |> put_req_header("content-type", "application/activity+json")
518 |> post("/inbox", data)
520 assert "ok" == json_response(conn, 200)
522 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
523 assert Activity.get_by_ap_id(data["id"])
526 @tag capture_log: true
527 test "it inserts an incoming activity into the database" <>
528 "even if we can't fetch the user but have it in our db",
532 ap_id: "https://mastodon.example.org/users/raymoo",
535 last_refreshed_at: nil
539 File.read!("test/fixtures/mastodon-post-activity.json")
541 |> Map.put("actor", user.ap_id)
542 |> put_in(["object", "attributedTo"], user.ap_id)
546 |> assign(:valid_signature, true)
547 |> put_req_header("content-type", "application/activity+json")
548 |> post("/inbox", data)
550 assert "ok" == json_response(conn, 200)
552 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
553 assert Activity.get_by_ap_id(data["id"])
556 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
557 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
559 sender_url = data["actor"]
560 Instances.set_consistently_unreachable(sender_url)
561 refute Instances.reachable?(sender_url)
565 |> assign(:valid_signature, true)
566 |> put_req_header("content-type", "application/activity+json")
567 |> post("/inbox", data)
569 assert "ok" == json_response(conn, 200)
570 assert Instances.reachable?(sender_url)
573 test "accept follow activity", %{conn: conn} do
574 clear_config([:instance, :federating], true)
575 relay = Relay.get_actor()
577 assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
579 followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
580 relay = refresh_record(relay)
583 File.read!("test/fixtures/relay/accept-follow.json")
584 |> String.replace("{{ap_id}}", relay.ap_id)
585 |> String.replace("{{activity_id}}", activity.data["id"])
589 |> assign(:valid_signature, true)
590 |> put_req_header("content-type", "application/activity+json")
591 |> post("/inbox", accept)
592 |> json_response(200)
594 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
596 assert Pleroma.FollowingRelationship.following?(
601 Mix.shell(Mix.Shell.Process)
604 Mix.shell(Mix.Shell.IO)
607 :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
608 assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]}
611 @tag capture_log: true
612 test "without valid signature, " <>
613 "it only accepts Create activities and requires enabled federation",
615 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
616 non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
618 conn = put_req_header(conn, "content-type", "application/activity+json")
620 clear_config([:instance, :federating], false)
623 |> post("/inbox", data)
624 |> json_response(403)
627 |> post("/inbox", non_create_data)
628 |> json_response(403)
630 clear_config([:instance, :federating], true)
632 ret_conn = post(conn, "/inbox", data)
633 assert "ok" == json_response(ret_conn, 200)
636 |> post("/inbox", non_create_data)
637 |> json_response(400)
640 test "accepts Add/Remove activities", %{conn: conn} do
641 object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"
644 File.read!("test/fixtures/statuses/note.json")
645 |> String.replace("{{nickname}}", "lain")
646 |> String.replace("{{object_id}}", object_id)
648 object_url = "https://example.com/objects/#{object_id}"
651 File.read!("test/fixtures/users_mock/user.json")
652 |> String.replace("{{nickname}}", "lain")
654 actor = "https://example.com/users/lain"
664 headers: [{"content-type", "application/activity+json"}]
674 headers: [{"content-type", "application/activity+json"}]
677 %{method: :get, url: "https://example.com/users/lain/collections/featured"} ->
681 "test/fixtures/users_mock/masto_featured.json"
683 |> String.replace("{{domain}}", "example.com")
684 |> String.replace("{{nickname}}", "lain"),
685 headers: [{"content-type", "application/activity+json"}]
690 "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423f",
692 "object" => object_url,
693 "target" => "https://example.com/users/lain/collections/featured",
695 "to" => [Pleroma.Constants.as_public()]
700 |> assign(:valid_signature, true)
701 |> put_req_header("content-type", "application/activity+json")
702 |> post("/inbox", data)
703 |> json_response(200)
705 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
706 assert Activity.get_by_ap_id(data["id"])
707 user = User.get_cached_by_ap_id(data["actor"])
708 assert user.pinned_objects[data["object"]]
711 "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423d",
713 "object" => object_url,
714 "target" => "https://example.com/users/lain/collections/featured",
716 "to" => [Pleroma.Constants.as_public()]
721 |> assign(:valid_signature, true)
722 |> put_req_header("content-type", "application/activity+json")
723 |> post("/inbox", data)
724 |> json_response(200)
726 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
727 user = refresh_record(user)
728 refute user.pinned_objects[data["object"]]
731 test "mastodon pin/unpin", %{conn: conn} do
732 status_id = "105786274556060421"
735 File.read!("test/fixtures/statuses/masto-note.json")
736 |> String.replace("{{nickname}}", "lain")
737 |> String.replace("{{status_id}}", status_id)
739 status_url = "https://example.com/users/lain/statuses/#{status_id}"
742 File.read!("test/fixtures/users_mock/user.json")
743 |> String.replace("{{nickname}}", "lain")
745 actor = "https://example.com/users/lain"
755 headers: [{"content-type", "application/activity+json"}]
765 headers: [{"content-type", "application/activity+json"}]
768 %{method: :get, url: "https://example.com/users/lain/collections/featured"} ->
772 "test/fixtures/users_mock/masto_featured.json"
774 |> String.replace("{{domain}}", "example.com")
775 |> String.replace("{{nickname}}", "lain"),
776 headers: [{"content-type", "application/activity+json"}]
781 "@context" => "https://www.w3.org/ns/activitystreams",
783 "object" => status_url,
784 "target" => "https://example.com/users/lain/collections/featured",
790 |> assign(:valid_signature, true)
791 |> put_req_header("content-type", "application/activity+json")
792 |> post("/inbox", data)
793 |> json_response(200)
795 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
796 assert Activity.get_by_object_ap_id_with_object(data["object"])
797 user = User.get_cached_by_ap_id(data["actor"])
798 assert user.pinned_objects[data["object"]]
802 "object" => status_url,
803 "target" => "https://example.com/users/lain/collections/featured",
809 |> assign(:valid_signature, true)
810 |> put_req_header("content-type", "application/activity+json")
811 |> post("/inbox", data)
812 |> json_response(200)
814 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
815 assert Activity.get_by_object_ap_id_with_object(data["object"])
816 user = refresh_record(user)
817 refute user.pinned_objects[data["object"]]
821 describe "/users/:nickname/inbox" do
824 File.read!("test/fixtures/mastodon-post-activity.json")
830 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
835 |> Map.put("bcc", [user.ap_id])
836 |> Kernel.put_in(["object", "bcc"], [user.ap_id])
840 |> assign(:valid_signature, true)
841 |> put_req_header("content-type", "application/activity+json")
842 |> post("/users/#{user.nickname}/inbox", data)
844 assert "ok" == json_response(conn, 200)
845 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
846 assert Activity.get_by_ap_id(data["id"])
849 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
854 |> Map.put("to", user.ap_id)
856 |> Kernel.put_in(["object", "to"], user.ap_id)
857 |> Kernel.put_in(["object", "cc"], [])
861 |> assign(:valid_signature, true)
862 |> put_req_header("content-type", "application/activity+json")
863 |> post("/users/#{user.nickname}/inbox", data)
865 assert "ok" == json_response(conn, 200)
866 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
867 assert Activity.get_by_ap_id(data["id"])
870 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
876 |> Map.put("cc", user.ap_id)
877 |> Kernel.put_in(["object", "to"], [])
878 |> Kernel.put_in(["object", "cc"], user.ap_id)
882 |> assign(:valid_signature, true)
883 |> put_req_header("content-type", "application/activity+json")
884 |> post("/users/#{user.nickname}/inbox", data)
886 assert "ok" == json_response(conn, 200)
887 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
888 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
889 assert user.ap_id in activity.recipients
892 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
899 |> Map.put("bcc", user.ap_id)
900 |> Kernel.put_in(["object", "to"], [])
901 |> Kernel.put_in(["object", "cc"], [])
902 |> Kernel.put_in(["object", "bcc"], user.ap_id)
906 |> assign(:valid_signature, true)
907 |> put_req_header("content-type", "application/activity+json")
908 |> post("/users/#{user.nickname}/inbox", data)
910 assert "ok" == json_response(conn, 200)
911 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
912 assert Activity.get_by_ap_id(data["id"])
915 test "it accepts announces with to as string instead of array", %{conn: conn} do
918 {:ok, post} = CommonAPI.post(user, %{status: "hey"})
919 announcer = insert(:user, local: false)
922 "@context" => "https://www.w3.org/ns/activitystreams",
923 "actor" => announcer.ap_id,
924 "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity",
925 "object" => post.data["object"],
926 "to" => "https://www.w3.org/ns/activitystreams#Public",
927 "cc" => [user.ap_id],
933 |> assign(:valid_signature, true)
934 |> put_req_header("content-type", "application/activity+json")
935 |> post("/users/#{user.nickname}/inbox", data)
937 assert "ok" == json_response(conn, 200)
938 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
939 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
940 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
943 test "it accepts messages from actors that are followed by the user", %{
947 recipient = insert(:user)
948 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
950 {:ok, recipient, actor} = User.follow(recipient, actor)
954 |> Map.put("attributedTo", actor.ap_id)
958 |> Map.put("actor", actor.ap_id)
959 |> Map.put("object", object)
963 |> assign(:valid_signature, true)
964 |> put_req_header("content-type", "application/activity+json")
965 |> post("/users/#{recipient.nickname}/inbox", data)
967 assert "ok" == json_response(conn, 200)
968 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
969 assert Activity.get_by_ap_id(data["id"])
972 test "it rejects reads from other users", %{conn: conn} do
974 other_user = insert(:user)
978 |> assign(:user, other_user)
979 |> put_req_header("accept", "application/activity+json")
980 |> get("/users/#{user.nickname}/inbox")
982 assert json_response(conn, 403)
985 test "it returns a note activity in a collection", %{conn: conn} do
986 note_activity = insert(:direct_note_activity)
987 note_object = Object.normalize(note_activity, fetch: false)
988 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
992 |> assign(:user, user)
993 |> put_req_header("accept", "application/activity+json")
994 |> get("/users/#{user.nickname}/inbox?page=true")
996 assert response(conn, 200) =~ note_object.data["content"]
999 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
1000 user = insert(:user)
1001 data = Map.put(data, "bcc", [user.ap_id])
1003 sender_host = URI.parse(data["actor"]).host
1004 Instances.set_consistently_unreachable(sender_host)
1005 refute Instances.reachable?(sender_host)
1009 |> assign(:valid_signature, true)
1010 |> put_req_header("content-type", "application/activity+json")
1011 |> post("/users/#{user.nickname}/inbox", data)
1013 assert "ok" == json_response(conn, 200)
1014 assert Instances.reachable?(sender_host)
1017 @tag capture_log: true
1018 test "it removes all follower collections but actor's", %{conn: conn} do
1019 [actor, recipient] = insert_pair(:user)
1023 recipient.follower_address,
1024 "https://www.w3.org/ns/activitystreams#Public"
1027 cc = [recipient.follower_address, actor.follower_address]
1030 "@context" => ["https://www.w3.org/ns/activitystreams"],
1032 "id" => Utils.generate_activity_id(),
1035 "actor" => actor.ap_id,
1040 "content" => "It's a note",
1041 "attributedTo" => actor.ap_id,
1042 "id" => Utils.generate_object_id()
1047 |> assign(:valid_signature, true)
1048 |> put_req_header("content-type", "application/activity+json")
1049 |> post("/users/#{recipient.nickname}/inbox", data)
1050 |> json_response(200)
1052 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
1054 assert activity = Activity.get_by_ap_id(data["id"])
1057 assert actor.follower_address in activity.recipients
1058 assert actor.follower_address in activity.data["cc"]
1060 refute recipient.follower_address in activity.recipients
1061 refute recipient.follower_address in activity.data["cc"]
1062 refute recipient.follower_address in activity.data["to"]
1065 test "it requires authentication", %{conn: conn} do
1066 user = insert(:user)
1067 conn = put_req_header(conn, "accept", "application/activity+json")
1069 ret_conn = get(conn, "/users/#{user.nickname}/inbox")
1070 assert json_response(ret_conn, 403)
1074 |> assign(:user, user)
1075 |> get("/users/#{user.nickname}/inbox")
1077 assert json_response(ret_conn, 200)
1080 @tag capture_log: true
1081 test "forwarded report", %{conn: conn} do
1082 admin = insert(:user, is_admin: true)
1083 actor = insert(:user, local: false)
1084 remote_domain = URI.parse(actor.ap_id).host
1085 reported_user = insert(:user)
1087 note = insert(:note_activity, user: reported_user)
1091 "https://www.w3.org/ns/activitystreams",
1092 "https://#{remote_domain}/schemas/litepub-0.1.jsonld",
1094 "@language" => "und"
1097 "actor" => actor.ap_id,
1101 "content" => "test",
1102 "context" => "context",
1103 "id" => "http://#{remote_domain}/activities/02be56cf-35e3-46b4-b2c6-47ae08dfee9e",
1104 "nickname" => reported_user.nickname,
1106 reported_user.ap_id,
1109 "actor_type" => "Person",
1110 "approval_pending" => false,
1112 "confirmation_pending" => false,
1113 "deactivated" => false,
1114 "display_name" => "test user",
1115 "id" => reported_user.id,
1117 "nickname" => reported_user.nickname,
1118 "registration_reason" => nil,
1121 "moderator" => false
1124 "url" => reported_user.ap_id
1127 "id" => note.data["id"],
1128 "published" => note.data["published"],
1132 "published" => note.data["published"],
1139 |> assign(:valid_signature, true)
1140 |> put_req_header("content-type", "application/activity+json")
1141 |> post("/users/#{reported_user.nickname}/inbox", data)
1142 |> json_response(200)
1144 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
1146 assert Pleroma.Repo.aggregate(Activity, :count, :id) == 2
1148 ObanHelpers.perform_all()
1150 Swoosh.TestAssertions.assert_email_sent(
1151 to: {admin.name, admin.email},
1152 html_body: ~r/Reported Account:/i
1156 @tag capture_log: true
1157 test "forwarded report from mastodon", %{conn: conn} do
1158 admin = insert(:user, is_admin: true)
1159 actor = insert(:user, local: false)
1160 remote_domain = URI.parse(actor.ap_id).host
1161 remote_actor = "https://#{remote_domain}/actor"
1162 [reported_user, another] = insert_list(2, :user)
1164 note = insert(:note_activity, user: reported_user)
1166 Pleroma.Web.CommonAPI.favorite(another, note.id)
1169 "test/fixtures/mastodon/application_actor.json"
1171 |> String.replace("{{DOMAIN}}", remote_domain)
1173 Tesla.Mock.mock(fn %{url: ^remote_actor} ->
1176 body: mock_json_body,
1177 headers: [{"content-type", "application/activity+json"}]
1182 "@context" => "https://www.w3.org/ns/activitystreams",
1183 "actor" => remote_actor,
1184 "content" => "test report",
1185 "id" => "https://#{remote_domain}/e3b12fd1-948c-446e-b93b-a5e67edbe1d8",
1187 reported_user.ap_id,
1194 |> assign(:valid_signature, true)
1195 |> put_req_header("content-type", "application/activity+json")
1196 |> post("/users/#{reported_user.nickname}/inbox", data)
1197 |> json_response(200)
1199 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
1201 flag_activity = "Flag" |> Pleroma.Activity.Queries.by_type() |> Pleroma.Repo.one()
1202 reported_user_ap_id = reported_user.ap_id
1204 [^reported_user_ap_id, flag_data] = flag_activity.data["object"]
1206 Enum.each(~w(actor content id published type), &Map.has_key?(flag_data, &1))
1207 ObanHelpers.perform_all()
1209 Swoosh.TestAssertions.assert_email_sent(
1210 to: {admin.name, admin.email},
1211 html_body: ~r/#{note.data["object"]}/i
1216 describe "GET /users/:nickname/outbox" do
1217 test "it paginates correctly", %{conn: conn} do
1218 user = insert(:user)
1219 conn = assign(conn, :user, user)
1220 outbox_endpoint = user.ap_id <> "/outbox"
1224 {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})
1230 |> put_req_header("accept", "application/activity+json")
1231 |> get(outbox_endpoint <> "?page=true")
1232 |> json_response(200)
1234 result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end)
1235 assert length(result["orderedItems"]) == 20
1236 assert length(result_ids) == 20
1237 assert result["next"]
1238 assert String.starts_with?(result["next"], outbox_endpoint)
1242 |> put_req_header("accept", "application/activity+json")
1243 |> get(result["next"])
1244 |> json_response(200)
1246 result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end)
1247 assert length(result_next["orderedItems"]) == 6
1248 assert length(result_next_ids) == 6
1249 refute Enum.find(result_next_ids, fn x -> x in result_ids end)
1250 refute Enum.find(result_ids, fn x -> x in result_next_ids end)
1251 assert String.starts_with?(result["id"], outbox_endpoint)
1255 |> put_req_header("accept", "application/activity+json")
1256 |> get(result_next["id"])
1257 |> json_response(200)
1259 assert result_next == result_next_again
1262 test "it returns 200 even if there're no activities", %{conn: conn} do
1263 user = insert(:user)
1264 outbox_endpoint = user.ap_id <> "/outbox"
1268 |> assign(:user, user)
1269 |> put_req_header("accept", "application/activity+json")
1270 |> get(outbox_endpoint)
1272 result = json_response(conn, 200)
1273 assert outbox_endpoint == result["id"]
1276 test "it returns a note activity in a collection", %{conn: conn} do
1277 note_activity = insert(:note_activity)
1278 note_object = Object.normalize(note_activity, fetch: false)
1279 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1283 |> assign(:user, user)
1284 |> put_req_header("accept", "application/activity+json")
1285 |> get("/users/#{user.nickname}/outbox?page=true")
1287 assert response(conn, 200) =~ note_object.data["content"]
1290 test "it returns an announce activity in a collection", %{conn: conn} do
1291 announce_activity = insert(:announce_activity)
1292 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
1296 |> assign(:user, user)
1297 |> put_req_header("accept", "application/activity+json")
1298 |> get("/users/#{user.nickname}/outbox?page=true")
1300 assert response(conn, 200) =~ announce_activity.data["object"]
1303 test "It returns poll Answers when authenticated", %{conn: conn} do
1304 poller = insert(:user)
1305 voter = insert(:user)
1308 CommonAPI.post(poller, %{
1310 poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10}
1313 assert question = Object.normalize(activity, fetch: false)
1315 {:ok, [activity], _object} = CommonAPI.vote(voter, question, [1])
1319 |> assign(:user, voter)
1320 |> put_req_header("accept", "application/activity+json")
1321 |> get(voter.ap_id <> "/outbox?page=true")
1322 |> json_response(200)
1324 assert [answer_outbox] = outbox_get["orderedItems"]
1325 assert answer_outbox["id"] == activity.data["id"]
1329 describe "POST /users/:nickname/outbox (C2S)" do
1330 setup do: clear_config([:instance, :limit])
1335 "@context" => "https://www.w3.org/ns/activitystreams",
1337 "object" => %{"type" => "Note", "content" => "AP C2S test"},
1338 "to" => "https://www.w3.org/ns/activitystreams#Public",
1344 test "it rejects posts from other users / unauthenticated users", %{
1348 user = insert(:user)
1349 other_user = insert(:user)
1350 conn = put_req_header(conn, "content-type", "application/activity+json")
1353 |> post("/users/#{user.nickname}/outbox", activity)
1354 |> json_response(403)
1357 |> assign(:user, other_user)
1358 |> post("/users/#{user.nickname}/outbox", activity)
1359 |> json_response(403)
1362 test "it inserts an incoming create activity into the database", %{
1366 user = insert(:user)
1370 |> assign(:user, user)
1371 |> put_req_header("content-type", "application/activity+json")
1372 |> post("/users/#{user.nickname}/outbox", activity)
1373 |> json_response(201)
1375 assert Activity.get_by_ap_id(result["id"])
1376 assert result["object"]
1377 assert %Object{data: object} = Object.normalize(result["object"], fetch: false)
1378 assert object["content"] == activity["object"]["content"]
1381 test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
1382 user = insert(:user)
1386 |> put_in(["object", "type"], "Benis")
1390 |> assign(:user, user)
1391 |> put_req_header("content-type", "application/activity+json")
1392 |> post("/users/#{user.nickname}/outbox", activity)
1393 |> json_response(400)
1396 test "it inserts an incoming sensitive activity into the database", %{
1400 user = insert(:user)
1401 conn = assign(conn, :user, user)
1402 object = Map.put(activity["object"], "sensitive", true)
1403 activity = Map.put(activity, "object", object)
1407 |> put_req_header("content-type", "application/activity+json")
1408 |> post("/users/#{user.nickname}/outbox", activity)
1409 |> json_response(201)
1411 assert Activity.get_by_ap_id(response["id"])
1412 assert response["object"]
1413 assert %Object{data: response_object} = Object.normalize(response["object"], fetch: false)
1414 assert response_object["sensitive"] == true
1415 assert response_object["content"] == activity["object"]["content"]
1419 |> put_req_header("accept", "application/activity+json")
1420 |> get(response["id"])
1421 |> json_response(200)
1423 assert representation["object"]["sensitive"] == true
1426 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
1427 user = insert(:user)
1428 activity = Map.put(activity, "type", "BadType")
1432 |> assign(:user, user)
1433 |> put_req_header("content-type", "application/activity+json")
1434 |> post("/users/#{user.nickname}/outbox", activity)
1436 assert json_response(conn, 400)
1439 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
1440 note_activity = insert(:note_activity)
1441 note_object = Object.normalize(note_activity, fetch: false)
1442 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1447 id: note_object.data["id"]
1453 |> assign(:user, user)
1454 |> put_req_header("content-type", "application/activity+json")
1455 |> post("/users/#{user.nickname}/outbox", data)
1457 result = json_response(conn, 201)
1458 assert Activity.get_by_ap_id(result["id"])
1460 assert object = Object.get_by_ap_id(note_object.data["id"])
1461 assert object.data["type"] == "Tombstone"
1464 test "it rejects delete activity of object from other actor", %{conn: conn} do
1465 note_activity = insert(:note_activity)
1466 note_object = Object.normalize(note_activity, fetch: false)
1467 user = insert(:user)
1472 id: note_object.data["id"]
1478 |> assign(:user, user)
1479 |> put_req_header("content-type", "application/activity+json")
1480 |> post("/users/#{user.nickname}/outbox", data)
1482 assert json_response(conn, 400)
1485 test "it increases like count when receiving a like action", %{conn: conn} do
1486 note_activity = insert(:note_activity)
1487 note_object = Object.normalize(note_activity, fetch: false)
1488 user = User.get_cached_by_ap_id(note_activity.data["actor"])
1493 id: note_object.data["id"]
1499 |> assign(:user, user)
1500 |> put_req_header("content-type", "application/activity+json")
1501 |> post("/users/#{user.nickname}/outbox", data)
1503 result = json_response(conn, 201)
1504 assert Activity.get_by_ap_id(result["id"])
1506 assert object = Object.get_by_ap_id(note_object.data["id"])
1507 assert object.data["like_count"] == 1
1510 test "it doesn't spreads faulty attributedTo or actor fields", %{
1514 reimu = insert(:user, nickname: "reimu")
1515 cirno = insert(:user, nickname: "cirno")
1522 |> put_in(["object", "actor"], reimu.ap_id)
1523 |> put_in(["object", "attributedTo"], reimu.ap_id)
1524 |> put_in(["actor"], reimu.ap_id)
1525 |> put_in(["attributedTo"], reimu.ap_id)
1529 |> assign(:user, cirno)
1530 |> put_req_header("content-type", "application/activity+json")
1531 |> post("/users/#{reimu.nickname}/outbox", activity)
1532 |> json_response(403)
1536 |> assign(:user, cirno)
1537 |> put_req_header("content-type", "application/activity+json")
1538 |> post("/users/#{cirno.nickname}/outbox", activity)
1539 |> json_response(201)
1541 assert cirno_outbox["attributedTo"] == nil
1542 assert cirno_outbox["actor"] == cirno.ap_id
1544 assert cirno_object = Object.normalize(cirno_outbox["object"], fetch: false)
1545 assert cirno_object.data["actor"] == cirno.ap_id
1546 assert cirno_object.data["attributedTo"] == cirno.ap_id
1549 test "Character limitation", %{conn: conn, activity: activity} do
1550 clear_config([:instance, :limit], 5)
1551 user = insert(:user)
1555 |> assign(:user, user)
1556 |> put_req_header("content-type", "application/activity+json")
1557 |> post("/users/#{user.nickname}/outbox", activity)
1558 |> json_response(400)
1560 assert result == "Note is over the character limit"
1564 describe "/relay/followers" do
1565 test "it returns relay followers", %{conn: conn} do
1566 relay_actor = Relay.get_actor()
1567 user = insert(:user)
1568 User.follow(user, relay_actor)
1572 |> get("/relay/followers")
1573 |> json_response(200)
1575 assert result["first"]["orderedItems"] == [user.ap_id]
1578 test "on non-federating instance, it returns 404", %{conn: conn} do
1579 clear_config([:instance, :federating], false)
1580 user = insert(:user)
1583 |> assign(:user, user)
1584 |> get("/relay/followers")
1585 |> json_response(404)
1589 describe "/relay/following" do
1590 test "it returns relay following", %{conn: conn} do
1593 |> get("/relay/following")
1594 |> json_response(200)
1596 assert result["first"]["orderedItems"] == []
1599 test "on non-federating instance, it returns 404", %{conn: conn} do
1600 clear_config([:instance, :federating], false)
1601 user = insert(:user)
1604 |> assign(:user, user)
1605 |> get("/relay/following")
1606 |> json_response(404)
1610 describe "/users/:nickname/followers" do
1611 test "it returns the followers in a collection", %{conn: conn} do
1612 user = insert(:user)
1613 user_two = insert(:user)
1614 User.follow(user, user_two)
1618 |> assign(:user, user_two)
1619 |> get("/users/#{user_two.nickname}/followers")
1620 |> json_response(200)
1622 assert result["first"]["orderedItems"] == [user.ap_id]
1625 test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
1626 user = insert(:user)
1627 user_two = insert(:user, hide_followers: true)
1628 User.follow(user, user_two)
1632 |> assign(:user, user)
1633 |> get("/users/#{user_two.nickname}/followers")
1634 |> json_response(200)
1636 assert is_binary(result["first"])
1639 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user",
1641 user = insert(:user)
1642 other_user = insert(:user, hide_followers: true)
1646 |> assign(:user, user)
1647 |> get("/users/#{other_user.nickname}/followers?page=1")
1649 assert result.status == 403
1650 assert result.resp_body == ""
1653 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
1655 user = insert(:user, hide_followers: true)
1656 other_user = insert(:user)
1657 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
1661 |> assign(:user, user)
1662 |> get("/users/#{user.nickname}/followers?page=1")
1663 |> json_response(200)
1665 assert result["totalItems"] == 1
1666 assert result["orderedItems"] == [other_user.ap_id]
1669 test "it works for more than 10 users", %{conn: conn} do
1670 user = insert(:user)
1672 Enum.each(1..15, fn _ ->
1673 other_user = insert(:user)
1674 User.follow(other_user, user)
1679 |> assign(:user, user)
1680 |> get("/users/#{user.nickname}/followers")
1681 |> json_response(200)
1683 assert length(result["first"]["orderedItems"]) == 10
1684 assert result["first"]["totalItems"] == 15
1685 assert result["totalItems"] == 15
1689 |> assign(:user, user)
1690 |> get("/users/#{user.nickname}/followers?page=2")
1691 |> json_response(200)
1693 assert length(result["orderedItems"]) == 5
1694 assert result["totalItems"] == 15
1697 test "does not require authentication", %{conn: conn} do
1698 user = insert(:user)
1701 |> get("/users/#{user.nickname}/followers")
1702 |> json_response(200)
1706 describe "/users/:nickname/following" do
1707 test "it returns the following in a collection", %{conn: conn} do
1708 user = insert(:user)
1709 user_two = insert(:user)
1710 User.follow(user, user_two)
1714 |> assign(:user, user)
1715 |> get("/users/#{user.nickname}/following")
1716 |> json_response(200)
1718 assert result["first"]["orderedItems"] == [user_two.ap_id]
1721 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
1722 user = insert(:user)
1723 user_two = insert(:user, hide_follows: true)
1724 User.follow(user, user_two)
1728 |> assign(:user, user)
1729 |> get("/users/#{user_two.nickname}/following")
1730 |> json_response(200)
1732 assert is_binary(result["first"])
1735 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user",
1737 user = insert(:user)
1738 user_two = insert(:user, hide_follows: true)
1742 |> assign(:user, user)
1743 |> get("/users/#{user_two.nickname}/following?page=1")
1745 assert result.status == 403
1746 assert result.resp_body == ""
1749 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
1751 user = insert(:user, hide_follows: true)
1752 other_user = insert(:user)
1753 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
1757 |> assign(:user, user)
1758 |> get("/users/#{user.nickname}/following?page=1")
1759 |> json_response(200)
1761 assert result["totalItems"] == 1
1762 assert result["orderedItems"] == [other_user.ap_id]
1765 test "it works for more than 10 users", %{conn: conn} do
1766 user = insert(:user)
1768 Enum.each(1..15, fn _ ->
1769 user = User.get_cached_by_id(user.id)
1770 other_user = insert(:user)
1771 User.follow(user, other_user)
1776 |> assign(:user, user)
1777 |> get("/users/#{user.nickname}/following")
1778 |> json_response(200)
1780 assert length(result["first"]["orderedItems"]) == 10
1781 assert result["first"]["totalItems"] == 15
1782 assert result["totalItems"] == 15
1786 |> assign(:user, user)
1787 |> get("/users/#{user.nickname}/following?page=2")
1788 |> json_response(200)
1790 assert length(result["orderedItems"]) == 5
1791 assert result["totalItems"] == 15
1794 test "does not require authentication", %{conn: conn} do
1795 user = insert(:user)
1798 |> get("/users/#{user.nickname}/following")
1799 |> json_response(200)
1803 describe "delivery tracking" do
1804 test "it tracks a signed object fetch", %{conn: conn} do
1805 user = insert(:user, local: false)
1806 activity = insert(:note_activity)
1807 object = Object.normalize(activity, fetch: false)
1809 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1812 |> put_req_header("accept", "application/activity+json")
1813 |> assign(:user, user)
1815 |> json_response(200)
1817 assert Delivery.get(object.id, user.id)
1820 test "it tracks a signed activity fetch", %{conn: conn} do
1821 user = insert(:user, local: false)
1822 activity = insert(:note_activity)
1823 object = Object.normalize(activity, fetch: false)
1825 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1828 |> put_req_header("accept", "application/activity+json")
1829 |> assign(:user, user)
1830 |> get(activity_path)
1831 |> json_response(200)
1833 assert Delivery.get(object.id, user.id)
1836 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1837 user = insert(:user, local: false)
1838 other_user = insert(:user, local: false)
1839 activity = insert(:note_activity)
1840 object = Object.normalize(activity, fetch: false)
1842 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1845 |> put_req_header("accept", "application/activity+json")
1846 |> assign(:user, user)
1848 |> json_response(200)
1851 |> put_req_header("accept", "application/activity+json")
1852 |> assign(:user, other_user)
1854 |> json_response(200)
1856 assert Delivery.get(object.id, user.id)
1857 assert Delivery.get(object.id, other_user.id)
1860 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1861 user = insert(:user, local: false)
1862 other_user = insert(:user, local: false)
1863 activity = insert(:note_activity)
1864 object = Object.normalize(activity, fetch: false)
1866 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1869 |> put_req_header("accept", "application/activity+json")
1870 |> assign(:user, user)
1871 |> get(activity_path)
1872 |> json_response(200)
1875 |> put_req_header("accept", "application/activity+json")
1876 |> assign(:user, other_user)
1877 |> get(activity_path)
1878 |> json_response(200)
1880 assert Delivery.get(object.id, user.id)
1881 assert Delivery.get(object.id, other_user.id)
1885 describe "Additional ActivityPub C2S endpoints" do
1886 test "GET /api/ap/whoami", %{conn: conn} do
1887 user = insert(:user)
1891 |> assign(:user, user)
1892 |> get("/api/ap/whoami")
1894 user = User.get_cached_by_id(user.id)
1896 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1899 |> get("/api/ap/whoami")
1900 |> json_response(403)
1903 setup do: clear_config([:media_proxy])
1904 setup do: clear_config([Pleroma.Upload])
1906 test "POST /api/ap/upload_media", %{conn: conn} do
1907 user = insert(:user)
1909 desc = "Description of the image"
1911 image = %Plug.Upload{
1912 content_type: "image/jpeg",
1913 path: Path.absname("test/fixtures/image.jpg"),
1914 filename: "an_image.jpg"
1919 |> assign(:user, user)
1920 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1921 |> json_response(:created)
1923 assert object["name"] == desc
1924 assert object["type"] == "Document"
1925 assert object["actor"] == user.ap_id
1926 assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"]
1927 assert is_binary(object_href)
1928 assert object_mediatype == "image/jpeg"
1929 assert String.ends_with?(object_href, ".jpg")
1931 activity_request = %{
1932 "@context" => "https://www.w3.org/ns/activitystreams",
1936 "content" => "AP C2S test, attachment",
1937 "attachment" => [object]
1939 "to" => "https://www.w3.org/ns/activitystreams#Public",
1945 |> assign(:user, user)
1946 |> post("/users/#{user.nickname}/outbox", activity_request)
1947 |> json_response(:created)
1949 assert activity_response["id"]
1950 assert activity_response["object"]
1951 assert activity_response["actor"] == user.ap_id
1953 assert %Object{data: %{"attachment" => [attachment]}} =
1954 Object.normalize(activity_response["object"], fetch: false)
1956 assert attachment["type"] == "Document"
1957 assert attachment["name"] == desc
1961 "href" => ^object_href,
1963 "mediaType" => ^object_mediatype
1965 ] = attachment["url"]
1967 # Fails if unauthenticated
1969 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1970 |> json_response(403)
1974 test "pinned collection", %{conn: conn} do
1975 clear_config([:instance, :max_pinned_statuses], 2)
1976 user = insert(:user)
1977 objects = insert_list(2, :note, user: user)
1979 Enum.reduce(objects, user, fn %{data: %{"id" => object_id}}, user ->
1980 {:ok, updated} = User.add_pinned_object_id(user, object_id)
1984 %{nickname: nickname, featured_address: featured_address, pinned_objects: pinned_objects} =
1985 refresh_record(user)
1987 %{"id" => ^featured_address, "orderedItems" => items, "totalItems" => 2} =
1989 |> get("/users/#{nickname}/collections/featured")
1990 |> json_response(200)
1992 object_ids = Enum.map(items, & &1["id"])
1994 assert Enum.all?(pinned_objects, fn {obj_id, _} ->
1995 obj_id in object_ids