- assert announce_activity.data["to"] == [
- User.ap_followers(user),
- note_activity.data["actor"]
- ]
-
- assert announce_activity.data["object"] == object.data["id"]
- assert announce_activity.data["actor"] == user.ap_id
- assert announce_activity.data["context"] == object.data["context"]
- end
-
- test "reverts annouce from object on error" do
- note_activity = insert(:note_activity)
- object = Object.normalize(note_activity)
- user = insert(:user)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.announce(user, object)
- end
-
- reloaded_object = Object.get_by_ap_id(object.data["id"])
- assert reloaded_object == object
- refute reloaded_object.data["announcement_count"]
- refute reloaded_object.data["announcements"]
- end
- end
-
- describe "announcing a private object" do
- test "adds an announce activity to the db if the audience is not widened" do
- user = insert(:user)
- {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
- object = Object.normalize(note_activity)
-
- {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
-
- assert announce_activity.data["to"] == [User.ap_followers(user)]
-
- assert announce_activity.data["object"] == object.data["id"]
- assert announce_activity.data["actor"] == user.ap_id
- assert announce_activity.data["context"] == object.data["context"]
- end
-
- test "does not add an announce activity to the db if the audience is widened" do
- user = insert(:user)
- {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
- object = Object.normalize(note_activity)
-
- assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
- end
-
- test "does not add an announce activity to the db if the announcer is not the author" do
- user = insert(:user)
- announcer = insert(:user)
- {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
- object = Object.normalize(note_activity)
-
- assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
- end
- end
-
- describe "unannouncing an object" do
- test "unannouncing a previously announced object" do
- note_activity = insert(:note_activity)
- object = Object.normalize(note_activity)
- user = insert(:user)
-
- # Unannouncing an object that is not announced does nothing
- {:ok, object} = ActivityPub.unannounce(user, object)
- refute object.data["announcement_count"]
-
- {:ok, announce_activity, object} = ActivityPub.announce(user, object)
- assert object.data["announcement_count"] == 1
-
- {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
- assert object.data["announcement_count"] == 0
-
- assert unannounce_activity.data["to"] == [
- User.ap_followers(user),
- object.data["actor"]
- ]
-
- assert unannounce_activity.data["type"] == "Undo"
- assert unannounce_activity.data["object"] == announce_activity.data
- assert unannounce_activity.data["actor"] == user.ap_id
- assert unannounce_activity.data["context"] == announce_activity.data["context"]
-
- assert Activity.get_by_id(announce_activity.id) == nil
- end
-
- test "reverts unannouncing on error" do
- note_activity = insert(:note_activity)
- object = Object.normalize(note_activity)
- user = insert(:user)
-
- {:ok, _announce_activity, object} = ActivityPub.announce(user, object)
- assert object.data["announcement_count"] == 1
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.unannounce(user, object)
- end
-
- object = Object.get_by_ap_id(object.data["id"])
- assert object.data["announcement_count"] == 1
- end
- end
-
- describe "uploading files" do
- test "copies the file to the configured folder" do
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, %Object{} = object} = ActivityPub.upload(file)
- assert object.data["name"] == "an_image.jpg"
- end
-
- test "works with base64 encoded images" do
- file = %{
- "img" => data_uri()
- }
-
- {:ok, %Object{}} = ActivityPub.upload(file)
- end
- end
-
- describe "fetch the latest Follow" do
- test "fetches the latest Follow activity" do
- %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
- follower = Repo.get_by(User, ap_id: activity.data["actor"])
- followed = Repo.get_by(User, ap_id: activity.data["object"])
-
- assert activity == Utils.fetch_latest_follow(follower, followed)
- end
- end
-
- describe "following / unfollowing" do
- test "it reverts follow activity" do
- follower = insert(:user)
- followed = insert(:user)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.follow(follower, followed)
- end
-
- assert Repo.aggregate(Activity, :count, :id) == 0
- assert Repo.aggregate(Object, :count, :id) == 0
- end
-
- test "it reverts unfollow activity" do
- follower = insert(:user)
- followed = insert(:user)
-
- {:ok, follow_activity} = ActivityPub.follow(follower, followed)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
- end
-
- activity = Activity.get_by_id(follow_activity.id)
- assert activity.data["type"] == "Follow"
- assert activity.data["actor"] == follower.ap_id
-
- assert activity.data["object"] == followed.ap_id
- end
-
- test "creates a follow activity" do
- follower = insert(:user)
- followed = insert(:user)
-
- {:ok, activity} = ActivityPub.follow(follower, followed)
- assert activity.data["type"] == "Follow"
- assert activity.data["actor"] == follower.ap_id
- assert activity.data["object"] == followed.ap_id
- end
-
- test "creates an undo activity for the last follow" do
- follower = insert(:user)
- followed = insert(:user)
-
- {:ok, follow_activity} = ActivityPub.follow(follower, followed)
- {:ok, activity} = ActivityPub.unfollow(follower, followed)
-
- assert activity.data["type"] == "Undo"
- assert activity.data["actor"] == follower.ap_id
-
- embedded_object = activity.data["object"]
- assert is_map(embedded_object)
- assert embedded_object["type"] == "Follow"
- assert embedded_object["object"] == followed.ap_id
- assert embedded_object["id"] == follow_activity.data["id"]
- end
-
- test "creates an undo activity for a pending follow request" do
- follower = insert(:user)
- followed = insert(:user, %{locked: true})
-
- {:ok, follow_activity} = ActivityPub.follow(follower, followed)
- {:ok, activity} = ActivityPub.unfollow(follower, followed)
-
- assert activity.data["type"] == "Undo"
- assert activity.data["actor"] == follower.ap_id
-
- embedded_object = activity.data["object"]
- assert is_map(embedded_object)
- assert embedded_object["type"] == "Follow"
- assert embedded_object["object"] == followed.ap_id
- assert embedded_object["id"] == follow_activity.data["id"]
- end
- end
-
- describe "blocking / unblocking" do
- test "reverts block activity on error" do
- [blocker, blocked] = insert_list(2, :user)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.block(blocker, blocked)
- end
-
- assert Repo.aggregate(Activity, :count, :id) == 0
- assert Repo.aggregate(Object, :count, :id) == 0
- end
-
- test "creates a block activity" do
- blocker = insert(:user)
- blocked = insert(:user)
-
- {:ok, activity} = ActivityPub.block(blocker, blocked)
-
- assert activity.data["type"] == "Block"
- assert activity.data["actor"] == blocker.ap_id
- assert activity.data["object"] == blocked.ap_id
- end
-
- test "reverts unblock activity on error" do
- [blocker, blocked] = insert_list(2, :user)
- {:ok, block_activity} = ActivityPub.block(blocker, blocked)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.unblock(blocker, blocked)
- end
-
- assert block_activity.data["type"] == "Block"
- assert block_activity.data["actor"] == blocker.ap_id
-
- assert Repo.aggregate(Activity, :count, :id) == 1
- assert Repo.aggregate(Object, :count, :id) == 1
- end
-
- test "creates an undo activity for the last block" do
- blocker = insert(:user)
- blocked = insert(:user)
-
- {:ok, block_activity} = ActivityPub.block(blocker, blocked)
- {:ok, activity} = ActivityPub.unblock(blocker, blocked)
-
- assert activity.data["type"] == "Undo"
- assert activity.data["actor"] == blocker.ap_id
-
- embedded_object = activity.data["object"]
- assert is_map(embedded_object)
- assert embedded_object["type"] == "Block"
- assert embedded_object["object"] == blocked.ap_id
- assert embedded_object["id"] == block_activity.data["id"]
- end
- end
-
- describe "deletion" do
- setup do: clear_config([:instance, :rewrite_policy])
-
- test "it reverts deletion on error" do
- note = insert(:note_activity)
- object = Object.normalize(note)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.delete(object)
- end
-
- assert Repo.aggregate(Activity, :count, :id) == 1
- assert Repo.get(Object, object.id) == object
- assert Activity.get_by_id(note.id) == note
- end
-
- test "it creates a delete activity and deletes the original object" do
- note = insert(:note_activity)
- object = Object.normalize(note)
- {:ok, delete} = ActivityPub.delete(object)
-
- assert delete.data["type"] == "Delete"
- assert delete.data["actor"] == note.data["actor"]
- assert delete.data["object"] == object.data["id"]
-
- assert Activity.get_by_id(delete.id) != nil
-
- assert Repo.get(Object, object.id).data["type"] == "Tombstone"
- end
-
- test "it doesn't fail when an activity was already deleted" do
- {:ok, delete} = insert(:note_activity) |> Object.normalize() |> ActivityPub.delete()
-
- assert {:ok, ^delete} = delete |> Object.normalize() |> ActivityPub.delete()
- end
-
- test "decrements user note count only for public activities" do
- user = insert(:user, note_count: 10)
-
- {:ok, a1} =
- CommonAPI.post(User.get_cached_by_id(user.id), %{
- "status" => "yeah",
- "visibility" => "public"
- })
-
- {:ok, a2} =
- CommonAPI.post(User.get_cached_by_id(user.id), %{
- "status" => "yeah",
- "visibility" => "unlisted"
- })
-
- {:ok, a3} =
- CommonAPI.post(User.get_cached_by_id(user.id), %{
- "status" => "yeah",
- "visibility" => "private"
- })
-
- {:ok, a4} =
- CommonAPI.post(User.get_cached_by_id(user.id), %{
- "status" => "yeah",
- "visibility" => "direct"
- })
-
- {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
- {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
- {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
- {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
-
- user = User.get_cached_by_id(user.id)
- assert user.note_count == 10
- end
-
- test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
- user = insert(:user)
- note = insert(:note_activity)
- object = Object.normalize(note)
-
- {:ok, object} =
- object
- |> Object.change(%{
- data: %{
- "actor" => object.data["actor"],
- "id" => object.data["id"],
- "to" => [user.ap_id],
- "type" => "Note"
- }
- })
- |> Object.update_and_set_cache()
-
- {:ok, delete} = ActivityPub.delete(object)
-
- assert user.ap_id in delete.data["to"]
- end
-
- test "decreases reply count" do
- user = insert(:user)
- user2 = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
- reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
- ap_id = activity.data["id"]
-
- {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
- {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
- {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
- {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
-
- _ = CommonAPI.delete(direct_reply.id, user2)
- assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
- assert object.data["repliesCount"] == 2
-
- _ = CommonAPI.delete(private_reply.id, user2)
- assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
- assert object.data["repliesCount"] == 2
-
- _ = CommonAPI.delete(public_reply.id, user2)
- assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
- assert object.data["repliesCount"] == 1
-
- _ = CommonAPI.delete(unlisted_reply.id, user2)
- assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
- assert object.data["repliesCount"] == 0
- end
-
- test "it passes delete activity through MRF before deleting the object" do
- Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
-
- note = insert(:note_activity)
- object = Object.normalize(note)
-
- {:error, {:reject, _}} = ActivityPub.delete(object)
-
- assert Activity.get_by_id(note.id)
- assert Repo.get(Object, object.id).data["type"] == object.data["type"]
- end
- end
-
- describe "timeline post-processing" do
- test "it filters broken threads" do
- user1 = insert(:user)
- user2 = insert(:user)
- user3 = insert(:user)
-
- {:ok, user1} = User.follow(user1, user3)
- assert User.following?(user1, user3)
-
- {:ok, user2} = User.follow(user2, user3)
- assert User.following?(user2, user3)
-
- {:ok, user3} = User.follow(user3, user2)
- assert User.following?(user3, user2)
-
- {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
-
- {:ok, private_activity_1} =
- CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
-
- {:ok, private_activity_2} =
- CommonAPI.post(user2, %{
- "status" => "hi 3",
- "visibility" => "private",
- "in_reply_to_status_id" => private_activity_1.id
- })
-
- {:ok, private_activity_3} =
- CommonAPI.post(user3, %{
- "status" => "hi 4",
- "visibility" => "private",
- "in_reply_to_status_id" => private_activity_2.id
- })
-
- activities =
- ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
- |> Enum.map(fn a -> a.id end)
-
- private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
-
- assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
-
- assert length(activities) == 3
-
- activities =
- ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
- |> Enum.map(fn a -> a.id end)
-
- assert [public_activity.id, private_activity_1.id] == activities
- assert length(activities) == 2
- end
- end
-
- describe "update" do
- setup do: clear_config([:instance, :max_pinned_statuses])
-
- test "it creates an update activity with the new user data" do
- user = insert(:user)
- {:ok, user} = User.ensure_keys_present(user)
- user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
-
- {:ok, update} =
- ActivityPub.update(%{
- actor: user_data["id"],
- to: [user.follower_address],
- cc: [],
- object: user_data
- })
-
- assert update.data["actor"] == user.ap_id
- assert update.data["to"] == [user.follower_address]
- assert embedded_object = update.data["object"]
- assert embedded_object["id"] == user_data["id"]
- assert embedded_object["type"] == user_data["type"]
- end
- end
-
- test "returned pinned statuses" do
- Config.put([:instance, :max_pinned_statuses], 3)
- user = insert(:user)
-
- {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
- {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
- {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
-
- CommonAPI.pin(activity_one.id, user)
- user = refresh_record(user)
-
- CommonAPI.pin(activity_two.id, user)
- user = refresh_record(user)
-
- CommonAPI.pin(activity_three.id, user)
- user = refresh_record(user)
-
- activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
-
- assert 3 = length(activities)
- end
-
- describe "flag/1" do
- setup do
- reporter = insert(:user)
- target_account = insert(:user)
- content = "foobar"
- {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
- context = Utils.generate_context_id()
-
- reporter_ap_id = reporter.ap_id
- target_ap_id = target_account.ap_id
- activity_ap_id = activity.data["id"]
-
- activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)