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.ActivityPubTest do
7 use Oban.Testing, repo: Pleroma.Repo
10 alias Pleroma.Builders.ActivityBuilder
12 alias Pleroma.Notification
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.ActivityPub.Utils
17 alias Pleroma.Web.AdminAPI.AccountView
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.Federator
21 import ExUnit.CaptureLog
23 import Pleroma.Factory
27 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
31 setup do: clear_config([:instance, :federating])
33 describe "streaming out participations" do
34 test "it streams them out" do
36 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
38 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
41 conversation.participations
42 |> Repo.preload(:user)
44 with_mock Pleroma.Web.Streamer,
45 stream: fn _, _ -> nil end do
46 ActivityPub.stream_out_participations(conversation.participations)
48 assert called(Pleroma.Web.Streamer.stream("participation", participations))
52 test "streams them out on activity creation" do
53 user_one = insert(:user)
54 user_two = insert(:user)
56 with_mock Pleroma.Web.Streamer,
57 stream: fn _, _ -> nil end do
59 CommonAPI.post(user_one, %{
60 "status" => "@#{user_two.nickname}",
61 "visibility" => "direct"
65 activity.data["context"]
66 |> Pleroma.Conversation.get_for_ap_id()
67 |> Repo.preload(participations: :user)
69 assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
74 describe "fetching restricted by visibility" do
75 test "it restricts by the appropriate visibility" do
78 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
80 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
82 {:ok, unlisted_activity} =
83 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
85 {:ok, private_activity} =
86 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
89 ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
91 assert activities == [direct_activity]
94 ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
96 assert activities == [unlisted_activity]
99 ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
101 assert activities == [private_activity]
104 ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
106 assert activities == [public_activity]
109 ActivityPub.fetch_activities([], %{
110 :visibility => ~w[private public],
111 "actor_id" => user.ap_id
114 assert activities == [public_activity, private_activity]
118 describe "fetching excluded by visibility" do
119 test "it excludes by the appropriate visibility" do
122 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
124 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
126 {:ok, unlisted_activity} =
127 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
129 {:ok, private_activity} =
130 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
133 ActivityPub.fetch_activities([], %{
134 "exclude_visibilities" => "direct",
135 "actor_id" => user.ap_id
138 assert public_activity in activities
139 assert unlisted_activity in activities
140 assert private_activity in activities
141 refute direct_activity in activities
144 ActivityPub.fetch_activities([], %{
145 "exclude_visibilities" => "unlisted",
146 "actor_id" => user.ap_id
149 assert public_activity in activities
150 refute unlisted_activity in activities
151 assert private_activity in activities
152 assert direct_activity in activities
155 ActivityPub.fetch_activities([], %{
156 "exclude_visibilities" => "private",
157 "actor_id" => user.ap_id
160 assert public_activity in activities
161 assert unlisted_activity in activities
162 refute private_activity in activities
163 assert direct_activity in activities
166 ActivityPub.fetch_activities([], %{
167 "exclude_visibilities" => "public",
168 "actor_id" => user.ap_id
171 refute public_activity in activities
172 assert unlisted_activity in activities
173 assert private_activity in activities
174 assert direct_activity in activities
178 describe "building a user from his ap id" do
179 test "it returns a user" do
180 user_id = "http://mastodon.example.org/users/admin"
181 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
182 assert user.ap_id == user_id
183 assert user.nickname == "admin@mastodon.example.org"
184 assert user.ap_enabled
185 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
188 test "it returns a user that is invisible" do
189 user_id = "http://mastodon.example.org/users/relay"
190 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
191 assert User.invisible?(user)
194 test "it fetches the appropriate tag-restricted posts" do
197 {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"})
198 {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
199 {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
201 fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"})
204 ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
207 ActivityPub.fetch_activities([], %{
209 "tag" => ["test", "essais"],
210 "tag_reject" => ["reject"]
214 ActivityPub.fetch_activities([], %{
217 "tag_all" => ["test", "reject"]
220 assert fetch_one == [status_one, status_three]
221 assert fetch_two == [status_one, status_two, status_three]
222 assert fetch_three == [status_one, status_two]
223 assert fetch_four == [status_three]
227 describe "insertion" do
228 test "drops activities beyond a certain limit" do
229 limit = Config.get([:instance, :remote_limit])
232 :crypto.strong_rand_bytes(limit + 1)
234 |> binary_part(0, limit + 1)
239 "content" => random_text
243 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
246 test "doesn't drop activities with content being null" do
250 "actor" => user.ap_id,
253 "actor" => user.ap_id,
260 assert {:ok, _} = ActivityPub.insert(data)
263 test "returns the activity if one with the same id is already in" do
264 activity = insert(:note_activity)
265 {:ok, new_activity} = ActivityPub.insert(activity.data)
267 assert activity.id == new_activity.id
270 test "inserts a given map into the activity database, giving it an id if it has none." do
274 "actor" => user.ap_id,
277 "actor" => user.ap_id,
284 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
285 assert activity.data["ok"] == data["ok"]
286 assert is_binary(activity.data["id"])
292 "actor" => user.ap_id,
294 "context" => "blabla",
296 "actor" => user.ap_id,
303 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
304 assert activity.data["ok"] == data["ok"]
305 assert activity.data["id"] == given_id
306 assert activity.data["context"] == "blabla"
307 assert activity.data["context_id"]
310 test "adds a context when none is there" do
314 "actor" => user.ap_id,
317 "actor" => user.ap_id,
324 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
325 object = Pleroma.Object.normalize(activity)
327 assert is_binary(activity.data["context"])
328 assert is_binary(object.data["context"])
329 assert activity.data["context_id"]
330 assert object.data["context_id"]
333 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
337 "actor" => user.ap_id,
340 "actor" => user.ap_id,
347 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
348 assert object = Object.normalize(activity)
349 assert is_binary(object.data["id"])
353 describe "listen activities" do
354 test "does not increase user note count" do
358 ActivityPub.listen(%{
359 to: ["https://www.w3.org/ns/activitystreams#Public"],
363 "actor" => user.ap_id,
364 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
366 "title" => "lain radio episode 1",
372 assert activity.actor == user.ap_id
374 user = User.get_cached_by_id(user.id)
375 assert user.note_count == 0
378 test "can be fetched into a timeline" do
379 _listen_activity_1 = insert(:listen)
380 _listen_activity_2 = insert(:listen)
381 _listen_activity_3 = insert(:listen)
383 timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})
385 assert length(timeline) == 3
389 describe "create activities" do
390 test "it reverts create" do
393 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
394 assert {:error, :reverted} =
395 ActivityPub.create(%{
396 to: ["user1", "user2"],
400 "to" => ["user1", "user2"],
402 "content" => "testing"
407 assert Repo.aggregate(Activity, :count, :id) == 0
408 assert Repo.aggregate(Object, :count, :id) == 0
411 test "removes doubled 'to' recipients" do
415 ActivityPub.create(%{
416 to: ["user1", "user1", "user2"],
420 "to" => ["user1", "user1", "user2"],
422 "content" => "testing"
426 assert activity.data["to"] == ["user1", "user2"]
427 assert activity.actor == user.ap_id
428 assert activity.recipients == ["user1", "user2", user.ap_id]
431 test "increases user note count only for public activities" do
435 CommonAPI.post(User.get_cached_by_id(user.id), %{
437 "visibility" => "public"
441 CommonAPI.post(User.get_cached_by_id(user.id), %{
443 "visibility" => "unlisted"
447 CommonAPI.post(User.get_cached_by_id(user.id), %{
449 "visibility" => "private"
453 CommonAPI.post(User.get_cached_by_id(user.id), %{
455 "visibility" => "direct"
458 user = User.get_cached_by_id(user.id)
459 assert user.note_count == 2
462 test "increases replies count" do
464 user2 = insert(:user)
466 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
467 ap_id = activity.data["id"]
468 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
471 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
472 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
473 assert object.data["repliesCount"] == 1
476 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
477 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
478 assert object.data["repliesCount"] == 2
481 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
482 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
483 assert object.data["repliesCount"] == 2
486 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
487 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
488 assert object.data["repliesCount"] == 2
492 describe "fetch activities for recipients" do
493 test "retrieve the activities for certain recipients" do
494 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
495 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
496 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
498 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
499 assert length(activities) == 2
500 assert activities == [activity_one, activity_two]
504 describe "fetch activities in context" do
505 test "retrieves activities that have a given context" do
506 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
507 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
508 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
509 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
510 activity_five = insert(:note_activity)
513 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
515 activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
516 assert activities == [activity_two, activity]
520 test "doesn't return blocked activities" do
521 activity_one = insert(:note_activity)
522 activity_two = insert(:note_activity)
523 activity_three = insert(:note_activity)
525 booster = insert(:user)
526 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
529 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
531 assert Enum.member?(activities, activity_two)
532 assert Enum.member?(activities, activity_three)
533 refute Enum.member?(activities, activity_one)
535 {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
538 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
540 assert Enum.member?(activities, activity_two)
541 assert Enum.member?(activities, activity_three)
542 assert Enum.member?(activities, activity_one)
544 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
545 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
546 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
547 activity_three = Activity.get_by_id(activity_three.id)
550 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
552 assert Enum.member?(activities, activity_two)
553 refute Enum.member?(activities, activity_three)
554 refute Enum.member?(activities, boost_activity)
555 assert Enum.member?(activities, activity_one)
558 ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
560 assert Enum.member?(activities, activity_two)
561 assert Enum.member?(activities, activity_three)
562 assert Enum.member?(activities, boost_activity)
563 assert Enum.member?(activities, activity_one)
566 test "doesn't return transitive interactions concerning blocked users" do
567 blocker = insert(:user)
568 blockee = insert(:user)
569 friend = insert(:user)
571 {:ok, _user_relationship} = User.block(blocker, blockee)
573 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
575 {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
577 {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
579 {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
581 activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
583 assert Enum.member?(activities, activity_one)
584 refute Enum.member?(activities, activity_two)
585 refute Enum.member?(activities, activity_three)
586 refute Enum.member?(activities, activity_four)
589 test "doesn't return announce activities concerning blocked users" do
590 blocker = insert(:user)
591 blockee = insert(:user)
592 friend = insert(:user)
594 {:ok, _user_relationship} = User.block(blocker, blockee)
596 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
598 {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
600 {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
603 ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
604 |> Enum.map(fn act -> act.id end)
606 assert Enum.member?(activities, activity_one.id)
607 refute Enum.member?(activities, activity_two.id)
608 refute Enum.member?(activities, activity_three.id)
611 test "doesn't return activities from blocked domains" do
612 domain = "dogwhistle.zone"
613 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
614 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
615 activity = insert(:note_activity, %{note: note})
617 {:ok, user} = User.block_domain(user, domain)
620 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
622 refute activity in activities
624 followed_user = insert(:user)
625 ActivityPub.follow(user, followed_user)
626 {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
629 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
631 refute repeat_activity in activities
634 test "does return activities from followed users on blocked domains" do
635 domain = "meanies.social"
636 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
637 blocker = insert(:user)
639 {:ok, blocker} = User.follow(blocker, domain_user)
640 {:ok, blocker} = User.block_domain(blocker, domain)
642 assert User.following?(blocker, domain_user)
643 assert User.blocks_domain?(blocker, domain_user)
644 refute User.blocks?(blocker, domain_user)
646 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
647 activity = insert(:note_activity, %{note: note})
650 ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
652 assert activity in activities
654 # And check that if the guy we DO follow boosts someone else from their domain,
655 # that should be hidden
656 another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
657 bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
658 bad_activity = insert(:note_activity, %{note: bad_note})
659 {:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
662 ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
664 refute repeat_activity in activities
667 test "doesn't return muted activities" do
668 activity_one = insert(:note_activity)
669 activity_two = insert(:note_activity)
670 activity_three = insert(:note_activity)
672 booster = insert(:user)
674 activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
675 {:ok, _user_relationships} = User.mute(user, activity_one_actor)
678 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
680 assert Enum.member?(activities, activity_two)
681 assert Enum.member?(activities, activity_three)
682 refute Enum.member?(activities, activity_one)
684 # Calling with 'with_muted' will deliver muted activities, too.
686 ActivityPub.fetch_activities([], %{
687 "muting_user" => user,
688 "with_muted" => true,
689 "skip_preload" => true
692 assert Enum.member?(activities, activity_two)
693 assert Enum.member?(activities, activity_three)
694 assert Enum.member?(activities, activity_one)
696 {:ok, _user_mute} = User.unmute(user, activity_one_actor)
699 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
701 assert Enum.member?(activities, activity_two)
702 assert Enum.member?(activities, activity_three)
703 assert Enum.member?(activities, activity_one)
705 activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
706 {:ok, _user_relationships} = User.mute(user, activity_three_actor)
707 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
708 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
709 activity_three = Activity.get_by_id(activity_three.id)
712 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
714 assert Enum.member?(activities, activity_two)
715 refute Enum.member?(activities, activity_three)
716 refute Enum.member?(activities, boost_activity)
717 assert Enum.member?(activities, activity_one)
719 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
721 assert Enum.member?(activities, activity_two)
722 assert Enum.member?(activities, activity_three)
723 assert Enum.member?(activities, boost_activity)
724 assert Enum.member?(activities, activity_one)
727 test "doesn't return thread muted activities" do
729 _activity_one = insert(:note_activity)
730 note_two = insert(:note, data: %{"context" => "suya.."})
731 activity_two = insert(:note_activity, note: note_two)
733 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
735 assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
738 test "returns thread muted activities when with_muted is set" do
740 _activity_one = insert(:note_activity)
741 note_two = insert(:note, data: %{"context" => "suya.."})
742 activity_two = insert(:note_activity, note: note_two)
744 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
746 assert [_activity_two, _activity_one] =
747 ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
750 test "does include announces on request" do
751 activity_three = insert(:note_activity)
753 booster = insert(:user)
755 {:ok, user} = User.follow(user, booster)
757 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
759 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
761 assert announce_activity.id == announce.id
764 test "excludes reblogs on request" do
766 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
767 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
769 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
771 assert activity == expected_activity
774 describe "public fetch activities" do
775 test "doesn't retrieve unlisted activities" do
778 {:ok, _unlisted_activity} =
779 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
781 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
783 [activity] = ActivityPub.fetch_public_activities()
785 assert activity == listed_activity
788 test "retrieves public activities" do
789 _activities = ActivityPub.fetch_public_activities()
791 %{public: public} = ActivityBuilder.public_and_non_public()
793 activities = ActivityPub.fetch_public_activities()
794 assert length(activities) == 1
795 assert Enum.at(activities, 0) == public
798 test "retrieves a maximum of 20 activities" do
799 ActivityBuilder.insert_list(10)
800 expected_activities = ActivityBuilder.insert_list(20)
802 activities = ActivityPub.fetch_public_activities()
804 assert collect_ids(activities) == collect_ids(expected_activities)
805 assert length(activities) == 20
808 test "retrieves ids starting from a since_id" do
809 activities = ActivityBuilder.insert_list(30)
810 expected_activities = ActivityBuilder.insert_list(10)
811 since_id = List.last(activities).id
813 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
815 assert collect_ids(activities) == collect_ids(expected_activities)
816 assert length(activities) == 10
819 test "retrieves ids up to max_id" do
820 ActivityBuilder.insert_list(10)
821 expected_activities = ActivityBuilder.insert_list(20)
825 |> ActivityBuilder.insert_list()
828 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
830 assert length(activities) == 20
831 assert collect_ids(activities) == collect_ids(expected_activities)
834 test "paginates via offset/limit" do
835 _first_part_activities = ActivityBuilder.insert_list(10)
836 second_part_activities = ActivityBuilder.insert_list(10)
838 later_activities = ActivityBuilder.insert_list(10)
841 ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
843 assert length(activities) == 20
845 assert collect_ids(activities) ==
846 collect_ids(second_part_activities) ++ collect_ids(later_activities)
849 test "doesn't return reblogs for users for whom reblogs have been muted" do
850 activity = insert(:note_activity)
852 booster = insert(:user)
853 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
855 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
857 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
859 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
862 test "returns reblogs for users for whom reblogs have not been muted" do
863 activity = insert(:note_activity)
865 booster = insert(:user)
866 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
867 {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
869 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
871 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
873 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
877 describe "react to an object" do
878 test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
879 Config.put([:instance, :federating], true)
881 reactor = insert(:user)
882 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
883 assert object = Object.normalize(activity)
885 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
887 assert called(Federator.publish(reaction_activity))
890 test "adds an emoji reaction activity to the db" do
892 reactor = insert(:user)
893 third_user = insert(:user)
894 fourth_user = insert(:user)
895 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
896 assert object = Object.normalize(activity)
898 {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
900 assert reaction_activity
902 assert reaction_activity.data["actor"] == reactor.ap_id
903 assert reaction_activity.data["type"] == "EmojiReact"
904 assert reaction_activity.data["content"] == "🔥"
905 assert reaction_activity.data["object"] == object.data["id"]
906 assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
907 assert reaction_activity.data["context"] == object.data["context"]
908 assert object.data["reaction_count"] == 1
909 assert object.data["reactions"] == [["🔥", [reactor.ap_id]]]
911 {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(third_user, object, "☕")
913 assert object.data["reaction_count"] == 2
914 assert object.data["reactions"] == [["🔥", [reactor.ap_id]], ["☕", [third_user.ap_id]]]
916 {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(fourth_user, object, "🔥")
918 assert object.data["reaction_count"] == 3
920 assert object.data["reactions"] == [
921 ["🔥", [fourth_user.ap_id, reactor.ap_id]],
922 ["☕", [third_user.ap_id]]
926 test "reverts emoji reaction on error" do
927 [user, reactor] = insert_list(2, :user)
929 {:ok, activity} = CommonAPI.post(user, %{"status" => "Status"})
930 object = Object.normalize(activity)
932 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
933 assert {:error, :reverted} = ActivityPub.react_with_emoji(reactor, object, "😀")
936 object = Object.get_by_ap_id(object.data["id"])
937 refute object.data["reaction_count"]
938 refute object.data["reactions"]
942 describe "unreacting to an object" do
943 test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
944 Config.put([:instance, :federating], true)
946 reactor = insert(:user)
947 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
948 assert object = Object.normalize(activity)
950 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
952 assert called(Federator.publish(reaction_activity))
954 {:ok, unreaction_activity, _object} =
955 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
957 assert called(Federator.publish(unreaction_activity))
960 test "adds an undo activity to the db" do
962 reactor = insert(:user)
963 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
964 assert object = Object.normalize(activity)
966 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
968 {:ok, unreaction_activity, _object} =
969 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
971 assert unreaction_activity.actor == reactor.ap_id
972 assert unreaction_activity.data["object"] == reaction_activity.data["id"]
974 object = Object.get_by_ap_id(object.data["id"])
975 assert object.data["reaction_count"] == 0
976 assert object.data["reactions"] == []
979 test "reverts emoji unreact on error" do
980 [user, reactor] = insert_list(2, :user)
981 {:ok, activity} = CommonAPI.post(user, %{"status" => "Status"})
982 object = Object.normalize(activity)
984 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "😀")
986 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
987 assert {:error, :reverted} =
988 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
991 object = Object.get_by_ap_id(object.data["id"])
993 assert object.data["reaction_count"] == 1
994 assert object.data["reactions"] == [["😀", [reactor.ap_id]]]
998 describe "unliking" do
999 test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
1000 Config.put([:instance, :federating], true)
1002 note_activity = insert(:note_activity)
1003 object = Object.normalize(note_activity)
1004 user = insert(:user)
1006 {:ok, object} = ActivityPub.unlike(user, object)
1007 refute called(Federator.publish())
1009 {:ok, _like_activity} = CommonAPI.favorite(user, note_activity.id)
1010 object = Object.get_by_id(object.id)
1011 assert object.data["like_count"] == 1
1013 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
1014 assert object.data["like_count"] == 0
1016 assert called(Federator.publish(unlike_activity))
1019 test "reverts unliking on error" do
1020 note_activity = insert(:note_activity)
1021 user = insert(:user)
1023 {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
1024 object = Object.normalize(note_activity)
1025 assert object.data["like_count"] == 1
1027 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1028 assert {:error, :reverted} = ActivityPub.unlike(user, object)
1031 assert Object.get_by_ap_id(object.data["id"]) == object
1032 assert object.data["like_count"] == 1
1033 assert Activity.get_by_id(like_activity.id)
1036 test "unliking a previously liked object" do
1037 note_activity = insert(:note_activity)
1038 object = Object.normalize(note_activity)
1039 user = insert(:user)
1041 # Unliking something that hasn't been liked does nothing
1042 {:ok, object} = ActivityPub.unlike(user, object)
1043 assert object.data["like_count"] == 0
1045 {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
1047 object = Object.get_by_id(object.id)
1048 assert object.data["like_count"] == 1
1050 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
1051 assert object.data["like_count"] == 0
1053 assert Activity.get_by_id(like_activity.id) == nil
1054 assert note_activity.actor in unlike_activity.recipients
1058 describe "announcing an object" do
1059 test "adds an announce activity to the db" do
1060 note_activity = insert(:note_activity)
1061 object = Object.normalize(note_activity)
1062 user = insert(:user)
1064 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1065 assert object.data["announcement_count"] == 1
1066 assert object.data["announcements"] == [user.ap_id]
1068 assert announce_activity.data["to"] == [
1069 User.ap_followers(user),
1070 note_activity.data["actor"]
1073 assert announce_activity.data["object"] == object.data["id"]
1074 assert announce_activity.data["actor"] == user.ap_id
1075 assert announce_activity.data["context"] == object.data["context"]
1078 test "reverts annouce from object on error" do
1079 note_activity = insert(:note_activity)
1080 object = Object.normalize(note_activity)
1081 user = insert(:user)
1083 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1084 assert {:error, :reverted} = ActivityPub.announce(user, object)
1087 reloaded_object = Object.get_by_ap_id(object.data["id"])
1088 assert reloaded_object == object
1089 refute reloaded_object.data["announcement_count"]
1090 refute reloaded_object.data["announcements"]
1094 describe "announcing a private object" do
1095 test "adds an announce activity to the db if the audience is not widened" do
1096 user = insert(:user)
1097 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1098 object = Object.normalize(note_activity)
1100 {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
1102 assert announce_activity.data["to"] == [User.ap_followers(user)]
1104 assert announce_activity.data["object"] == object.data["id"]
1105 assert announce_activity.data["actor"] == user.ap_id
1106 assert announce_activity.data["context"] == object.data["context"]
1109 test "does not add an announce activity to the db if the audience is widened" do
1110 user = insert(:user)
1111 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1112 object = Object.normalize(note_activity)
1114 assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
1117 test "does not add an announce activity to the db if the announcer is not the author" do
1118 user = insert(:user)
1119 announcer = insert(:user)
1120 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1121 object = Object.normalize(note_activity)
1123 assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
1127 describe "unannouncing an object" do
1128 test "unannouncing a previously announced object" do
1129 note_activity = insert(:note_activity)
1130 object = Object.normalize(note_activity)
1131 user = insert(:user)
1133 # Unannouncing an object that is not announced does nothing
1134 {:ok, object} = ActivityPub.unannounce(user, object)
1135 refute object.data["announcement_count"]
1137 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1138 assert object.data["announcement_count"] == 1
1140 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
1141 assert object.data["announcement_count"] == 0
1143 assert unannounce_activity.data["to"] == [
1144 User.ap_followers(user),
1145 object.data["actor"]
1148 assert unannounce_activity.data["type"] == "Undo"
1149 assert unannounce_activity.data["object"] == announce_activity.data
1150 assert unannounce_activity.data["actor"] == user.ap_id
1151 assert unannounce_activity.data["context"] == announce_activity.data["context"]
1153 assert Activity.get_by_id(announce_activity.id) == nil
1156 test "reverts unannouncing on error" do
1157 note_activity = insert(:note_activity)
1158 object = Object.normalize(note_activity)
1159 user = insert(:user)
1161 {:ok, _announce_activity, object} = ActivityPub.announce(user, object)
1162 assert object.data["announcement_count"] == 1
1164 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1165 assert {:error, :reverted} = ActivityPub.unannounce(user, object)
1168 object = Object.get_by_ap_id(object.data["id"])
1169 assert object.data["announcement_count"] == 1
1173 describe "uploading files" do
1174 test "copies the file to the configured folder" do
1175 file = %Plug.Upload{
1176 content_type: "image/jpg",
1177 path: Path.absname("test/fixtures/image.jpg"),
1178 filename: "an_image.jpg"
1181 {:ok, %Object{} = object} = ActivityPub.upload(file)
1182 assert object.data["name"] == "an_image.jpg"
1185 test "works with base64 encoded images" do
1190 {:ok, %Object{}} = ActivityPub.upload(file)
1194 describe "fetch the latest Follow" do
1195 test "fetches the latest Follow activity" do
1196 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1197 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1198 followed = Repo.get_by(User, ap_id: activity.data["object"])
1200 assert activity == Utils.fetch_latest_follow(follower, followed)
1204 describe "following / unfollowing" do
1205 test "it reverts follow activity" do
1206 follower = insert(:user)
1207 followed = insert(:user)
1209 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1210 assert {:error, :reverted} = ActivityPub.follow(follower, followed)
1213 assert Repo.aggregate(Activity, :count, :id) == 0
1214 assert Repo.aggregate(Object, :count, :id) == 0
1217 test "it reverts unfollow activity" do
1218 follower = insert(:user)
1219 followed = insert(:user)
1221 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1223 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1224 assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
1227 activity = Activity.get_by_id(follow_activity.id)
1228 assert activity.data["type"] == "Follow"
1229 assert activity.data["actor"] == follower.ap_id
1231 assert activity.data["object"] == followed.ap_id
1234 test "creates a follow activity" do
1235 follower = insert(:user)
1236 followed = insert(:user)
1238 {:ok, activity} = ActivityPub.follow(follower, followed)
1239 assert activity.data["type"] == "Follow"
1240 assert activity.data["actor"] == follower.ap_id
1241 assert activity.data["object"] == followed.ap_id
1244 test "creates an undo activity for the last follow" do
1245 follower = insert(:user)
1246 followed = insert(:user)
1248 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1249 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1251 assert activity.data["type"] == "Undo"
1252 assert activity.data["actor"] == follower.ap_id
1254 embedded_object = activity.data["object"]
1255 assert is_map(embedded_object)
1256 assert embedded_object["type"] == "Follow"
1257 assert embedded_object["object"] == followed.ap_id
1258 assert embedded_object["id"] == follow_activity.data["id"]
1261 test "creates an undo activity for a pending follow request" do
1262 follower = insert(:user)
1263 followed = insert(:user, %{locked: true})
1265 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1266 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1268 assert activity.data["type"] == "Undo"
1269 assert activity.data["actor"] == follower.ap_id
1271 embedded_object = activity.data["object"]
1272 assert is_map(embedded_object)
1273 assert embedded_object["type"] == "Follow"
1274 assert embedded_object["object"] == followed.ap_id
1275 assert embedded_object["id"] == follow_activity.data["id"]
1279 describe "blocking / unblocking" do
1280 test "reverts block activity on error" do
1281 [blocker, blocked] = insert_list(2, :user)
1283 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1284 assert {:error, :reverted} = ActivityPub.block(blocker, blocked)
1287 assert Repo.aggregate(Activity, :count, :id) == 0
1288 assert Repo.aggregate(Object, :count, :id) == 0
1291 test "creates a block activity" do
1292 blocker = insert(:user)
1293 blocked = insert(:user)
1295 {:ok, activity} = ActivityPub.block(blocker, blocked)
1297 assert activity.data["type"] == "Block"
1298 assert activity.data["actor"] == blocker.ap_id
1299 assert activity.data["object"] == blocked.ap_id
1302 test "reverts unblock activity on error" do
1303 [blocker, blocked] = insert_list(2, :user)
1304 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1306 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1307 assert {:error, :reverted} = ActivityPub.unblock(blocker, blocked)
1310 assert block_activity.data["type"] == "Block"
1311 assert block_activity.data["actor"] == blocker.ap_id
1313 assert Repo.aggregate(Activity, :count, :id) == 1
1314 assert Repo.aggregate(Object, :count, :id) == 1
1317 test "creates an undo activity for the last block" do
1318 blocker = insert(:user)
1319 blocked = insert(:user)
1321 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1322 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1324 assert activity.data["type"] == "Undo"
1325 assert activity.data["actor"] == blocker.ap_id
1327 embedded_object = activity.data["object"]
1328 assert is_map(embedded_object)
1329 assert embedded_object["type"] == "Block"
1330 assert embedded_object["object"] == blocked.ap_id
1331 assert embedded_object["id"] == block_activity.data["id"]
1335 describe "deletion" do
1336 setup do: clear_config([:instance, :rewrite_policy])
1338 test "it reverts deletion on error" do
1339 note = insert(:note_activity)
1340 object = Object.normalize(note)
1342 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1343 assert {:error, :reverted} = ActivityPub.delete(object)
1346 assert Repo.aggregate(Activity, :count, :id) == 1
1347 assert Repo.get(Object, object.id) == object
1348 assert Activity.get_by_id(note.id) == note
1351 test "it creates a delete activity and deletes the original object" do
1352 note = insert(:note_activity)
1353 object = Object.normalize(note)
1354 {:ok, delete} = ActivityPub.delete(object)
1356 assert delete.data["type"] == "Delete"
1357 assert delete.data["actor"] == note.data["actor"]
1358 assert delete.data["object"] == object.data["id"]
1360 assert Activity.get_by_id(delete.id) != nil
1362 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1365 test "it doesn't fail when an activity was already deleted" do
1366 {:ok, delete} = insert(:note_activity) |> Object.normalize() |> ActivityPub.delete()
1368 assert {:ok, ^delete} = delete |> Object.normalize() |> ActivityPub.delete()
1371 test "decrements user note count only for public activities" do
1372 user = insert(:user, note_count: 10)
1375 CommonAPI.post(User.get_cached_by_id(user.id), %{
1377 "visibility" => "public"
1381 CommonAPI.post(User.get_cached_by_id(user.id), %{
1383 "visibility" => "unlisted"
1387 CommonAPI.post(User.get_cached_by_id(user.id), %{
1389 "visibility" => "private"
1393 CommonAPI.post(User.get_cached_by_id(user.id), %{
1395 "visibility" => "direct"
1398 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1399 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1400 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1401 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1403 user = User.get_cached_by_id(user.id)
1404 assert user.note_count == 10
1407 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1408 user = insert(:user)
1409 note = insert(:note_activity)
1410 object = Object.normalize(note)
1416 "actor" => object.data["actor"],
1417 "id" => object.data["id"],
1418 "to" => [user.ap_id],
1422 |> Object.update_and_set_cache()
1424 {:ok, delete} = ActivityPub.delete(object)
1426 assert user.ap_id in delete.data["to"]
1429 test "decreases reply count" do
1430 user = insert(:user)
1431 user2 = insert(:user)
1433 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1434 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1435 ap_id = activity.data["id"]
1437 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1438 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1439 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1440 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1442 _ = CommonAPI.delete(direct_reply.id, user2)
1443 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1444 assert object.data["repliesCount"] == 2
1446 _ = CommonAPI.delete(private_reply.id, user2)
1447 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1448 assert object.data["repliesCount"] == 2
1450 _ = CommonAPI.delete(public_reply.id, user2)
1451 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1452 assert object.data["repliesCount"] == 1
1454 _ = CommonAPI.delete(unlisted_reply.id, user2)
1455 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1456 assert object.data["repliesCount"] == 0
1459 test "it passes delete activity through MRF before deleting the object" do
1460 Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
1462 note = insert(:note_activity)
1463 object = Object.normalize(note)
1465 {:error, {:reject, _}} = ActivityPub.delete(object)
1467 assert Activity.get_by_id(note.id)
1468 assert Repo.get(Object, object.id).data["type"] == object.data["type"]
1472 describe "timeline post-processing" do
1473 test "it filters broken threads" do
1474 user1 = insert(:user)
1475 user2 = insert(:user)
1476 user3 = insert(:user)
1478 {:ok, user1} = User.follow(user1, user3)
1479 assert User.following?(user1, user3)
1481 {:ok, user2} = User.follow(user2, user3)
1482 assert User.following?(user2, user3)
1484 {:ok, user3} = User.follow(user3, user2)
1485 assert User.following?(user3, user2)
1487 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1489 {:ok, private_activity_1} =
1490 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1492 {:ok, private_activity_2} =
1493 CommonAPI.post(user2, %{
1495 "visibility" => "private",
1496 "in_reply_to_status_id" => private_activity_1.id
1499 {:ok, private_activity_3} =
1500 CommonAPI.post(user3, %{
1502 "visibility" => "private",
1503 "in_reply_to_status_id" => private_activity_2.id
1507 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1508 |> Enum.map(fn a -> a.id end)
1510 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1512 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1514 assert length(activities) == 3
1517 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1518 |> Enum.map(fn a -> a.id end)
1520 assert [public_activity.id, private_activity_1.id] == activities
1521 assert length(activities) == 2
1525 describe "update" do
1526 setup do: clear_config([:instance, :max_pinned_statuses])
1528 test "it creates an update activity with the new user data" do
1529 user = insert(:user)
1530 {:ok, user} = User.ensure_keys_present(user)
1531 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1534 ActivityPub.update(%{
1535 actor: user_data["id"],
1536 to: [user.follower_address],
1541 assert update.data["actor"] == user.ap_id
1542 assert update.data["to"] == [user.follower_address]
1543 assert embedded_object = update.data["object"]
1544 assert embedded_object["id"] == user_data["id"]
1545 assert embedded_object["type"] == user_data["type"]
1549 test "returned pinned statuses" do
1550 Config.put([:instance, :max_pinned_statuses], 3)
1551 user = insert(:user)
1553 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1554 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1555 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1557 CommonAPI.pin(activity_one.id, user)
1558 user = refresh_record(user)
1560 CommonAPI.pin(activity_two.id, user)
1561 user = refresh_record(user)
1563 CommonAPI.pin(activity_three.id, user)
1564 user = refresh_record(user)
1566 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1568 assert 3 = length(activities)
1571 describe "flag/1" do
1573 reporter = insert(:user)
1574 target_account = insert(:user)
1576 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1577 context = Utils.generate_context_id()
1579 reporter_ap_id = reporter.ap_id
1580 target_ap_id = target_account.ap_id
1581 activity_ap_id = activity.data["id"]
1583 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1589 target_account: target_account,
1590 reported_activity: activity,
1592 activity_ap_id: activity_ap_id,
1593 activity_with_object: activity_with_object,
1594 reporter_ap_id: reporter_ap_id,
1595 target_ap_id: target_ap_id
1599 test "it can create a Flag activity",
1603 target_account: target_account,
1604 reported_activity: reported_activity,
1606 activity_ap_id: activity_ap_id,
1607 activity_with_object: activity_with_object,
1608 reporter_ap_id: reporter_ap_id,
1609 target_ap_id: target_ap_id
1611 assert {:ok, activity} =
1615 account: target_account,
1616 statuses: [reported_activity],
1622 "id" => activity_ap_id,
1623 "content" => content,
1624 "published" => activity_with_object.object.data["published"],
1625 "actor" => AccountView.render("show.json", %{user: target_account})
1629 actor: ^reporter_ap_id,
1632 "content" => ^content,
1633 "context" => ^context,
1634 "object" => [^target_ap_id, ^note_obj]
1639 test_with_mock "strips status data from Flag, before federating it",
1643 target_account: target_account,
1644 reported_activity: reported_activity,
1654 account: target_account,
1655 statuses: [reported_activity],
1660 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1662 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1666 test "fetch_activities/2 returns activities addressed to a list " do
1667 user = insert(:user)
1668 member = insert(:user)
1669 {:ok, list} = Pleroma.List.create("foo", user)
1670 {:ok, list} = Pleroma.List.follow(list, member)
1673 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1675 activity = Repo.preload(activity, :bookmark)
1676 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1678 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1682 File.read!("test/fixtures/avatar_data_uri")
1685 describe "fetch_activities_bounded" do
1686 test "fetches private posts for followed users" do
1687 user = insert(:user)
1690 CommonAPI.post(user, %{
1691 "status" => "thought I looked cute might delete later :3",
1692 "visibility" => "private"
1695 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1696 assert result.id == activity.id
1699 test "fetches only public posts for other users" do
1700 user = insert(:user)
1701 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1703 {:ok, _private_activity} =
1704 CommonAPI.post(user, %{
1705 "status" => "why is tenshi eating a corndog so cute?",
1706 "visibility" => "private"
1709 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1710 assert result.id == activity.id
1714 describe "fetch_follow_information_for_user" do
1715 test "syncronizes following/followers counters" do
1719 follower_address: "http://localhost:4001/users/fuser2/followers",
1720 following_address: "http://localhost:4001/users/fuser2/following"
1723 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1724 assert info.follower_count == 527
1725 assert info.following_count == 267
1728 test "detects hidden followers" do
1731 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1732 %Tesla.Env{status: 403, body: ""}
1735 apply(HttpRequestMock, :request, [env])
1742 follower_address: "http://localhost:4001/users/masto_closed/followers",
1743 following_address: "http://localhost:4001/users/masto_closed/following"
1746 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1747 assert follow_info.hide_followers == true
1748 assert follow_info.hide_follows == false
1751 test "detects hidden follows" do
1754 "http://localhost:4001/users/masto_closed/following?page=1" ->
1755 %Tesla.Env{status: 403, body: ""}
1758 apply(HttpRequestMock, :request, [env])
1765 follower_address: "http://localhost:4001/users/masto_closed/followers",
1766 following_address: "http://localhost:4001/users/masto_closed/following"
1769 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1770 assert follow_info.hide_followers == false
1771 assert follow_info.hide_follows == true
1774 test "detects hidden follows/followers for friendica" do
1778 follower_address: "http://localhost:8080/followers/fuser3",
1779 following_address: "http://localhost:8080/following/fuser3"
1782 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1783 assert follow_info.hide_followers == true
1784 assert follow_info.follower_count == 296
1785 assert follow_info.following_count == 32
1786 assert follow_info.hide_follows == true
1789 test "doesn't crash when follower and following counters are hidden" do
1792 "http://localhost:4001/users/masto_hidden_counters/following" ->
1794 "@context" => "https://www.w3.org/ns/activitystreams",
1795 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1798 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1799 %Tesla.Env{status: 403, body: ""}
1801 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1803 "@context" => "https://www.w3.org/ns/activitystreams",
1804 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1807 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1808 %Tesla.Env{status: 403, body: ""}
1815 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1816 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1819 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1821 assert follow_info.hide_followers == true
1822 assert follow_info.follower_count == 0
1823 assert follow_info.hide_follows == true
1824 assert follow_info.following_count == 0
1828 describe "fetch_favourites/3" do
1829 test "returns a favourite activities sorted by adds to favorite" do
1830 user = insert(:user)
1831 other_user = insert(:user)
1832 user1 = insert(:user)
1833 user2 = insert(:user)
1834 {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
1835 {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
1836 {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
1837 {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
1838 {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
1840 {:ok, _} = CommonAPI.favorite(user, a4.id)
1841 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1842 {:ok, _} = CommonAPI.favorite(user, a3.id)
1843 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1844 {:ok, _} = CommonAPI.favorite(user, a5.id)
1845 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1846 {:ok, _} = CommonAPI.favorite(user, a1.id)
1847 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1848 result = ActivityPub.fetch_favourites(user)
1850 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1852 result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
1853 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1857 describe "Move activity" do
1859 %{ap_id: old_ap_id} = old_user = insert(:user)
1860 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1861 follower = insert(:user)
1862 follower_move_opted_out = insert(:user, allow_following_move: false)
1864 User.follow(follower, old_user)
1865 User.follow(follower_move_opted_out, old_user)
1867 assert User.following?(follower, old_user)
1868 assert User.following?(follower_move_opted_out, old_user)
1870 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1875 "actor" => ^old_ap_id,
1876 "object" => ^old_ap_id,
1877 "target" => ^new_ap_id,
1884 "op" => "move_following",
1885 "origin_id" => old_user.id,
1886 "target_id" => new_user.id
1889 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1891 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1893 refute User.following?(follower, old_user)
1894 assert User.following?(follower, new_user)
1896 assert User.following?(follower_move_opted_out, old_user)
1897 refute User.following?(follower_move_opted_out, new_user)
1899 activity = %Activity{activity | object: nil}
1901 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1903 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1906 test "old user must be in the new user's `also_known_as` list" do
1907 old_user = insert(:user)
1908 new_user = insert(:user)
1910 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1911 ActivityPub.move(old_user, new_user)
1915 test "doesn't retrieve replies activities with exclude_replies" do
1916 user = insert(:user)
1918 {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
1921 CommonAPI.post(user, %{"status" => "yeah", "in_reply_to_status_id" => activity.id})
1923 [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"})
1925 assert result.id == activity.id
1927 assert length(ActivityPub.fetch_public_activities()) == 2
1930 describe "replies filtering with public messages" do
1931 setup :public_messages
1933 test "public timeline", %{users: %{u1: user}} do
1936 |> Map.put("type", ["Create", "Announce"])
1937 |> Map.put("local_only", false)
1938 |> Map.put("blocking_user", user)
1939 |> Map.put("muting_user", user)
1940 |> Map.put("reply_filtering_user", user)
1941 |> ActivityPub.fetch_public_activities()
1942 |> Enum.map(& &1.id)
1944 assert length(activities_ids) == 16
1947 test "public timeline with reply_visibility `following`", %{
1953 activities: activities
1957 |> Map.put("type", ["Create", "Announce"])
1958 |> Map.put("local_only", false)
1959 |> Map.put("blocking_user", user)
1960 |> Map.put("muting_user", user)
1961 |> Map.put("reply_visibility", "following")
1962 |> Map.put("reply_filtering_user", user)
1963 |> ActivityPub.fetch_public_activities()
1964 |> Enum.map(& &1.id)
1966 assert length(activities_ids) == 14
1969 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1971 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1974 test "public timeline with reply_visibility `self`", %{
1980 activities: activities
1984 |> Map.put("type", ["Create", "Announce"])
1985 |> Map.put("local_only", false)
1986 |> Map.put("blocking_user", user)
1987 |> Map.put("muting_user", user)
1988 |> Map.put("reply_visibility", "self")
1989 |> Map.put("reply_filtering_user", user)
1990 |> ActivityPub.fetch_public_activities()
1991 |> Enum.map(& &1.id)
1993 assert length(activities_ids) == 10
1994 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1995 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1998 test "home timeline", %{
2000 activities: activities,
2008 |> Map.put("type", ["Create", "Announce"])
2009 |> Map.put("blocking_user", user)
2010 |> Map.put("muting_user", user)
2011 |> Map.put("user", user)
2012 |> Map.put("reply_filtering_user", user)
2015 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2016 |> Enum.map(& &1.id)
2018 assert length(activities_ids) == 13
2033 assert Enum.all?(visible_ids, &(&1 in activities_ids))
2036 test "home timeline with reply_visibility `following`", %{
2038 activities: activities,
2046 |> Map.put("type", ["Create", "Announce"])
2047 |> Map.put("blocking_user", user)
2048 |> Map.put("muting_user", user)
2049 |> Map.put("user", user)
2050 |> Map.put("reply_visibility", "following")
2051 |> Map.put("reply_filtering_user", user)
2054 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2055 |> Enum.map(& &1.id)
2057 assert length(activities_ids) == 11
2072 assert Enum.all?(visible_ids, &(&1 in activities_ids))
2075 test "home timeline with reply_visibility `self`", %{
2077 activities: activities,
2085 |> Map.put("type", ["Create", "Announce"])
2086 |> Map.put("blocking_user", user)
2087 |> Map.put("muting_user", user)
2088 |> Map.put("user", user)
2089 |> Map.put("reply_visibility", "self")
2090 |> Map.put("reply_filtering_user", user)
2093 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2094 |> Enum.map(& &1.id)
2096 assert length(activities_ids) == 9
2109 assert Enum.all?(visible_ids, &(&1 in activities_ids))
2113 describe "replies filtering with private messages" do
2114 setup :private_messages
2116 test "public timeline", %{users: %{u1: user}} do
2119 |> Map.put("type", ["Create", "Announce"])
2120 |> Map.put("local_only", false)
2121 |> Map.put("blocking_user", user)
2122 |> Map.put("muting_user", user)
2123 |> Map.put("user", user)
2124 |> ActivityPub.fetch_public_activities()
2125 |> Enum.map(& &1.id)
2127 assert activities_ids == []
2130 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
2133 |> Map.put("type", ["Create", "Announce"])
2134 |> Map.put("local_only", false)
2135 |> Map.put("blocking_user", user)
2136 |> Map.put("muting_user", user)
2137 |> Map.put("reply_visibility", "following")
2138 |> Map.put("reply_filtering_user", user)
2139 |> Map.put("user", user)
2140 |> ActivityPub.fetch_public_activities()
2141 |> Enum.map(& &1.id)
2143 assert activities_ids == []
2146 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
2149 |> Map.put("type", ["Create", "Announce"])
2150 |> Map.put("local_only", false)
2151 |> Map.put("blocking_user", user)
2152 |> Map.put("muting_user", user)
2153 |> Map.put("reply_visibility", "self")
2154 |> Map.put("reply_filtering_user", user)
2155 |> Map.put("user", user)
2156 |> ActivityPub.fetch_public_activities()
2157 |> Enum.map(& &1.id)
2159 assert activities_ids == []
2162 test "home timeline", %{users: %{u1: user}} do
2165 |> Map.put("type", ["Create", "Announce"])
2166 |> Map.put("blocking_user", user)
2167 |> Map.put("muting_user", user)
2168 |> Map.put("user", user)
2171 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2172 |> Enum.map(& &1.id)
2174 assert length(activities_ids) == 12
2177 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
2180 |> Map.put("type", ["Create", "Announce"])
2181 |> Map.put("blocking_user", user)
2182 |> Map.put("muting_user", user)
2183 |> Map.put("user", user)
2184 |> Map.put("reply_visibility", "following")
2185 |> Map.put("reply_filtering_user", user)
2188 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2189 |> Enum.map(& &1.id)
2191 assert length(activities_ids) == 12
2194 test "home timeline with default reply_visibility `self`", %{
2196 activities: activities,
2204 |> Map.put("type", ["Create", "Announce"])
2205 |> Map.put("blocking_user", user)
2206 |> Map.put("muting_user", user)
2207 |> Map.put("user", user)
2208 |> Map.put("reply_visibility", "self")
2209 |> Map.put("reply_filtering_user", user)
2212 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2213 |> Enum.map(& &1.id)
2215 assert length(activities_ids) == 10
2218 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
2220 assert Enum.all?(visible_ids, &(&1 in activities_ids))
2224 defp public_messages(_) do
2225 [u1, u2, u3, u4] = insert_list(4, :user)
2226 {:ok, u1} = User.follow(u1, u2)
2227 {:ok, u2} = User.follow(u2, u1)
2228 {:ok, u1} = User.follow(u1, u4)
2229 {:ok, u4} = User.follow(u4, u1)
2231 {:ok, u2} = User.follow(u2, u3)
2232 {:ok, u3} = User.follow(u3, u2)
2234 {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status"})
2237 CommonAPI.post(u2, %{
2238 "status" => "@#{u1.nickname} reply from u2 to u1",
2239 "in_reply_to_status_id" => a1.id
2243 CommonAPI.post(u3, %{
2244 "status" => "@#{u1.nickname} reply from u3 to u1",
2245 "in_reply_to_status_id" => a1.id
2249 CommonAPI.post(u4, %{
2250 "status" => "@#{u1.nickname} reply from u4 to u1",
2251 "in_reply_to_status_id" => a1.id
2254 {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status"})
2257 CommonAPI.post(u1, %{
2258 "status" => "@#{u2.nickname} reply from u1 to u2",
2259 "in_reply_to_status_id" => a2.id
2263 CommonAPI.post(u3, %{
2264 "status" => "@#{u2.nickname} reply from u3 to u2",
2265 "in_reply_to_status_id" => a2.id
2269 CommonAPI.post(u4, %{
2270 "status" => "@#{u2.nickname} reply from u4 to u2",
2271 "in_reply_to_status_id" => a2.id
2274 {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status"})
2277 CommonAPI.post(u1, %{
2278 "status" => "@#{u3.nickname} reply from u1 to u3",
2279 "in_reply_to_status_id" => a3.id
2283 CommonAPI.post(u2, %{
2284 "status" => "@#{u3.nickname} reply from u2 to u3",
2285 "in_reply_to_status_id" => a3.id
2289 CommonAPI.post(u4, %{
2290 "status" => "@#{u3.nickname} reply from u4 to u3",
2291 "in_reply_to_status_id" => a3.id
2294 {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status"})
2297 CommonAPI.post(u1, %{
2298 "status" => "@#{u4.nickname} reply from u1 to u4",
2299 "in_reply_to_status_id" => a4.id
2303 CommonAPI.post(u2, %{
2304 "status" => "@#{u4.nickname} reply from u2 to u4",
2305 "in_reply_to_status_id" => a4.id
2309 CommonAPI.post(u3, %{
2310 "status" => "@#{u4.nickname} reply from u3 to u4",
2311 "in_reply_to_status_id" => a4.id
2315 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
2316 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
2317 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
2318 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
2319 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
2320 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
2323 defp private_messages(_) do
2324 [u1, u2, u3, u4] = insert_list(4, :user)
2325 {:ok, u1} = User.follow(u1, u2)
2326 {:ok, u2} = User.follow(u2, u1)
2327 {:ok, u1} = User.follow(u1, u3)
2328 {:ok, u3} = User.follow(u3, u1)
2329 {:ok, u1} = User.follow(u1, u4)
2330 {:ok, u4} = User.follow(u4, u1)
2332 {:ok, u2} = User.follow(u2, u3)
2333 {:ok, u3} = User.follow(u3, u2)
2335 {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status", "visibility" => "private"})
2338 CommonAPI.post(u2, %{
2339 "status" => "@#{u1.nickname} reply from u2 to u1",
2340 "in_reply_to_status_id" => a1.id,
2341 "visibility" => "private"
2345 CommonAPI.post(u3, %{
2346 "status" => "@#{u1.nickname} reply from u3 to u1",
2347 "in_reply_to_status_id" => a1.id,
2348 "visibility" => "private"
2352 CommonAPI.post(u4, %{
2353 "status" => "@#{u1.nickname} reply from u4 to u1",
2354 "in_reply_to_status_id" => a1.id,
2355 "visibility" => "private"
2358 {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status", "visibility" => "private"})
2361 CommonAPI.post(u1, %{
2362 "status" => "@#{u2.nickname} reply from u1 to u2",
2363 "in_reply_to_status_id" => a2.id,
2364 "visibility" => "private"
2368 CommonAPI.post(u3, %{
2369 "status" => "@#{u2.nickname} reply from u3 to u2",
2370 "in_reply_to_status_id" => a2.id,
2371 "visibility" => "private"
2374 {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status", "visibility" => "private"})
2377 CommonAPI.post(u1, %{
2378 "status" => "@#{u3.nickname} reply from u1 to u3",
2379 "in_reply_to_status_id" => a3.id,
2380 "visibility" => "private"
2384 CommonAPI.post(u2, %{
2385 "status" => "@#{u3.nickname} reply from u2 to u3",
2386 "in_reply_to_status_id" => a3.id,
2387 "visibility" => "private"
2390 {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status", "visibility" => "private"})
2393 CommonAPI.post(u1, %{
2394 "status" => "@#{u4.nickname} reply from u1 to u4",
2395 "in_reply_to_status_id" => a4.id,
2396 "visibility" => "private"
2400 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
2401 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
2402 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
2403 u2: %{r1: r2_1.id, r2: r2_2.id},
2404 u3: %{r1: r3_1.id, r2: r3_2.id},
2408 describe "maybe_update_follow_information/1" do
2410 clear_config([:instance, :external_user_synchronization], true)
2414 ap_id: "https://gensokyo.2hu/users/raymoo",
2415 following_address: "https://gensokyo.2hu/users/following",
2416 follower_address: "https://gensokyo.2hu/users/followers",
2423 test "logs an error when it can't fetch the info", %{user: user} do
2424 assert capture_log(fn ->
2425 ActivityPub.maybe_update_follow_information(user)
2426 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2429 test "just returns the input if the user type is Application", %{
2434 |> Map.put(:type, "Application")
2436 refute capture_log(fn ->
2437 assert ^user = ActivityPub.maybe_update_follow_information(user)
2438 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2441 test "it just returns the input if the user has no following/follower addresses", %{
2446 |> Map.put(:following_address, nil)
2447 |> Map.put(:follower_address, nil)
2449 refute capture_log(fn ->
2450 assert ^user = ActivityPub.maybe_update_follow_information(user)
2451 end) =~ "Follower/Following counter update for #{user.ap_id} failed"