1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
8 alias Pleroma.Builders.ActivityBuilder
9 alias Pleroma.Instances
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.Utils
14 alias Pleroma.Web.CommonAPI
16 import Pleroma.Factory
21 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 describe "streaming out participations" do
26 test "it streams them out" do
28 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
30 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
33 conversation.participations
34 |> Repo.preload(:user)
36 with_mock Pleroma.Web.Streamer,
37 stream: fn _, _ -> nil end do
38 ActivityPub.stream_out_participations(conversation.participations)
40 Enum.each(participations, fn participation ->
41 assert called(Pleroma.Web.Streamer.stream("participation", participation))
47 describe "fetching restricted by visibility" do
48 test "it restricts by the appropriate visibility" do
51 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
53 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
55 {:ok, unlisted_activity} =
56 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
58 {:ok, private_activity} =
59 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
62 ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
64 assert activities == [direct_activity]
67 ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
69 assert activities == [unlisted_activity]
72 ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
74 assert activities == [private_activity]
77 ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
79 assert activities == [public_activity]
82 ActivityPub.fetch_activities([], %{
83 :visibility => ~w[private public],
84 "actor_id" => user.ap_id
87 assert activities == [public_activity, private_activity]
91 describe "building a user from his ap id" do
92 test "it returns a user" do
93 user_id = "http://mastodon.example.org/users/admin"
94 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
95 assert user.ap_id == user_id
96 assert user.nickname == "admin@mastodon.example.org"
97 assert user.info.source_data
98 assert user.info.ap_enabled
99 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
102 test "it fetches the appropriate tag-restricted posts" do
105 {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"})
106 {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
107 {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
109 fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"})
112 ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
115 ActivityPub.fetch_activities([], %{
117 "tag" => ["test", "essais"],
118 "tag_reject" => ["reject"]
122 ActivityPub.fetch_activities([], %{
125 "tag_all" => ["test", "reject"]
128 assert fetch_one == [status_one, status_three]
129 assert fetch_two == [status_one, status_two, status_three]
130 assert fetch_three == [status_one, status_two]
131 assert fetch_four == [status_three]
135 describe "insertion" do
136 test "drops activities beyond a certain limit" do
137 limit = Pleroma.Config.get([:instance, :remote_limit])
140 :crypto.strong_rand_bytes(limit + 1)
142 |> binary_part(0, limit + 1)
147 "content" => random_text
151 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
154 test "doesn't drop activities with content being null" do
158 "actor" => user.ap_id,
161 "actor" => user.ap_id,
168 assert {:ok, _} = ActivityPub.insert(data)
171 test "returns the activity if one with the same id is already in" do
172 activity = insert(:note_activity)
173 {:ok, new_activity} = ActivityPub.insert(activity.data)
175 assert activity.id == new_activity.id
178 test "inserts a given map into the activity database, giving it an id if it has none." do
182 "actor" => user.ap_id,
185 "actor" => user.ap_id,
192 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
193 assert activity.data["ok"] == data["ok"]
194 assert is_binary(activity.data["id"])
200 "actor" => user.ap_id,
202 "context" => "blabla",
204 "actor" => user.ap_id,
211 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
212 assert activity.data["ok"] == data["ok"]
213 assert activity.data["id"] == given_id
214 assert activity.data["context"] == "blabla"
215 assert activity.data["context_id"]
218 test "adds a context when none is there" do
222 "actor" => user.ap_id,
225 "actor" => user.ap_id,
232 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
233 object = Pleroma.Object.normalize(activity)
235 assert is_binary(activity.data["context"])
236 assert is_binary(object.data["context"])
237 assert activity.data["context_id"]
238 assert object.data["context_id"]
241 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
245 "actor" => user.ap_id,
248 "actor" => user.ap_id,
255 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
256 object = Object.normalize(activity.data["object"])
258 assert is_binary(object.data["id"])
259 assert %Object{} = Object.get_by_ap_id(activity.data["object"])
263 describe "create activities" do
264 test "removes doubled 'to' recipients" do
268 ActivityPub.create(%{
269 to: ["user1", "user1", "user2"],
273 "to" => ["user1", "user1", "user2"],
275 "content" => "testing"
279 assert activity.data["to"] == ["user1", "user2"]
280 assert activity.actor == user.ap_id
281 assert activity.recipients == ["user1", "user2", user.ap_id]
284 test "increases user note count only for public activities" do
288 CommonAPI.post(User.get_cached_by_id(user.id), %{
290 "visibility" => "public"
294 CommonAPI.post(User.get_cached_by_id(user.id), %{
296 "visibility" => "unlisted"
300 CommonAPI.post(User.get_cached_by_id(user.id), %{
302 "visibility" => "private"
306 CommonAPI.post(User.get_cached_by_id(user.id), %{
308 "visibility" => "direct"
311 user = User.get_cached_by_id(user.id)
312 assert user.info.note_count == 2
315 test "increases replies count" do
317 user2 = insert(:user)
319 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
320 ap_id = activity.data["id"]
321 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
324 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
325 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
326 assert object.data["repliesCount"] == 1
329 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
330 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
331 assert object.data["repliesCount"] == 2
334 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
335 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
336 assert object.data["repliesCount"] == 2
339 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
340 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
341 assert object.data["repliesCount"] == 2
345 describe "fetch activities for recipients" do
346 test "retrieve the activities for certain recipients" do
347 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
348 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
349 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
351 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
352 assert length(activities) == 2
353 assert activities == [activity_one, activity_two]
357 describe "fetch activities in context" do
358 test "retrieves activities that have a given context" do
359 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
360 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
361 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
362 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
363 activity_five = insert(:note_activity)
366 {:ok, user} = User.block(user, %{ap_id: activity_five.data["actor"]})
368 activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
369 assert activities == [activity_two, activity]
373 test "doesn't return blocked activities" do
374 activity_one = insert(:note_activity)
375 activity_two = insert(:note_activity)
376 activity_three = insert(:note_activity)
378 booster = insert(:user)
379 {:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
382 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
384 assert Enum.member?(activities, activity_two)
385 assert Enum.member?(activities, activity_three)
386 refute Enum.member?(activities, activity_one)
388 {:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
391 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
393 assert Enum.member?(activities, activity_two)
394 assert Enum.member?(activities, activity_three)
395 assert Enum.member?(activities, activity_one)
397 {:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
398 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
399 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
400 activity_three = Activity.get_by_id(activity_three.id)
403 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
405 assert Enum.member?(activities, activity_two)
406 refute Enum.member?(activities, activity_three)
407 refute Enum.member?(activities, boost_activity)
408 assert Enum.member?(activities, activity_one)
411 ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
413 assert Enum.member?(activities, activity_two)
414 assert Enum.member?(activities, activity_three)
415 assert Enum.member?(activities, boost_activity)
416 assert Enum.member?(activities, activity_one)
419 test "doesn't return transitive interactions concerning blocked users" do
420 blocker = insert(:user)
421 blockee = insert(:user)
422 friend = insert(:user)
424 {:ok, blocker} = User.block(blocker, blockee)
426 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
428 {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
430 {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
432 {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
434 activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
436 assert Enum.member?(activities, activity_one)
437 refute Enum.member?(activities, activity_two)
438 refute Enum.member?(activities, activity_three)
439 refute Enum.member?(activities, activity_four)
442 test "doesn't return announce activities concerning blocked users" do
443 blocker = insert(:user)
444 blockee = insert(:user)
445 friend = insert(:user)
447 {:ok, blocker} = User.block(blocker, blockee)
449 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
451 {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
453 {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
456 ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
457 |> Enum.map(fn act -> act.id end)
459 assert Enum.member?(activities, activity_one.id)
460 refute Enum.member?(activities, activity_two.id)
461 refute Enum.member?(activities, activity_three.id)
464 test "doesn't return muted activities" do
465 activity_one = insert(:note_activity)
466 activity_two = insert(:note_activity)
467 activity_three = insert(:note_activity)
469 booster = insert(:user)
470 {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
473 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
475 assert Enum.member?(activities, activity_two)
476 assert Enum.member?(activities, activity_three)
477 refute Enum.member?(activities, activity_one)
479 # Calling with 'with_muted' will deliver muted activities, too.
481 ActivityPub.fetch_activities([], %{
482 "muting_user" => user,
483 "with_muted" => true,
484 "skip_preload" => true
487 assert Enum.member?(activities, activity_two)
488 assert Enum.member?(activities, activity_three)
489 assert Enum.member?(activities, activity_one)
491 {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
494 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
496 assert Enum.member?(activities, activity_two)
497 assert Enum.member?(activities, activity_three)
498 assert Enum.member?(activities, activity_one)
500 {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
501 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
502 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
503 activity_three = Activity.get_by_id(activity_three.id)
506 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
508 assert Enum.member?(activities, activity_two)
509 refute Enum.member?(activities, activity_three)
510 refute Enum.member?(activities, boost_activity)
511 assert Enum.member?(activities, activity_one)
513 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
515 assert Enum.member?(activities, activity_two)
516 assert Enum.member?(activities, activity_three)
517 assert Enum.member?(activities, boost_activity)
518 assert Enum.member?(activities, activity_one)
521 test "does include announces on request" do
522 activity_three = insert(:note_activity)
524 booster = insert(:user)
526 {:ok, user} = User.follow(user, booster)
528 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
530 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following])
532 assert announce_activity.id == announce.id
535 test "excludes reblogs on request" do
537 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
538 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
540 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
542 assert activity == expected_activity
545 describe "public fetch activities" do
546 test "doesn't retrieve unlisted activities" do
549 {:ok, _unlisted_activity} =
550 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
552 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
554 [activity] = ActivityPub.fetch_public_activities()
556 assert activity == listed_activity
559 test "retrieves public activities" do
560 _activities = ActivityPub.fetch_public_activities()
562 %{public: public} = ActivityBuilder.public_and_non_public()
564 activities = ActivityPub.fetch_public_activities()
565 assert length(activities) == 1
566 assert Enum.at(activities, 0) == public
569 test "retrieves a maximum of 20 activities" do
570 activities = ActivityBuilder.insert_list(30)
571 last_expected = List.last(activities)
573 activities = ActivityPub.fetch_public_activities()
574 last = List.last(activities)
576 assert length(activities) == 20
577 assert last == last_expected
580 test "retrieves ids starting from a since_id" do
581 activities = ActivityBuilder.insert_list(30)
582 later_activities = ActivityBuilder.insert_list(10)
583 since_id = List.last(activities).id
584 last_expected = List.last(later_activities)
586 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
587 last = List.last(activities)
589 assert length(activities) == 10
590 assert last == last_expected
593 test "retrieves ids up to max_id" do
594 _first_activities = ActivityBuilder.insert_list(10)
595 activities = ActivityBuilder.insert_list(20)
596 later_activities = ActivityBuilder.insert_list(10)
597 max_id = List.first(later_activities).id
598 last_expected = List.last(activities)
600 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
601 last = List.last(activities)
603 assert length(activities) == 20
604 assert last == last_expected
607 test "doesn't return reblogs for users for whom reblogs have been muted" do
608 activity = insert(:note_activity)
610 booster = insert(:user)
611 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
613 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
615 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
617 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
620 test "returns reblogs for users for whom reblogs have not been muted" do
621 activity = insert(:note_activity)
623 booster = insert(:user)
624 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
625 {:ok, user} = CommonAPI.show_reblogs(user, booster)
627 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
629 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
631 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
635 describe "like an object" do
636 test "adds a like activity to the db" do
637 note_activity = insert(:note_activity)
638 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
640 user_two = insert(:user)
642 {:ok, like_activity, object} = ActivityPub.like(user, object)
644 assert like_activity.data["actor"] == user.ap_id
645 assert like_activity.data["type"] == "Like"
646 assert like_activity.data["object"] == object.data["id"]
647 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
648 assert like_activity.data["context"] == object.data["context"]
649 assert object.data["like_count"] == 1
650 assert object.data["likes"] == [user.ap_id]
652 # Just return the original activity if the user already liked it.
653 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
655 assert like_activity == same_like_activity
656 assert object.data["likes"] == [user.ap_id]
658 [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
659 assert note_activity.data["object"]["like_count"] == 1
661 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
662 assert object.data["like_count"] == 2
666 describe "unliking" do
667 test "unliking a previously liked object" do
668 note_activity = insert(:note_activity)
669 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
672 # Unliking something that hasn't been liked does nothing
673 {:ok, object} = ActivityPub.unlike(user, object)
674 assert object.data["like_count"] == 0
676 {:ok, like_activity, object} = ActivityPub.like(user, object)
677 assert object.data["like_count"] == 1
679 {:ok, _, _, object} = ActivityPub.unlike(user, object)
680 assert object.data["like_count"] == 0
682 assert Activity.get_by_id(like_activity.id) == nil
686 describe "announcing an object" do
687 test "adds an announce activity to the db" do
688 note_activity = insert(:note_activity)
689 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
692 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
693 assert object.data["announcement_count"] == 1
694 assert object.data["announcements"] == [user.ap_id]
696 assert announce_activity.data["to"] == [
697 User.ap_followers(user),
698 note_activity.data["actor"]
701 assert announce_activity.data["object"] == object.data["id"]
702 assert announce_activity.data["actor"] == user.ap_id
703 assert announce_activity.data["context"] == object.data["context"]
707 describe "unannouncing an object" do
708 test "unannouncing a previously announced object" do
709 note_activity = insert(:note_activity)
710 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
713 # Unannouncing an object that is not announced does nothing
714 # {:ok, object} = ActivityPub.unannounce(user, object)
715 # assert object.data["announcement_count"] == 0
717 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
718 assert object.data["announcement_count"] == 1
720 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
721 assert object.data["announcement_count"] == 0
723 assert unannounce_activity.data["to"] == [
724 User.ap_followers(user),
725 announce_activity.data["actor"]
728 assert unannounce_activity.data["type"] == "Undo"
729 assert unannounce_activity.data["object"] == announce_activity.data
730 assert unannounce_activity.data["actor"] == user.ap_id
731 assert unannounce_activity.data["context"] == announce_activity.data["context"]
733 assert Activity.get_by_id(announce_activity.id) == nil
737 describe "uploading files" do
738 test "copies the file to the configured folder" do
740 content_type: "image/jpg",
741 path: Path.absname("test/fixtures/image.jpg"),
742 filename: "an_image.jpg"
745 {:ok, %Object{} = object} = ActivityPub.upload(file)
746 assert object.data["name"] == "an_image.jpg"
749 test "works with base64 encoded images" do
754 {:ok, %Object{}} = ActivityPub.upload(file)
758 describe "fetch the latest Follow" do
759 test "fetches the latest Follow activity" do
760 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
761 follower = Repo.get_by(User, ap_id: activity.data["actor"])
762 followed = Repo.get_by(User, ap_id: activity.data["object"])
764 assert activity == Utils.fetch_latest_follow(follower, followed)
768 describe "following / unfollowing" do
769 test "creates a follow activity" do
770 follower = insert(:user)
771 followed = insert(:user)
773 {:ok, activity} = ActivityPub.follow(follower, followed)
774 assert activity.data["type"] == "Follow"
775 assert activity.data["actor"] == follower.ap_id
776 assert activity.data["object"] == followed.ap_id
779 test "creates an undo activity for the last follow" do
780 follower = insert(:user)
781 followed = insert(:user)
783 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
784 {:ok, activity} = ActivityPub.unfollow(follower, followed)
786 assert activity.data["type"] == "Undo"
787 assert activity.data["actor"] == follower.ap_id
789 assert is_map(activity.data["object"])
790 assert activity.data["object"]["type"] == "Follow"
791 assert activity.data["object"]["object"] == followed.ap_id
792 assert activity.data["object"]["id"] == follow_activity.data["id"]
796 describe "blocking / unblocking" do
797 test "creates a block activity" do
798 blocker = insert(:user)
799 blocked = insert(:user)
801 {:ok, activity} = ActivityPub.block(blocker, blocked)
803 assert activity.data["type"] == "Block"
804 assert activity.data["actor"] == blocker.ap_id
805 assert activity.data["object"] == blocked.ap_id
808 test "creates an undo activity for the last block" do
809 blocker = insert(:user)
810 blocked = insert(:user)
812 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
813 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
815 assert activity.data["type"] == "Undo"
816 assert activity.data["actor"] == blocker.ap_id
818 assert is_map(activity.data["object"])
819 assert activity.data["object"]["type"] == "Block"
820 assert activity.data["object"]["object"] == blocked.ap_id
821 assert activity.data["object"]["id"] == block_activity.data["id"]
825 describe "deletion" do
826 test "it creates a delete activity and deletes the original object" do
827 note = insert(:note_activity)
828 object = Object.get_by_ap_id(note.data["object"]["id"])
829 {:ok, delete} = ActivityPub.delete(object)
831 assert delete.data["type"] == "Delete"
832 assert delete.data["actor"] == note.data["actor"]
833 assert delete.data["object"] == note.data["object"]["id"]
835 assert Activity.get_by_id(delete.id) != nil
837 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
840 test "decrements user note count only for public activities" do
841 user = insert(:user, info: %{note_count: 10})
844 CommonAPI.post(User.get_cached_by_id(user.id), %{
846 "visibility" => "public"
850 CommonAPI.post(User.get_cached_by_id(user.id), %{
852 "visibility" => "unlisted"
856 CommonAPI.post(User.get_cached_by_id(user.id), %{
858 "visibility" => "private"
862 CommonAPI.post(User.get_cached_by_id(user.id), %{
864 "visibility" => "direct"
867 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
868 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
869 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
870 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
872 user = User.get_cached_by_id(user.id)
873 assert user.info.note_count == 10
876 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
878 note = insert(:note_activity)
881 Object.get_by_ap_id(note.data["object"]["id"])
884 "actor" => note.data["object"]["actor"],
885 "id" => note.data["object"]["id"],
886 "to" => [user.ap_id],
890 |> Object.update_and_set_cache()
892 {:ok, delete} = ActivityPub.delete(object)
894 assert user.ap_id in delete.data["to"]
897 test "decreases reply count" do
899 user2 = insert(:user)
901 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
902 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
903 ap_id = activity.data["id"]
905 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
906 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
907 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
908 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
910 _ = CommonAPI.delete(direct_reply.id, user2)
911 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
912 assert object.data["repliesCount"] == 2
914 _ = CommonAPI.delete(private_reply.id, user2)
915 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
916 assert object.data["repliesCount"] == 2
918 _ = CommonAPI.delete(public_reply.id, user2)
919 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
920 assert object.data["repliesCount"] == 1
922 _ = CommonAPI.delete(unlisted_reply.id, user2)
923 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
924 assert object.data["repliesCount"] == 0
928 describe "timeline post-processing" do
929 test "it filters broken threads" do
930 user1 = insert(:user)
931 user2 = insert(:user)
932 user3 = insert(:user)
934 {:ok, user1} = User.follow(user1, user3)
935 assert User.following?(user1, user3)
937 {:ok, user2} = User.follow(user2, user3)
938 assert User.following?(user2, user3)
940 {:ok, user3} = User.follow(user3, user2)
941 assert User.following?(user3, user2)
943 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
945 {:ok, private_activity_1} =
946 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
948 {:ok, private_activity_2} =
949 CommonAPI.post(user2, %{
951 "visibility" => "private",
952 "in_reply_to_status_id" => private_activity_1.id
955 {:ok, private_activity_3} =
956 CommonAPI.post(user3, %{
958 "visibility" => "private",
959 "in_reply_to_status_id" => private_activity_2.id
962 activities = ActivityPub.fetch_activities([user1.ap_id | user1.following])
964 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
966 assert [public_activity, private_activity_1, private_activity_3] ==
969 assert length(activities) == 3
971 activities = ActivityPub.contain_timeline(activities, user1)
973 assert [public_activity, private_activity_1] == activities
974 assert length(activities) == 2
979 test "it creates an update activity with the new user data" do
981 {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user)
982 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
985 ActivityPub.update(%{
986 actor: user_data["id"],
987 to: [user.follower_address],
992 assert update.data["actor"] == user.ap_id
993 assert update.data["to"] == [user.follower_address]
994 assert update.data["object"]["id"] == user_data["id"]
995 assert update.data["object"]["type"] == user_data["type"]
999 test "returned pinned statuses" do
1000 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1001 user = insert(:user)
1003 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1004 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1005 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1007 CommonAPI.pin(activity_one.id, user)
1008 user = refresh_record(user)
1010 CommonAPI.pin(activity_two.id, user)
1011 user = refresh_record(user)
1013 CommonAPI.pin(activity_three.id, user)
1014 user = refresh_record(user)
1016 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1018 assert 3 = length(activities)
1021 test "it can create a Flag activity" do
1022 reporter = insert(:user)
1023 target_account = insert(:user)
1024 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
1025 context = Utils.generate_context_id()
1028 reporter_ap_id = reporter.ap_id
1029 target_ap_id = target_account.ap_id
1030 activity_ap_id = activity.data["id"]
1032 assert {:ok, activity} =
1036 account: target_account,
1037 statuses: [activity],
1042 actor: ^reporter_ap_id,
1045 "content" => ^content,
1046 "context" => ^context,
1047 "object" => [^target_ap_id, ^activity_ap_id]
1052 describe "publish_one/1" do
1053 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
1057 actor = insert(:user)
1058 inbox = "http://200.site/users/nick1/inbox"
1060 assert {:ok, _} = ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1062 assert called(Instances.set_reachable(inbox))
1065 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
1069 actor = insert(:user)
1070 inbox = "http://200.site/users/nick1/inbox"
1073 ActivityPub.publish_one(%{
1078 unreachable_since: NaiveDateTime.utc_now()
1081 assert called(Instances.set_reachable(inbox))
1084 test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
1088 actor = insert(:user)
1089 inbox = "http://200.site/users/nick1/inbox"
1092 ActivityPub.publish_one(%{
1097 unreachable_since: nil
1100 refute called(Instances.set_reachable(inbox))
1103 test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
1107 actor = insert(:user)
1108 inbox = "http://404.site/users/nick1/inbox"
1110 assert {:error, _} =
1111 ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1113 assert called(Instances.set_unreachable(inbox))
1116 test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
1120 actor = insert(:user)
1121 inbox = "http://connrefused.site/users/nick1/inbox"
1123 assert {:error, _} =
1124 ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1126 assert called(Instances.set_unreachable(inbox))
1129 test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
1133 actor = insert(:user)
1134 inbox = "http://200.site/users/nick1/inbox"
1136 assert {:ok, _} = ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1138 refute called(Instances.set_unreachable(inbox))
1141 test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
1145 actor = insert(:user)
1146 inbox = "http://connrefused.site/users/nick1/inbox"
1148 assert {:error, _} =
1149 ActivityPub.publish_one(%{
1154 unreachable_since: NaiveDateTime.utc_now()
1157 refute called(Instances.set_unreachable(inbox))
1162 File.read!("test/fixtures/avatar_data_uri")