diff options
| author | Egor Kislitsyn <egor@kislitsyn.com> | 2020-06-01 15:48:51 +0400 | 
|---|---|---|
| committer | Egor Kislitsyn <egor@kislitsyn.com> | 2020-06-01 15:48:51 +0400 | 
| commit | a7627bdc7ae67a5c103f968eea02d6b1cf1ef8da (patch) | |
| tree | e12af401307cfc3120d50c01580cd959f3b2503a /test/web/activity_pub | |
| parent | decaa64f75f8bd69622fa5fba757f99719f09808 (diff) | |
| parent | e96765df6b04fe5e9766271a9c62e559392758b2 (diff) | |
| download | pleroma-a7627bdc7ae67a5c103f968eea02d6b1cf1ef8da.tar.gz pleroma-a7627bdc7ae67a5c103f968eea02d6b1cf1ef8da.zip  | |
Merge remote-tracking branch 'origin/develop' into global-status-expiration
Diffstat (limited to 'test/web/activity_pub')
18 files changed, 1663 insertions, 1121 deletions
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index a8f1f0e26..24edab41a 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do    use Pleroma.Web.ConnCase    use Oban.Testing, repo: Pleroma.Repo -  import Pleroma.Factory    alias Pleroma.Activity    alias Pleroma.Config    alias Pleroma.Delivery @@ -14,13 +13,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do    alias Pleroma.Object    alias Pleroma.Tests.ObanHelpers    alias Pleroma.User +  alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.ObjectView    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.ActivityPub.UserView    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.Endpoint    alias Pleroma.Workers.ReceiverWorker +  import Pleroma.Factory + +  require Pleroma.Constants +    setup_all do      Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)      :ok @@ -168,6 +173,60 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do      end    end +  describe "mastodon compatibility routes" do +    test "it returns a json representation of the object with accept application/json", %{ +      conn: conn +    } do +      {:ok, object} = +        %{ +          "type" => "Note", +          "content" => "hey", +          "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", +          "actor" => Endpoint.url() <> "/users/raymoo", +          "to" => [Pleroma.Constants.as_public()] +        } +        |> Object.create() + +      conn = +        conn +        |> put_req_header("accept", "application/json") +        |> get("/users/raymoo/statuses/999999999") + +      assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object}) +    end + +    test "it returns a json representation of the activity with accept application/json", %{ +      conn: conn +    } do +      {:ok, object} = +        %{ +          "type" => "Note", +          "content" => "hey", +          "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", +          "actor" => Endpoint.url() <> "/users/raymoo", +          "to" => [Pleroma.Constants.as_public()] +        } +        |> Object.create() + +      {:ok, activity, _} = +        %{ +          "id" => object.data["id"] <> "/activity", +          "type" => "Create", +          "object" => object.data["id"], +          "actor" => object.data["actor"], +          "to" => object.data["to"] +        } +        |> ActivityPub.persist(local: true) + +      conn = +        conn +        |> put_req_header("accept", "application/json") +        |> get("/users/raymoo/statuses/999999999/activity") + +      assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) +    end +  end +    describe "/objects/:uuid" do      test "it returns a json representation of the object with accept application/json", %{        conn: conn @@ -341,7 +400,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do      test "cached purged after activity deletion", %{conn: conn} do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "cofe"})        uuid = String.split(activity.data["id"], "/") |> List.last() @@ -392,6 +451,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        assert Activity.get_by_ap_id(data["id"])      end +    @tag capture_log: true +    test "it inserts an incoming activity into the database" <> +           "even if we can't fetch the user but have it in our db", +         %{conn: conn} do +      user = +        insert(:user, +          ap_id: "https://mastodon.example.org/users/raymoo", +          ap_enabled: true, +          local: false, +          last_refreshed_at: nil +        ) + +      data = +        File.read!("test/fixtures/mastodon-post-activity.json") +        |> Poison.decode!() +        |> Map.put("actor", user.ap_id) +        |> put_in(["object", "attridbutedTo"], user.ap_id) + +      conn = +        conn +        |> assign(:valid_signature, true) +        |> put_req_header("content-type", "application/activity+json") +        |> post("/inbox", data) + +      assert "ok" == json_response(conn, 200) + +      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) +      assert Activity.get_by_ap_id(data["id"]) +    end +      test "it clears `unreachable` federation status of the sender", %{conn: conn} do        data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() @@ -815,26 +904,49 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        assert object["content"] == activity["object"]["content"]      end +    test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do +      user = insert(:user) + +      activity = +        activity +        |> put_in(["object", "type"], "Benis") + +      _result = +        conn +        |> assign(:user, user) +        |> put_req_header("content-type", "application/activity+json") +        |> post("/users/#{user.nickname}/outbox", activity) +        |> json_response(400) +    end +      test "it inserts an incoming sensitive activity into the database", %{        conn: conn,        activity: activity      } do        user = insert(:user) +      conn = assign(conn, :user, user)        object = Map.put(activity["object"], "sensitive", true)        activity = Map.put(activity, "object", object) -      result = +      response =          conn -        |> assign(:user, user)          |> put_req_header("content-type", "application/activity+json")          |> post("/users/#{user.nickname}/outbox", activity)          |> json_response(201) -      assert Activity.get_by_ap_id(result["id"]) -      assert result["object"] -      assert %Object{data: object} = Object.normalize(result["object"]) -      assert object["sensitive"] == activity["object"]["sensitive"] -      assert object["content"] == activity["object"]["content"] +      assert Activity.get_by_ap_id(response["id"]) +      assert response["object"] +      assert %Object{data: response_object} = Object.normalize(response["object"]) +      assert response_object["sensitive"] == true +      assert response_object["content"] == activity["object"]["content"] + +      representation = +        conn +        |> put_req_header("accept", "application/activity+json") +        |> get(response["id"]) +        |> json_response(200) + +      assert representation["object"]["sensitive"] == true      end      test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 8d2e9844b..4cd14aa4a 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -16,7 +16,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.AdminAPI.AccountView    alias Pleroma.Web.CommonAPI -  alias Pleroma.Web.Federator    import ExUnit.CaptureLog    import Mock @@ -33,7 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do    describe "streaming out participations" do      test "it streams them out" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) +      {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})        {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity) @@ -57,8 +56,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do          stream: fn _, _ -> nil end do          {:ok, activity} =            CommonAPI.post(user_one, %{ -            "status" => "@#{user_two.nickname}", -            "visibility" => "direct" +            status: "@#{user_two.nickname}", +            visibility: "direct"            })          conversation = @@ -75,15 +74,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "it restricts by the appropriate visibility" do        user = insert(:user) -      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) +      {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) -      {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) +      {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) -      {:ok, unlisted_activity} = -        CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) +      {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) -      {:ok, private_activity} = -        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) +      {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})        activities =          ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id}) @@ -119,15 +116,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "it excludes by the appropriate visibility" do        user = insert(:user) -      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) +      {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) -      {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) +      {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) -      {:ok, unlisted_activity} = -        CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) +      {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) -      {:ok, private_activity} = -        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) +      {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})        activities =          ActivityPub.fetch_activities([], %{ @@ -194,9 +189,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "it fetches the appropriate tag-restricted posts" do        user = insert(:user) -      {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"}) -      {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"}) -      {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"}) +      {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) +      {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) +      {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})        fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"}) @@ -433,26 +428,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, _} =          CommonAPI.post(User.get_cached_by_id(user.id), %{ -          "status" => "1", -          "visibility" => "public" +          status: "1", +          visibility: "public"          })        {:ok, _} =          CommonAPI.post(User.get_cached_by_id(user.id), %{ -          "status" => "2", -          "visibility" => "unlisted" +          status: "2", +          visibility: "unlisted"          })        {:ok, _} =          CommonAPI.post(User.get_cached_by_id(user.id), %{ -          "status" => "2", -          "visibility" => "private" +          status: "2", +          visibility: "private"          })        {:ok, _} =          CommonAPI.post(User.get_cached_by_id(user.id), %{ -          "status" => "3", -          "visibility" => "direct" +          status: "3", +          visibility: "direct"          })        user = User.get_cached_by_id(user.id) @@ -463,27 +458,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        user = insert(:user)        user2 = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})        ap_id = activity.data["id"] -      reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id} +      reply_data = %{status: "1", in_reply_to_status_id: activity.id}        # public -      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public")) +      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))        assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)        assert object.data["repliesCount"] == 1        # unlisted -      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted")) +      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))        assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)        assert object.data["repliesCount"] == 2        # private -      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private")) +      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))        assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)        assert object.data["repliesCount"] == 2        # direct -      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct")) +      {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))        assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)        assert object.data["repliesCount"] == 2      end @@ -542,7 +537,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      assert Enum.member?(activities, activity_one)      {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]}) -    {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster) +    {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)      %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)      activity_three = Activity.get_by_id(activity_three.id) @@ -570,13 +565,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _user_relationship} = User.block(blocker, blockee) -    {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"}) +    {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"}) -    {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"}) +    {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"}) -    {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"}) +    {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) -    {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"}) +    {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})      activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker}) @@ -593,11 +588,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _user_relationship} = User.block(blocker, blockee) -    {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"}) +    {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"}) -    {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"}) +    {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) -    {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend) +    {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)      activities =        ActivityPub.fetch_activities([], %{"blocking_user" => blocker}) @@ -623,7 +618,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      followed_user = insert(:user)      ActivityPub.follow(user, followed_user) -    {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user) +    {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)      activities =        ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) @@ -656,7 +651,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})      bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})      bad_activity = insert(:note_activity, %{note: bad_note}) -    {:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user) +    {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)      activities =        ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true}) @@ -704,7 +699,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])      {:ok, _user_relationships} = User.mute(user, activity_three_actor) -    {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster) +    {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)      %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)      activity_three = Activity.get_by_id(activity_three.id) @@ -754,7 +749,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, user} = User.follow(user, booster) -    {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster) +    {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)      [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)]) @@ -775,10 +770,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "doesn't retrieve unlisted activities" do        user = insert(:user) -      {:ok, _unlisted_activity} = -        CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"}) +      {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"}) -      {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"}) +      {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})        [activity] = ActivityPub.fetch_public_activities() @@ -852,7 +846,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        booster = insert(:user)        {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster) -      {:ok, activity, _} = CommonAPI.repeat(activity.id, booster) +      {:ok, activity} = CommonAPI.repeat(activity.id, booster)        activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) @@ -866,7 +860,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)        {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster) -      {:ok, activity, _} = CommonAPI.repeat(activity.id, booster) +      {:ok, activity} = CommonAPI.repeat(activity.id, booster)        activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) @@ -874,302 +868,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end -  describe "react to an object" do -    test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do -      Config.put([:instance, :federating], true) -      user = insert(:user) -      reactor = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) -      assert object = Object.normalize(activity) - -      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - -      assert called(Federator.publish(reaction_activity)) -    end - -    test "adds an emoji reaction activity to the db" do -      user = insert(:user) -      reactor = insert(:user) -      third_user = insert(:user) -      fourth_user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) -      assert object = Object.normalize(activity) - -      {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - -      assert reaction_activity - -      assert reaction_activity.data["actor"] == reactor.ap_id -      assert reaction_activity.data["type"] == "EmojiReact" -      assert reaction_activity.data["content"] == "🔥" -      assert reaction_activity.data["object"] == object.data["id"] -      assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]] -      assert reaction_activity.data["context"] == object.data["context"] -      assert object.data["reaction_count"] == 1 -      assert object.data["reactions"] == [["🔥", [reactor.ap_id]]] - -      {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(third_user, object, "☕") - -      assert object.data["reaction_count"] == 2 -      assert object.data["reactions"] == [["🔥", [reactor.ap_id]], ["☕", [third_user.ap_id]]] - -      {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(fourth_user, object, "🔥") - -      assert object.data["reaction_count"] == 3 - -      assert object.data["reactions"] == [ -               ["🔥", [fourth_user.ap_id, reactor.ap_id]], -               ["☕", [third_user.ap_id]] -             ] -    end - -    test "reverts emoji reaction on error" do -      [user, reactor] = insert_list(2, :user) - -      {:ok, activity} = CommonAPI.post(user, %{"status" => "Status"}) -      object = Object.normalize(activity) - -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = ActivityPub.react_with_emoji(reactor, object, "😀") -      end - -      object = Object.get_by_ap_id(object.data["id"]) -      refute object.data["reaction_count"] -      refute object.data["reactions"] -    end -  end - -  describe "unreacting to an object" do -    test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do -      Config.put([:instance, :federating], true) -      user = insert(:user) -      reactor = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) -      assert object = Object.normalize(activity) - -      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - -      assert called(Federator.publish(reaction_activity)) - -      {:ok, unreaction_activity, _object} = -        ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) - -      assert called(Federator.publish(unreaction_activity)) -    end - -    test "adds an undo activity to the db" do -      user = insert(:user) -      reactor = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) -      assert object = Object.normalize(activity) - -      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - -      {:ok, unreaction_activity, _object} = -        ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) - -      assert unreaction_activity.actor == reactor.ap_id -      assert unreaction_activity.data["object"] == reaction_activity.data["id"] - -      object = Object.get_by_ap_id(object.data["id"]) -      assert object.data["reaction_count"] == 0 -      assert object.data["reactions"] == [] -    end - -    test "reverts emoji unreact on error" do -      [user, reactor] = insert_list(2, :user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "Status"}) -      object = Object.normalize(activity) - -      {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "😀") - -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = -                 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) -      end - -      object = Object.get_by_ap_id(object.data["id"]) - -      assert object.data["reaction_count"] == 1 -      assert object.data["reactions"] == [["😀", [reactor.ap_id]]] -    end -  end - -  describe "unliking" do -    test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do -      Config.put([:instance, :federating], true) - -      note_activity = insert(:note_activity) -      object = Object.normalize(note_activity) -      user = insert(:user) - -      {:ok, object} = ActivityPub.unlike(user, object) -      refute called(Federator.publish()) - -      {:ok, _like_activity} = CommonAPI.favorite(user, note_activity.id) -      object = Object.get_by_id(object.id) -      assert object.data["like_count"] == 1 - -      {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object) -      assert object.data["like_count"] == 0 - -      assert called(Federator.publish(unlike_activity)) -    end - -    test "reverts unliking on error" do -      note_activity = insert(:note_activity) -      user = insert(:user) - -      {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) -      object = Object.normalize(note_activity) -      assert object.data["like_count"] == 1 - -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = ActivityPub.unlike(user, object) -      end - -      assert Object.get_by_ap_id(object.data["id"]) == object -      assert object.data["like_count"] == 1 -      assert Activity.get_by_id(like_activity.id) -    end - -    test "unliking a previously liked object" do -      note_activity = insert(:note_activity) -      object = Object.normalize(note_activity) -      user = insert(:user) - -      # Unliking something that hasn't been liked does nothing -      {:ok, object} = ActivityPub.unlike(user, object) -      assert object.data["like_count"] == 0 - -      {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) - -      object = Object.get_by_id(object.id) -      assert object.data["like_count"] == 1 - -      {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object) -      assert object.data["like_count"] == 0 - -      assert Activity.get_by_id(like_activity.id) == nil -      assert note_activity.actor in unlike_activity.recipients -    end -  end - -  describe "announcing an object" do -    test "adds an announce activity to the db" 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 -      assert object.data["announcements"] == [user.ap_id] - -      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{ @@ -1184,7 +882,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "works with base64 encoded images" do        file = %{ -        "img" => data_uri() +        img: data_uri()        }        {:ok, %Object{}} = ActivityPub.upload(file) @@ -1276,7 +974,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end -  describe "blocking / unblocking" do +  describe "blocking" do      test "reverts block activity on error" do        [blocker, blocked] = insert_list(2, :user) @@ -1289,183 +987,38 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end      test "creates a block activity" do +      clear_config([:instance, :federating], true)        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 +      with_mock Pleroma.Web.Federator, +        publish: fn _ -> nil end do +        {:ok, activity} = ActivityPub.block(blocker, blocked) -    test "reverts unblock activity on error" do -      [blocker, blocked] = insert_list(2, :user) -      {:ok, block_activity} = ActivityPub.block(blocker, blocked) +        assert activity.data["type"] == "Block" +        assert activity.data["actor"] == blocker.ap_id +        assert activity.data["object"] == blocked.ap_id -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = ActivityPub.unblock(blocker, blocked) +        assert called(Pleroma.Web.Federator.publish(activity))        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 +    test "works with outgoing blocks disabled, but doesn't federate" do +      clear_config([:instance, :federating], true) +      clear_config([:activitypub, :outgoing_blocks], false)        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]) +      with_mock Pleroma.Web.Federator, +        publish: fn _ -> nil end do +        {:ok, activity} = ActivityPub.block(blocker, blocked) -    test "it reverts deletion on error" do -      note = insert(:note_activity) -      object = Object.normalize(note) +        assert activity.data["type"] == "Block" +        assert activity.data["actor"] == blocker.ap_id +        assert activity.data["object"] == blocked.ap_id -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = ActivityPub.delete(object) +        refute called(Pleroma.Web.Federator.publish(:_))        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 @@ -1484,23 +1037,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, user3} = User.follow(user3, user2)        assert User.following?(user3, user2) -      {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"}) +      {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"}) -      {:ok, private_activity_1} = -        CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"}) +      {: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 +          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 +          status: "hi 4", +          visibility: "private", +          in_reply_to_status_id: private_activity_2.id          })        activities = @@ -1550,9 +1102,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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!!!"}) +    {: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) @@ -1573,7 +1125,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        reporter = insert(:user)        target_account = insert(:user)        content = "foobar" -      {:ok, activity} = CommonAPI.post(target_account, %{"status" => content}) +      {:ok, activity} = CommonAPI.post(target_account, %{status: content})        context = Utils.generate_context_id()        reporter_ap_id = reporter.ap_id @@ -1669,8 +1221,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, list} = Pleroma.List.create("foo", user)      {:ok, list} = Pleroma.List.follow(list, member) -    {:ok, activity} = -      CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) +    {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})      activity = Repo.preload(activity, :bookmark)      activity = %Activity{activity | thread_muted?: !!activity.thread_muted?} @@ -1688,8 +1239,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => "thought I looked cute might delete later :3", -          "visibility" => "private" +          status: "thought I looked cute might delete later :3", +          visibility: "private"          })        [result] = ActivityPub.fetch_activities_bounded([user.follower_address], []) @@ -1698,12 +1249,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "fetches only public posts for other users" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})        {:ok, _private_activity} =          CommonAPI.post(user, %{ -          "status" => "why is tenshi eating a corndog so cute?", -          "visibility" => "private" +          status: "why is tenshi eating a corndog so cute?", +          visibility: "private"          })        [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address]) @@ -1831,11 +1382,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        other_user = insert(:user)        user1 = insert(:user)        user2 = insert(:user) -      {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"}) -      {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"}) -      {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "}) -      {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "}) -      {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "}) +      {:ok, a1} = CommonAPI.post(user1, %{status: "bla"}) +      {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"}) +      {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "}) +      {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "}) +      {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})        {:ok, _} = CommonAPI.favorite(user, a4.id)        {:ok, _} = CommonAPI.favorite(other_user, a3.id) @@ -1915,10 +1466,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do    test "doesn't retrieve replies activities with exclude_replies" do      user = insert(:user) -    {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"}) +    {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) -    {:ok, _reply} = -      CommonAPI.post(user, %{"status" => "yeah", "in_reply_to_status_id" => activity.id}) +    {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})      [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"}) @@ -2231,84 +1781,84 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, u2} = User.follow(u2, u3)      {:ok, u3} = User.follow(u3, u2) -    {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status"}) +    {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})      {:ok, r1_1} =        CommonAPI.post(u2, %{ -        "status" => "@#{u1.nickname} reply from u2 to u1", -        "in_reply_to_status_id" => a1.id +        status: "@#{u1.nickname} reply from u2 to u1", +        in_reply_to_status_id: a1.id        })      {:ok, r1_2} =        CommonAPI.post(u3, %{ -        "status" => "@#{u1.nickname} reply from u3 to u1", -        "in_reply_to_status_id" => a1.id +        status: "@#{u1.nickname} reply from u3 to u1", +        in_reply_to_status_id: a1.id        })      {:ok, r1_3} =        CommonAPI.post(u4, %{ -        "status" => "@#{u1.nickname} reply from u4 to u1", -        "in_reply_to_status_id" => a1.id +        status: "@#{u1.nickname} reply from u4 to u1", +        in_reply_to_status_id: a1.id        }) -    {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status"}) +    {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})      {:ok, r2_1} =        CommonAPI.post(u1, %{ -        "status" => "@#{u2.nickname} reply from u1 to u2", -        "in_reply_to_status_id" => a2.id +        status: "@#{u2.nickname} reply from u1 to u2", +        in_reply_to_status_id: a2.id        })      {:ok, r2_2} =        CommonAPI.post(u3, %{ -        "status" => "@#{u2.nickname} reply from u3 to u2", -        "in_reply_to_status_id" => a2.id +        status: "@#{u2.nickname} reply from u3 to u2", +        in_reply_to_status_id: a2.id        })      {:ok, r2_3} =        CommonAPI.post(u4, %{ -        "status" => "@#{u2.nickname} reply from u4 to u2", -        "in_reply_to_status_id" => a2.id +        status: "@#{u2.nickname} reply from u4 to u2", +        in_reply_to_status_id: a2.id        }) -    {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status"}) +    {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})      {:ok, r3_1} =        CommonAPI.post(u1, %{ -        "status" => "@#{u3.nickname} reply from u1 to u3", -        "in_reply_to_status_id" => a3.id +        status: "@#{u3.nickname} reply from u1 to u3", +        in_reply_to_status_id: a3.id        })      {:ok, r3_2} =        CommonAPI.post(u2, %{ -        "status" => "@#{u3.nickname} reply from u2 to u3", -        "in_reply_to_status_id" => a3.id +        status: "@#{u3.nickname} reply from u2 to u3", +        in_reply_to_status_id: a3.id        })      {:ok, r3_3} =        CommonAPI.post(u4, %{ -        "status" => "@#{u3.nickname} reply from u4 to u3", -        "in_reply_to_status_id" => a3.id +        status: "@#{u3.nickname} reply from u4 to u3", +        in_reply_to_status_id: a3.id        }) -    {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status"}) +    {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})      {:ok, r4_1} =        CommonAPI.post(u1, %{ -        "status" => "@#{u4.nickname} reply from u1 to u4", -        "in_reply_to_status_id" => a4.id +        status: "@#{u4.nickname} reply from u1 to u4", +        in_reply_to_status_id: a4.id        })      {:ok, r4_2} =        CommonAPI.post(u2, %{ -        "status" => "@#{u4.nickname} reply from u2 to u4", -        "in_reply_to_status_id" => a4.id +        status: "@#{u4.nickname} reply from u2 to u4", +        in_reply_to_status_id: a4.id        })      {:ok, r4_3} =        CommonAPI.post(u3, %{ -        "status" => "@#{u4.nickname} reply from u3 to u4", -        "in_reply_to_status_id" => a4.id +        status: "@#{u4.nickname} reply from u3 to u4", +        in_reply_to_status_id: a4.id        })      {:ok, @@ -2332,68 +1882,68 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, u2} = User.follow(u2, u3)      {:ok, u3} = User.follow(u3, u2) -    {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status", "visibility" => "private"}) +    {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})      {:ok, r1_1} =        CommonAPI.post(u2, %{ -        "status" => "@#{u1.nickname} reply from u2 to u1", -        "in_reply_to_status_id" => a1.id, -        "visibility" => "private" +        status: "@#{u1.nickname} reply from u2 to u1", +        in_reply_to_status_id: a1.id, +        visibility: "private"        })      {:ok, r1_2} =        CommonAPI.post(u3, %{ -        "status" => "@#{u1.nickname} reply from u3 to u1", -        "in_reply_to_status_id" => a1.id, -        "visibility" => "private" +        status: "@#{u1.nickname} reply from u3 to u1", +        in_reply_to_status_id: a1.id, +        visibility: "private"        })      {:ok, r1_3} =        CommonAPI.post(u4, %{ -        "status" => "@#{u1.nickname} reply from u4 to u1", -        "in_reply_to_status_id" => a1.id, -        "visibility" => "private" +        status: "@#{u1.nickname} reply from u4 to u1", +        in_reply_to_status_id: a1.id, +        visibility: "private"        }) -    {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status", "visibility" => "private"}) +    {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})      {:ok, r2_1} =        CommonAPI.post(u1, %{ -        "status" => "@#{u2.nickname} reply from u1 to u2", -        "in_reply_to_status_id" => a2.id, -        "visibility" => "private" +        status: "@#{u2.nickname} reply from u1 to u2", +        in_reply_to_status_id: a2.id, +        visibility: "private"        })      {:ok, r2_2} =        CommonAPI.post(u3, %{ -        "status" => "@#{u2.nickname} reply from u3 to u2", -        "in_reply_to_status_id" => a2.id, -        "visibility" => "private" +        status: "@#{u2.nickname} reply from u3 to u2", +        in_reply_to_status_id: a2.id, +        visibility: "private"        }) -    {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status", "visibility" => "private"}) +    {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})      {:ok, r3_1} =        CommonAPI.post(u1, %{ -        "status" => "@#{u3.nickname} reply from u1 to u3", -        "in_reply_to_status_id" => a3.id, -        "visibility" => "private" +        status: "@#{u3.nickname} reply from u1 to u3", +        in_reply_to_status_id: a3.id, +        visibility: "private"        })      {:ok, r3_2} =        CommonAPI.post(u2, %{ -        "status" => "@#{u3.nickname} reply from u2 to u3", -        "in_reply_to_status_id" => a3.id, -        "visibility" => "private" +        status: "@#{u3.nickname} reply from u2 to u3", +        in_reply_to_status_id: a3.id, +        visibility: "private"        }) -    {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status", "visibility" => "private"}) +    {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})      {:ok, r4_1} =        CommonAPI.post(u1, %{ -        "status" => "@#{u4.nickname} reply from u1 to u4", -        "in_reply_to_status_id" => a4.id, -        "visibility" => "private" +        status: "@#{u4.nickname} reply from u1 to u4", +        in_reply_to_status_id: a4.id, +        visibility: "private"        })      {:ok, diff --git a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/web/activity_pub/mrf/steal_emoji_policy_test.exs new file mode 100644 index 000000000..3f8222736 --- /dev/null +++ b/test/web/activity_pub/mrf/steal_emoji_policy_test.exs @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do +  use Pleroma.DataCase + +  alias Pleroma.Config +  alias Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy + +  setup_all do +    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end + +  setup do +    emoji_path = Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") +    File.rm_rf!(emoji_path) +    File.mkdir!(emoji_path) + +    Pleroma.Emoji.reload() + +    on_exit(fn -> +      File.rm_rf!(emoji_path) +    end) + +    :ok +  end + +  test "does nothing by default" do +    installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) +    refute "firedfox" in installed_emoji + +    message = %{ +      "type" => "Create", +      "object" => %{ +        "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], +        "actor" => "https://example.org/users/admin" +      } +    } + +    assert {:ok, message} == StealEmojiPolicy.filter(message) + +    installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) +    refute "firedfox" in installed_emoji +  end + +  test "Steals emoji on unknown shortcode from allowed remote host" do +    installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) +    refute "firedfox" in installed_emoji + +    message = %{ +      "type" => "Create", +      "object" => %{ +        "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], +        "actor" => "https://example.org/users/admin" +      } +    } + +    clear_config([:mrf_steal_emoji, :hosts], ["example.org"]) +    clear_config([:mrf_steal_emoji, :size_limit], 284_468) + +    assert {:ok, message} == StealEmojiPolicy.filter(message) + +    installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) +    assert "firedfox" in installed_emoji +  end +end diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs index 93989e28a..7953eecf2 100644 --- a/test/web/activity_pub/object_validator_test.exs +++ b/test/web/activity_pub/object_validator_test.exs @@ -1,6 +1,8 @@  defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do    use Pleroma.DataCase +  alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.Builder    alias Pleroma.Web.ActivityPub.ObjectValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator    alias Pleroma.Web.ActivityPub.Utils @@ -8,10 +10,182 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do    import Pleroma.Factory +  describe "EmojiReacts" do +    setup do +      user = insert(:user) +      {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + +      object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) + +      {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") + +      %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} +    end + +    test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do +      assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) +    end + +    test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do +      without_content = +        valid_emoji_react +        |> Map.delete("content") + +      {:error, cng} = ObjectValidator.validate(without_content, []) + +      refute cng.valid? +      assert {:content, {"can't be blank", [validation: :required]}} in cng.errors +    end + +    test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do +      without_emoji_content = +        valid_emoji_react +        |> Map.put("content", "x") + +      {:error, cng} = ObjectValidator.validate(without_emoji_content, []) + +      refute cng.valid? + +      assert {:content, {"must be a single character emoji", []}} in cng.errors +    end +  end + +  describe "Undos" do +    setup do +      user = insert(:user) +      {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) +      {:ok, like} = CommonAPI.favorite(user, post_activity.id) +      {:ok, valid_like_undo, []} = Builder.undo(user, like) + +      %{user: user, like: like, valid_like_undo: valid_like_undo} +    end + +    test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do +      assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) +    end + +    test "it does not validate if the actor of the undo is not the actor of the object", %{ +      valid_like_undo: valid_like_undo +    } do +      other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + +      bad_actor = +        valid_like_undo +        |> Map.put("actor", other_user.ap_id) + +      {:error, cng} = ObjectValidator.validate(bad_actor, []) + +      assert {:actor, {"not the same as object actor", []}} in cng.errors +    end + +    test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do +      missing_object = +        valid_like_undo +        |> Map.put("object", "https://gensokyo.2hu/objects/1") + +      {:error, cng} = ObjectValidator.validate(missing_object, []) + +      assert {:object, {"can't find object", []}} in cng.errors +      assert length(cng.errors) == 1 +    end +  end + +  describe "deletes" do +    setup do +      user = insert(:user) +      {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) + +      {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) +      {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) + +      %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} +    end + +    test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do +      {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) + +      assert valid_post_delete["deleted_activity_id"] +    end + +    test "it is invalid if the object isn't in a list of certain types", %{ +      valid_post_delete: valid_post_delete +    } do +      object = Object.get_by_ap_id(valid_post_delete["object"]) + +      data = +        object.data +        |> Map.put("type", "Like") + +      {:ok, _object} = +        object +        |> Ecto.Changeset.change(%{data: data}) +        |> Object.update_and_set_cache() + +      {:error, cng} = ObjectValidator.validate(valid_post_delete, []) +      assert {:object, {"object not in allowed types", []}} in cng.errors +    end + +    test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do +      assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) +    end + +    test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do +      no_id = +        valid_post_delete +        |> Map.delete("id") + +      {:error, cng} = ObjectValidator.validate(no_id, []) + +      assert {:id, {"can't be blank", [validation: :required]}} in cng.errors +    end + +    test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do +      missing_object = +        valid_post_delete +        |> Map.put("object", "http://does.not/exist") + +      {:error, cng} = ObjectValidator.validate(missing_object, []) + +      assert {:object, {"can't find object", []}} in cng.errors +    end + +    test "it's invalid if the actor of the object and the actor of delete are from different domains", +         %{valid_post_delete: valid_post_delete} do +      valid_user = insert(:user) + +      valid_other_actor = +        valid_post_delete +        |> Map.put("actor", valid_user.ap_id) + +      assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) + +      invalid_other_actor = +        valid_post_delete +        |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + +      {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) + +      assert {:actor, {"is not allowed to delete object", []}} in cng.errors +    end + +    test "it's valid if the actor of the object is a local superuser", +         %{valid_post_delete: valid_post_delete} do +      user = +        insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") + +      valid_other_actor = +        valid_post_delete +        |> Map.put("actor", user.ap_id) + +      {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) +      assert meta[:do_not_federate] +    end +  end +    describe "likes" do      setup do        user = insert(:user) -      {:ok, post_activity} = CommonAPI.post(user, %{"status" => "uguu"}) +      {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"})        valid_like = %{          "to" => [user.ap_id], @@ -106,4 +280,96 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do        assert {:object, valid_like["object"]} in validated.changes      end    end + +  describe "announces" do +    setup do +      user = insert(:user) +      announcer = insert(:user) +      {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + +      object = Object.normalize(post_activity, false) +      {:ok, valid_announce, []} = Builder.announce(announcer, object) + +      %{ +        valid_announce: valid_announce, +        user: user, +        post_activity: post_activity, +        announcer: announcer +      } +    end + +    test "returns ok for a valid announce", %{valid_announce: valid_announce} do +      assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) +    end + +    test "returns an error if the object can't be found", %{valid_announce: valid_announce} do +      without_object = +        valid_announce +        |> Map.delete("object") + +      {:error, cng} = ObjectValidator.validate(without_object, []) + +      assert {:object, {"can't be blank", [validation: :required]}} in cng.errors + +      nonexisting_object = +        valid_announce +        |> Map.put("object", "https://gensokyo.2hu/objects/99999999") + +      {:error, cng} = ObjectValidator.validate(nonexisting_object, []) + +      assert {:object, {"can't find object", []}} in cng.errors +    end + +    test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do +      nonexisting_actor = +        valid_announce +        |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + +      {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) + +      assert {:actor, {"can't find user", []}} in cng.errors +    end + +    test "returns an error if the actor already announced the object", %{ +      valid_announce: valid_announce, +      announcer: announcer, +      post_activity: post_activity +    } do +      _announce = CommonAPI.repeat(post_activity.id, announcer) + +      {:error, cng} = ObjectValidator.validate(valid_announce, []) + +      assert {:actor, {"already announced this object", []}} in cng.errors +      assert {:object, {"already announced by this actor", []}} in cng.errors +    end + +    test "returns an error if the actor can't announce the object", %{ +      announcer: announcer, +      user: user +    } do +      {:ok, post_activity} = +        CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) + +      object = Object.normalize(post_activity, false) + +      # Another user can't announce it +      {:ok, announce, []} = Builder.announce(announcer, object, public: false) + +      {:error, cng} = ObjectValidator.validate(announce, []) + +      assert {:actor, {"can not announce this object", []}} in cng.errors + +      # The actor of the object can announce it +      {:ok, announce, []} = Builder.announce(user, object, public: false) + +      assert {:ok, _, _} = ObjectValidator.validate(announce, []) + +      # The actor of the object can not announce it publicly +      {:ok, announce, []} = Builder.announce(user, object, public: true) + +      {:error, cng} = ObjectValidator.validate(announce, []) + +      assert {:actor, {"can not announce this object publicly", []}} in cng.errors +    end +  end  end diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs new file mode 100644 index 000000000..f278f039b --- /dev/null +++ b/test/web/activity_pub/object_validators/types/recipients_test.exs @@ -0,0 +1,27 @@ +defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do +  alias Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients +  use Pleroma.DataCase + +  test "it asserts that all elements of the list are object ids" do +    list = ["https://lain.com/users/lain", "invalid"] + +    assert :error == Recipients.cast(list) +  end + +  test "it works with a list" do +    list = ["https://lain.com/users/lain"] +    assert {:ok, list} == Recipients.cast(list) +  end + +  test "it works with a list with whole objects" do +    list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}] +    resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"] +    assert {:ok, resulting_list} == Recipients.cast(list) +  end + +  test "it turns a single string into a list" do +    recipient = "https://lain.com/users/lain" + +    assert {:ok, [recipient]} == Recipients.cast(recipient) +  end +end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs index f3c437498..26557720b 100644 --- a/test/web/activity_pub/pipeline_test.exs +++ b/test/web/activity_pub/pipeline_test.exs @@ -9,6 +9,11 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do    import Pleroma.Factory    describe "common_pipeline/2" do +    setup do +      clear_config([:instance, :federating], true) +      :ok +    end +      test "it goes through validation, filtering, persisting, side effects and federation for local activities" do        activity = insert(:note_activity)        meta = [local: true] @@ -83,5 +88,44 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do          assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))        end      end + +    test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do +      clear_config([:instance, :federating], false) + +      activity = insert(:note_activity) +      meta = [local: true] + +      with_mocks([ +        {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, +        { +          Pleroma.Web.ActivityPub.MRF, +          [], +          [filter: fn o -> {:ok, o} end] +        }, +        { +          Pleroma.Web.ActivityPub.ActivityPub, +          [], +          [persist: fn o, m -> {:ok, o, m} end] +        }, +        { +          Pleroma.Web.ActivityPub.SideEffects, +          [], +          [handle: fn o, m -> {:ok, o, m} end] +        }, +        { +          Pleroma.Web.Federator, +          [], +          [] +        } +      ]) do +        assert {:ok, ^activity, ^meta} = +                 Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + +        assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) +        assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) +        assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) +        assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) +      end +    end    end  end diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index 9e16e39c4..b3b573c9b 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do    use Pleroma.DataCase    alias Pleroma.Activity -  alias Pleroma.Object    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Relay @@ -95,21 +94,21 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do        end)        assert capture_log(fn -> -               assert Relay.publish(activity) == {:error, nil} -             end) =~ "[error] error: nil" +               assert Relay.publish(activity) == {:error, false} +             end) =~ "[error] error: false"      end      test_with_mock "returns announce activity and publish to federate",                     Pleroma.Web.Federator,                     [:passthrough],                     [] do -      Pleroma.Config.put([:instance, :federating], true) +      clear_config([:instance, :federating], true)        service_actor = Relay.get_actor()        note = insert(:note_activity) -      assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note) +      assert {:ok, %Activity{} = activity} = Relay.publish(note)        assert activity.data["type"] == "Announce"        assert activity.data["actor"] == service_actor.ap_id -      assert activity.data["object"] == obj.data["id"] +      assert activity.data["to"] == [service_actor.follower_address]        assert called(Pleroma.Web.Federator.publish(activity))      end @@ -117,13 +116,12 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do                     Pleroma.Web.Federator,                     [:passthrough],                     [] do -      Pleroma.Config.put([:instance, :federating], false) +      clear_config([:instance, :federating], false)        service_actor = Relay.get_actor()        note = insert(:note_activity) -      assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note) +      assert {:ok, %Activity{} = activity} = Relay.publish(note)        assert activity.data["type"] == "Announce"        assert activity.data["actor"] == service_actor.ap_id -      assert activity.data["object"] == obj.data["id"]        refute called(Pleroma.Web.Federator.publish(activity))      end    end diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index 0b6b55156..a80104ea7 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -3,23 +3,273 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Web.ActivityPub.SideEffectsTest do +  use Oban.Testing, repo: Pleroma.Repo    use Pleroma.DataCase +  alias Pleroma.Activity    alias Pleroma.Notification    alias Pleroma.Object    alias Pleroma.Repo +  alias Pleroma.Tests.ObanHelpers +  alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Builder    alias Pleroma.Web.ActivityPub.SideEffects    alias Pleroma.Web.CommonAPI    import Pleroma.Factory +  import Mock + +  describe "delete objects" do +    setup do +      user = insert(:user) +      other_user = insert(:user) + +      {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) +      {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) +      {:ok, favorite} = CommonAPI.favorite(user, post.id) +      object = Object.normalize(post) +      {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) +      {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) +      {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) +      {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) + +      %{ +        user: user, +        delete: delete, +        post: post, +        object: object, +        delete_user: delete_user, +        op: op, +        favorite: favorite +      } +    end + +    test "it handles object deletions", %{ +      delete: delete, +      post: post, +      object: object, +      user: user, +      op: op, +      favorite: favorite +    } do +      with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], +        stream_out: fn _ -> nil end, +        stream_out_participations: fn _, _ -> nil end do +        {:ok, delete, _} = SideEffects.handle(delete) +        user = User.get_cached_by_ap_id(object.data["actor"]) + +        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) +        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) +      end + +      object = Object.get_by_id(object.id) +      assert object.data["type"] == "Tombstone" +      refute Activity.get_by_id(post.id) +      refute Activity.get_by_id(favorite.id) + +      user = User.get_by_id(user.id) +      assert user.note_count == 0 + +      object = Object.normalize(op.data["object"], false) + +      assert object.data["repliesCount"] == 0 +    end + +    test "it handles object deletions when the object itself has been pruned", %{ +      delete: delete, +      post: post, +      object: object, +      user: user, +      op: op +    } do +      with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], +        stream_out: fn _ -> nil end, +        stream_out_participations: fn _, _ -> nil end do +        {:ok, delete, _} = SideEffects.handle(delete) +        user = User.get_cached_by_ap_id(object.data["actor"]) + +        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) +        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) +      end + +      object = Object.get_by_id(object.id) +      assert object.data["type"] == "Tombstone" +      refute Activity.get_by_id(post.id) + +      user = User.get_by_id(user.id) +      assert user.note_count == 0 + +      object = Object.normalize(op.data["object"], false) + +      assert object.data["repliesCount"] == 0 +    end + +    test "it handles user deletions", %{delete_user: delete, user: user} do +      {:ok, _delete, _} = SideEffects.handle(delete) +      ObanHelpers.perform_all() + +      assert User.get_cached_by_ap_id(user.ap_id).deactivated +    end +  end + +  describe "EmojiReact objects" do +    setup do +      poster = insert(:user) +      user = insert(:user) + +      {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + +      {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌") +      {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true) + +      %{emoji_react: emoji_react, user: user, poster: poster} +    end + +    test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do +      {:ok, emoji_react, _} = SideEffects.handle(emoji_react) +      object = Object.get_by_ap_id(emoji_react.data["object"]) + +      assert object.data["reaction_count"] == 1 +      assert ["👌", [user.ap_id]] in object.data["reactions"] +    end + +    test "creates a notification", %{emoji_react: emoji_react, poster: poster} do +      {:ok, emoji_react, _} = SideEffects.handle(emoji_react) +      assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id) +    end +  end + +  describe "delete users with confirmation pending" do +    setup do +      user = insert(:user, confirmation_pending: true) +      {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) +      {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) +      {:ok, delete: delete_user, user: user} +    end + +    test "when activation is not required", %{delete: delete, user: user} do +      clear_config([:instance, :account_activation_required], false) +      {:ok, _, _} = SideEffects.handle(delete) +      ObanHelpers.perform_all() + +      assert User.get_cached_by_id(user.id).deactivated +    end + +    test "when activation is required", %{delete: delete, user: user} do +      clear_config([:instance, :account_activation_required], true) +      {:ok, _, _} = SideEffects.handle(delete) +      ObanHelpers.perform_all() + +      refute User.get_cached_by_id(user.id) +    end +  end + +  describe "Undo objects" do +    setup do +      poster = insert(:user) +      user = insert(:user) +      {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) +      {:ok, like} = CommonAPI.favorite(user, post.id) +      {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") +      {:ok, announce} = CommonAPI.repeat(post.id, user) +      {:ok, block} = ActivityPub.block(user, poster) +      User.block(user, poster) + +      {:ok, undo_data, _meta} = Builder.undo(user, like) +      {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) + +      {:ok, undo_data, _meta} = Builder.undo(user, reaction) +      {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true) + +      {:ok, undo_data, _meta} = Builder.undo(user, announce) +      {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true) + +      {:ok, undo_data, _meta} = Builder.undo(user, block) +      {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true) + +      %{ +        like_undo: like_undo, +        post: post, +        like: like, +        reaction_undo: reaction_undo, +        reaction: reaction, +        announce_undo: announce_undo, +        announce: announce, +        block_undo: block_undo, +        block: block, +        poster: poster, +        user: user +      } +    end + +    test "deletes the original block", %{block_undo: block_undo, block: block} do +      {:ok, _block_undo, _} = SideEffects.handle(block_undo) +      refute Activity.get_by_id(block.id) +    end + +    test "unblocks the blocked user", %{block_undo: block_undo, block: block} do +      blocker = User.get_by_ap_id(block.data["actor"]) +      blocked = User.get_by_ap_id(block.data["object"]) + +      {:ok, _block_undo, _} = SideEffects.handle(block_undo) +      refute User.blocks?(blocker, blocked) +    end + +    test "an announce undo removes the announce from the object", %{ +      announce_undo: announce_undo, +      post: post +    } do +      {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) + +      object = Object.get_by_ap_id(post.data["object"]) + +      assert object.data["announcement_count"] == 0 +      assert object.data["announcements"] == [] +    end + +    test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do +      {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) +      refute Activity.get_by_id(announce.id) +    end + +    test "a reaction undo removes the reaction from the object", %{ +      reaction_undo: reaction_undo, +      post: post +    } do +      {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) + +      object = Object.get_by_ap_id(post.data["object"]) + +      assert object.data["reaction_count"] == 0 +      assert object.data["reactions"] == [] +    end + +    test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do +      {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) +      refute Activity.get_by_id(reaction.id) +    end + +    test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do +      {:ok, _like_undo, _} = SideEffects.handle(like_undo) + +      object = Object.get_by_ap_id(post.data["object"]) + +      assert object.data["like_count"] == 0 +      assert object.data["likes"] == [] +    end + +    test "deletes the original like", %{like_undo: like_undo, like: like} do +      {:ok, _like_undo, _} = SideEffects.handle(like_undo) +      refute Activity.get_by_id(like.id) +    end +  end    describe "like objects" do      setup do        poster = insert(:user)        user = insert(:user) -      {:ok, post} = CommonAPI.post(poster, %{"status" => "hey"}) +      {:ok, post} = CommonAPI.post(poster, %{status: "hey"})        {:ok, like_data, _meta} = Builder.like(user, post.object)        {:ok, like, _meta} = ActivityPub.persist(like_data, local: true) @@ -39,4 +289,61 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do        assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)      end    end + +  describe "announce objects" do +    setup do +      poster = insert(:user) +      user = insert(:user) +      {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) +      {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) + +      {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true) + +      {:ok, private_announce_data, _meta} = +        Builder.announce(user, private_post.object, public: false) + +      {:ok, relay_announce_data, _meta} = +        Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true) + +      {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true) +      {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true) +      {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true) + +      %{ +        announce: announce, +        user: user, +        poster: poster, +        private_announce: private_announce, +        relay_announce: relay_announce +      } +    end + +    test "adds the announce to the original object", %{announce: announce, user: user} do +      {:ok, announce, _} = SideEffects.handle(announce) +      object = Object.get_by_ap_id(announce.data["object"]) +      assert object.data["announcement_count"] == 1 +      assert user.ap_id in object.data["announcements"] +    end + +    test "does not add the announce to the original object if the actor is a service actor", %{ +      relay_announce: announce +    } do +      {:ok, announce, _} = SideEffects.handle(announce) +      object = Object.get_by_ap_id(announce.data["object"]) +      assert object.data["announcement_count"] == nil +    end + +    test "creates a notification", %{announce: announce, poster: poster} do +      {:ok, announce, _} = SideEffects.handle(announce) +      assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id) +    end + +    test "it streams out the announce", %{announce: announce} do +      with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do +        {:ok, announce, _} = SideEffects.handle(announce) + +        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce)) +      end +    end +  end  end diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/web/activity_pub/transmogrifier/announce_handling_test.exs new file mode 100644 index 000000000..e895636b5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -0,0 +1,172 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.Transmogrifier +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  test "it works for incoming honk announces" do +    user = insert(:user, ap_id: "https://honktest/u/test", local: false) +    other_user = insert(:user) +    {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"}) + +    announce = %{ +      "@context" => "https://www.w3.org/ns/activitystreams", +      "actor" => "https://honktest/u/test", +      "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx", +      "object" => post.data["object"], +      "published" => "2019-06-25T19:33:58Z", +      "to" => "https://www.w3.org/ns/activitystreams#Public", +      "type" => "Announce" +    } + +    {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce) + +    object = Object.get_by_ap_id(post.data["object"]) + +    assert length(object.data["announcements"]) == 1 +    assert user.ap_id in object.data["announcements"] +  end + +  test "it works for incoming announces with actor being inlined (kroeg)" do +    data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() + +    _user = insert(:user, local: false, ap_id: data["actor"]["id"]) +    other_user = insert(:user) + +    {:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"}) + +    data = +      data +      |> put_in(["object", "id"], post.data["object"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == "https://puckipedia.com/" +  end + +  test "it works for incoming announces, fetching the announced object" do +    data = +      File.read!("test/fixtures/mastodon-announce.json") +      |> Poison.decode!() +      |> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367") + +    Tesla.Mock.mock(fn +      %{method: :get} -> +        %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")} +    end) + +    _user = insert(:user, local: false, ap_id: data["actor"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == "http://mastodon.example.org/users/admin" +    assert data["type"] == "Announce" + +    assert data["id"] == +             "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + +    assert data["object"] == +             "http://mastodon.example.org/users/admin/statuses/99541947525187367" + +    assert(Activity.get_create_by_object_ap_id(data["object"])) +  end + +  @tag capture_log: true +  test "it works for incoming announces with an existing activity" do +    user = insert(:user) +    {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + +    data = +      File.read!("test/fixtures/mastodon-announce.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) + +    _user = insert(:user, local: false, ap_id: data["actor"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == "http://mastodon.example.org/users/admin" +    assert data["type"] == "Announce" + +    assert data["id"] == +             "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + +    assert data["object"] == activity.data["object"] + +    assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id +  end + +  # Ignore inlined activities for now +  @tag skip: true +  test "it works for incoming announces with an inlined activity" do +    data = +      File.read!("test/fixtures/mastodon-announce-private.json") +      |> Poison.decode!() + +    _user = +      insert(:user, +        local: false, +        ap_id: data["actor"], +        follower_address: data["actor"] <> "/followers" +      ) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == "http://mastodon.example.org/users/admin" +    assert data["type"] == "Announce" + +    assert data["id"] == +             "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + +    object = Object.normalize(data["object"]) + +    assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" +    assert object.data["content"] == "this is a private toot" +  end + +  @tag capture_log: true +  test "it rejects incoming announces with an inlined activity from another origin" do +    Tesla.Mock.mock(fn +      %{method: :get} -> %Tesla.Env{status: 404, body: ""} +    end) + +    data = +      File.read!("test/fixtures/bogus-mastodon-announce.json") +      |> Poison.decode!() + +    _user = insert(:user, local: false, ap_id: data["actor"]) + +    assert {:error, e} = Transmogrifier.handle_incoming(data) +  end + +  test "it does not clobber the addressing on announce activities" do +    user = insert(:user) +    {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + +    data = +      File.read!("test/fixtures/mastodon-announce.json") +      |> Poison.decode!() +      |> Map.put("object", Object.normalize(activity).data["id"]) +      |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"]) +      |> Map.put("cc", []) + +    _user = +      insert(:user, +        local: false, +        ap_id: data["actor"], +        follower_address: "http://mastodon.example.org/users/admin/followers" +      ) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["to"] == ["http://mastodon.example.org/users/admin/followers"] +  end +end diff --git a/test/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/web/activity_pub/transmogrifier/delete_handling_test.exs new file mode 100644 index 000000000..c9a53918c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/delete_handling_test.exs @@ -0,0 +1,114 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.DeleteHandlingTest do +  use Oban.Testing, repo: Pleroma.Repo +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.Tests.ObanHelpers +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.Transmogrifier + +  import Pleroma.Factory + +  setup_all do +    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end + +  test "it works for incoming deletes" do +    activity = insert(:note_activity) +    deleting_user = insert(:user) + +    data = +      File.read!("test/fixtures/mastodon-delete.json") +      |> Poison.decode!() +      |> Map.put("actor", deleting_user.ap_id) +      |> put_in(["object", "id"], activity.data["object"]) + +    {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = +      Transmogrifier.handle_incoming(data) + +    assert id == data["id"] + +    # We delete the Create activity because we base our timelines on it. +    # This should be changed after we unify objects and activities +    refute Activity.get_by_id(activity.id) +    assert actor == deleting_user.ap_id + +    # Objects are replaced by a tombstone object. +    object = Object.normalize(activity.data["object"]) +    assert object.data["type"] == "Tombstone" +  end + +  test "it works for incoming when the object has been pruned" do +    activity = insert(:note_activity) + +    {:ok, object} = +      Object.normalize(activity.data["object"]) +      |> Repo.delete() + +    Cachex.del(:object_cache, "object:#{object.data["id"]}") + +    deleting_user = insert(:user) + +    data = +      File.read!("test/fixtures/mastodon-delete.json") +      |> Poison.decode!() +      |> Map.put("actor", deleting_user.ap_id) +      |> put_in(["object", "id"], activity.data["object"]) + +    {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = +      Transmogrifier.handle_incoming(data) + +    assert id == data["id"] + +    # We delete the Create activity because we base our timelines on it. +    # This should be changed after we unify objects and activities +    refute Activity.get_by_id(activity.id) +    assert actor == deleting_user.ap_id +  end + +  test "it fails for incoming deletes with spoofed origin" do +    activity = insert(:note_activity) +    %{ap_id: ap_id} = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + +    data = +      File.read!("test/fixtures/mastodon-delete.json") +      |> Poison.decode!() +      |> Map.put("actor", ap_id) +      |> put_in(["object", "id"], activity.data["object"]) + +    assert match?({:error, _}, Transmogrifier.handle_incoming(data)) +  end + +  @tag capture_log: true +  test "it works for incoming user deletes" do +    %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + +    data = +      File.read!("test/fixtures/mastodon-delete-user.json") +      |> Poison.decode!() + +    {:ok, _} = Transmogrifier.handle_incoming(data) +    ObanHelpers.perform_all() + +    assert User.get_cached_by_ap_id(ap_id).deactivated +  end + +  test "it fails for incoming user deletes with spoofed origin" do +    %{ap_id: ap_id} = insert(:user) + +    data = +      File.read!("test/fixtures/mastodon-delete-user.json") +      |> Poison.decode!() +      |> Map.put("actor", ap_id) + +    assert match?({:error, _}, Transmogrifier.handle_incoming(data)) + +    assert User.get_cached_by_ap_id(ap_id) +  end +end diff --git a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs new file mode 100644 index 000000000..0fb056b50 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.Transmogrifier +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  test "it works for incoming emoji reactions" do +    user = insert(:user) +    other_user = insert(:user, local: false) +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + +    data = +      File.read!("test/fixtures/emoji-reaction.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) +      |> Map.put("actor", other_user.ap_id) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == other_user.ap_id +    assert data["type"] == "EmojiReact" +    assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" +    assert data["object"] == activity.data["object"] +    assert data["content"] == "👌" + +    object = Object.get_by_ap_id(data["object"]) + +    assert object.data["reaction_count"] == 1 +    assert match?([["👌", _]], object.data["reactions"]) +  end + +  test "it reject invalid emoji reactions" do +    user = insert(:user) +    other_user = insert(:user, local: false) +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + +    data = +      File.read!("test/fixtures/emoji-reaction-too-long.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) +      |> Map.put("actor", other_user.ap_id) + +    assert {:error, _} = Transmogrifier.handle_incoming(data) + +    data = +      File.read!("test/fixtures/emoji-reaction-no-emoji.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) +      |> Map.put("actor", other_user.ap_id) + +    assert {:error, _} = Transmogrifier.handle_incoming(data) +  end +end diff --git a/test/web/activity_pub/transmogrifier/like_handling_test.exs b/test/web/activity_pub/transmogrifier/like_handling_test.exs index 54a5c1dbc..53fe1d550 100644 --- a/test/web/activity_pub/transmogrifier/like_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/like_handling_test.exs @@ -14,7 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do    test "it works for incoming likes" do      user = insert(:user) -    {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"})      data =        File.read!("test/fixtures/mastodon-like.json") @@ -36,7 +36,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do    test "it works for incoming misskey likes, turning them into EmojiReacts" do      user = insert(:user) -    {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"})      data =        File.read!("test/fixtures/misskey-like.json") @@ -57,7 +57,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do    test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReacts" do      user = insert(:user) -    {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"})      data =        File.read!("test/fixtures/misskey-like.json") diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/web/activity_pub/transmogrifier/undo_handling_test.exs new file mode 100644 index 000000000..01dd6c370 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/undo_handling_test.exs @@ -0,0 +1,185 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.Transmogrifier +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  test "it works for incoming emoji reaction undos" do +    user = insert(:user) + +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) +    {:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌") + +    data = +      File.read!("test/fixtures/mastodon-undo-like.json") +      |> Poison.decode!() +      |> Map.put("object", reaction_activity.data["id"]) +      |> Map.put("actor", user.ap_id) + +    {:ok, activity} = Transmogrifier.handle_incoming(data) + +    assert activity.actor == user.ap_id +    assert activity.data["id"] == data["id"] +    assert activity.data["type"] == "Undo" +  end + +  test "it returns an error for incoming unlikes wihout a like activity" do +    user = insert(:user) +    {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + +    data = +      File.read!("test/fixtures/mastodon-undo-like.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) + +    assert Transmogrifier.handle_incoming(data) == :error +  end + +  test "it works for incoming unlikes with an existing like activity" do +    user = insert(:user) +    {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + +    like_data = +      File.read!("test/fixtures/mastodon-like.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) + +    _liker = insert(:user, ap_id: like_data["actor"], local: false) + +    {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) + +    data = +      File.read!("test/fixtures/mastodon-undo-like.json") +      |> Poison.decode!() +      |> Map.put("object", like_data) +      |> Map.put("actor", like_data["actor"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == "http://mastodon.example.org/users/admin" +    assert data["type"] == "Undo" +    assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" +    assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" + +    note = Object.get_by_ap_id(like_data["object"]) +    assert note.data["like_count"] == 0 +    assert note.data["likes"] == [] +  end + +  test "it works for incoming unlikes with an existing like activity and a compact object" do +    user = insert(:user) +    {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + +    like_data = +      File.read!("test/fixtures/mastodon-like.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) + +    _liker = insert(:user, ap_id: like_data["actor"], local: false) + +    {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) + +    data = +      File.read!("test/fixtures/mastodon-undo-like.json") +      |> Poison.decode!() +      |> Map.put("object", like_data["id"]) +      |> Map.put("actor", like_data["actor"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == "http://mastodon.example.org/users/admin" +    assert data["type"] == "Undo" +    assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" +    assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" +  end + +  test "it works for incoming unannounces with an existing notice" do +    user = insert(:user) +    {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + +    announce_data = +      File.read!("test/fixtures/mastodon-announce.json") +      |> Poison.decode!() +      |> Map.put("object", activity.data["object"]) + +    _announcer = insert(:user, ap_id: announce_data["actor"], local: false) + +    {:ok, %Activity{data: announce_data, local: false}} = +      Transmogrifier.handle_incoming(announce_data) + +    data = +      File.read!("test/fixtures/mastodon-undo-announce.json") +      |> Poison.decode!() +      |> Map.put("object", announce_data) +      |> Map.put("actor", announce_data["actor"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["type"] == "Undo" + +    assert data["object"] == +             "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" +  end + +  test "it works for incomming unfollows with an existing follow" do +    user = insert(:user) + +    follow_data = +      File.read!("test/fixtures/mastodon-follow-activity.json") +      |> Poison.decode!() +      |> Map.put("object", user.ap_id) + +    _follower = insert(:user, ap_id: follow_data["actor"], local: false) + +    {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) + +    data = +      File.read!("test/fixtures/mastodon-unfollow-activity.json") +      |> Poison.decode!() +      |> Map.put("object", follow_data) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["type"] == "Undo" +    assert data["object"]["type"] == "Follow" +    assert data["object"]["object"] == user.ap_id +    assert data["actor"] == "http://mastodon.example.org/users/admin" + +    refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) +  end + +  test "it works for incoming unblocks with an existing block" do +    user = insert(:user) + +    block_data = +      File.read!("test/fixtures/mastodon-block-activity.json") +      |> Poison.decode!() +      |> Map.put("object", user.ap_id) + +    _blocker = insert(:user, ap_id: block_data["actor"], local: false) + +    {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) + +    data = +      File.read!("test/fixtures/mastodon-unblock-activity.json") +      |> Poison.decode!() +      |> Map.put("object", block_data) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) +    assert data["type"] == "Undo" +    assert data["object"] == block_data["id"] + +    blocker = User.get_cached_by_ap_id(data["actor"]) + +    refute User.blocks?(blocker, user) +  end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 23efa4be6..94d8552e8 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -28,6 +28,63 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do    setup do: clear_config([:instance, :max_remote_account_fields])    describe "handle_incoming" do +    test "it works for incoming notices with tag not being an array (kroeg)" do +      data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!() + +      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) +      object = Object.normalize(data["object"]) + +      assert object.data["emoji"] == %{ +               "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png" +             } + +      data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!() + +      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) +      object = Object.normalize(data["object"]) + +      assert "test" in object.data["tag"] +    end + +    test "it works for incoming notices with url not being a string (prismo)" do +      data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!() + +      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) +      object = Object.normalize(data["object"]) + +      assert object.data["url"] == "https://prismo.news/posts/83" +    end + +    test "it cleans up incoming notices which are not really DMs" do +      user = insert(:user) +      other_user = insert(:user) + +      to = [user.ap_id, other_user.ap_id] + +      data = +        File.read!("test/fixtures/mastodon-post-activity.json") +        |> Poison.decode!() +        |> Map.put("to", to) +        |> Map.put("cc", []) + +      object = +        data["object"] +        |> Map.put("to", to) +        |> Map.put("cc", []) + +      data = Map.put(data, "object", object) + +      {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) + +      assert data["to"] == [] +      assert data["cc"] == to + +      object_data = Object.normalize(activity).data + +      assert object_data["to"] == [] +      assert object_data["cc"] == to +    end +      test "it ignores an incoming notice if we already have it" do        activity = insert(:note_activity) @@ -212,8 +269,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => "suya...", -          "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10} +          status: "suya...", +          poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10}          })        object = Object.normalize(activity) @@ -260,272 +317,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do                 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"      end -    test "it works for incoming announces with actor being inlined (kroeg)" do -      data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "https://puckipedia.com/" -    end - -    test "it works for incoming notices with tag not being an array (kroeg)" do -      data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) -      object = Object.normalize(data["object"]) - -      assert object.data["emoji"] == %{ -               "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png" -             } - -      data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) -      object = Object.normalize(data["object"]) - -      assert "test" in object.data["tag"] -    end - -    test "it works for incoming notices with url not being a string (prismo)" do -      data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) -      object = Object.normalize(data["object"]) - -      assert object.data["url"] == "https://prismo.news/posts/83" -    end - -    test "it cleans up incoming notices which are not really DMs" do -      user = insert(:user) -      other_user = insert(:user) - -      to = [user.ap_id, other_user.ap_id] - -      data = -        File.read!("test/fixtures/mastodon-post-activity.json") -        |> Poison.decode!() -        |> Map.put("to", to) -        |> Map.put("cc", []) - -      object = -        data["object"] -        |> Map.put("to", to) -        |> Map.put("cc", []) - -      data = Map.put(data, "object", object) - -      {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - -      assert data["to"] == [] -      assert data["cc"] == to - -      object_data = Object.normalize(activity).data - -      assert object_data["to"] == [] -      assert object_data["cc"] == to -    end - -    test "it works for incoming emoji reactions" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - -      data = -        File.read!("test/fixtures/emoji-reaction.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "http://mastodon.example.org/users/admin" -      assert data["type"] == "EmojiReact" -      assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" -      assert data["object"] == activity.data["object"] -      assert data["content"] == "👌" -    end - -    test "it reject invalid emoji reactions" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - -      data = -        File.read!("test/fixtures/emoji-reaction-too-long.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      assert :error = Transmogrifier.handle_incoming(data) - -      data = -        File.read!("test/fixtures/emoji-reaction-no-emoji.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      assert :error = Transmogrifier.handle_incoming(data) -    end - -    test "it works for incoming emoji reaction undos" do -      user = insert(:user) - -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) -      {:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "👌") - -      data = -        File.read!("test/fixtures/mastodon-undo-like.json") -        |> Poison.decode!() -        |> Map.put("object", reaction_activity.data["id"]) -        |> Map.put("actor", user.ap_id) - -      {:ok, activity} = Transmogrifier.handle_incoming(data) - -      assert activity.actor == user.ap_id -      assert activity.data["id"] == data["id"] -      assert activity.data["type"] == "Undo" -    end - -    test "it returns an error for incoming unlikes wihout a like activity" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) - -      data = -        File.read!("test/fixtures/mastodon-undo-like.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      assert Transmogrifier.handle_incoming(data) == :error -    end - -    test "it works for incoming unlikes with an existing like activity" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) - -      like_data = -        File.read!("test/fixtures/mastodon-like.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - -      data = -        File.read!("test/fixtures/mastodon-undo-like.json") -        |> Poison.decode!() -        |> Map.put("object", like_data) -        |> Map.put("actor", like_data["actor"]) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "http://mastodon.example.org/users/admin" -      assert data["type"] == "Undo" -      assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" -      assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2" -    end - -    test "it works for incoming unlikes with an existing like activity and a compact object" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) - -      like_data = -        File.read!("test/fixtures/mastodon-like.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - -      data = -        File.read!("test/fixtures/mastodon-undo-like.json") -        |> Poison.decode!() -        |> Map.put("object", like_data["id"]) -        |> Map.put("actor", like_data["actor"]) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "http://mastodon.example.org/users/admin" -      assert data["type"] == "Undo" -      assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" -      assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2" -    end - -    test "it works for incoming announces" do -      data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "http://mastodon.example.org/users/admin" -      assert data["type"] == "Announce" - -      assert data["id"] == -               "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - -      assert data["object"] == -               "http://mastodon.example.org/users/admin/statuses/99541947525187367" - -      assert Activity.get_create_by_object_ap_id(data["object"]) -    end - -    test "it works for incoming announces with an existing activity" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) - -      data = -        File.read!("test/fixtures/mastodon-announce.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "http://mastodon.example.org/users/admin" -      assert data["type"] == "Announce" - -      assert data["id"] == -               "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - -      assert data["object"] == activity.data["object"] - -      assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id -    end - -    test "it works for incoming announces with an inlined activity" do -      data = -        File.read!("test/fixtures/mastodon-announce-private.json") -        |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["actor"] == "http://mastodon.example.org/users/admin" -      assert data["type"] == "Announce" - -      assert data["id"] == -               "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - -      object = Object.normalize(data["object"]) - -      assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" -      assert object.data["content"] == "this is a private toot" -    end - -    @tag capture_log: true -    test "it rejects incoming announces with an inlined activity from another origin" do -      data = -        File.read!("test/fixtures/bogus-mastodon-announce.json") -        |> Poison.decode!() - -      assert :error = Transmogrifier.handle_incoming(data) -    end - -    test "it does not clobber the addressing on announce activities" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) - -      data = -        File.read!("test/fixtures/mastodon-announce.json") -        |> Poison.decode!() -        |> Map.put("object", Object.normalize(activity).data["id"]) -        |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"]) -        |> Map.put("cc", []) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["to"] == ["http://mastodon.example.org/users/admin/followers"] -    end -      test "it ensures that as:Public activities make it to their followers collection" do        user = insert(:user) @@ -598,8 +389,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "it strips internal reactions" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) -      {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") +      {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) +      {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢")        %{object: object} = Activity.get_by_id_with_object(activity.id)        assert Map.has_key?(object.data, "reactions") @@ -766,113 +557,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        assert user.locked == true      end -    test "it works for incoming deletes" do -      activity = insert(:note_activity) -      deleting_user = insert(:user) - -      data = -        File.read!("test/fixtures/mastodon-delete.json") -        |> Poison.decode!() - -      object = -        data["object"] -        |> Map.put("id", activity.data["object"]) - -      data = -        data -        |> Map.put("object", object) -        |> Map.put("actor", deleting_user.ap_id) - -      {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = -        Transmogrifier.handle_incoming(data) - -      assert id == data["id"] -      refute Activity.get_by_id(activity.id) -      assert actor == deleting_user.ap_id -    end - -    test "it fails for incoming deletes with spoofed origin" do -      activity = insert(:note_activity) - -      data = -        File.read!("test/fixtures/mastodon-delete.json") -        |> Poison.decode!() - -      object = -        data["object"] -        |> Map.put("id", activity.data["object"]) - -      data = -        data -        |> Map.put("object", object) - -      assert capture_log(fn -> -               :error = Transmogrifier.handle_incoming(data) -             end) =~ -               "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, :nxdomain}" - -      assert Activity.get_by_id(activity.id) -    end - -    @tag capture_log: true -    test "it works for incoming user deletes" do -      %{ap_id: ap_id} = -        insert(:user, ap_id: "http://mastodon.example.org/users/admin", local: false) - -      data = -        File.read!("test/fixtures/mastodon-delete-user.json") -        |> Poison.decode!() - -      {:ok, _} = Transmogrifier.handle_incoming(data) -      ObanHelpers.perform_all() - -      refute User.get_cached_by_ap_id(ap_id) -    end - -    test "it fails for incoming user deletes with spoofed origin" do -      %{ap_id: ap_id} = insert(:user) - -      data = -        File.read!("test/fixtures/mastodon-delete-user.json") -        |> Poison.decode!() -        |> Map.put("actor", ap_id) - -      assert capture_log(fn -> -               assert :error == Transmogrifier.handle_incoming(data) -             end) =~ "Object containment failed" - -      assert User.get_cached_by_ap_id(ap_id) -    end - -    test "it works for incoming unannounces with an existing notice" do -      user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) - -      announce_data = -        File.read!("test/fixtures/mastodon-announce.json") -        |> Poison.decode!() -        |> Map.put("object", activity.data["object"]) - -      {:ok, %Activity{data: announce_data, local: false}} = -        Transmogrifier.handle_incoming(announce_data) - -      data = -        File.read!("test/fixtures/mastodon-undo-announce.json") -        |> Poison.decode!() -        |> Map.put("object", announce_data) -        |> Map.put("actor", announce_data["actor"]) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["type"] == "Undo" -      assert object_data = data["object"] -      assert object_data["type"] == "Announce" -      assert object_data["object"] == activity.data["object"] - -      assert object_data["id"] == -               "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" -    end -      test "it works for incomming unfollows with an existing follow" do        user = insert(:user) @@ -967,32 +651,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        refute User.following?(blocked, blocker)      end -    test "it works for incoming unblocks with an existing block" do -      user = insert(:user) - -      block_data = -        File.read!("test/fixtures/mastodon-block-activity.json") -        |> Poison.decode!() -        |> Map.put("object", user.ap_id) - -      {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) - -      data = -        File.read!("test/fixtures/mastodon-unblock-activity.json") -        |> Poison.decode!() -        |> Map.put("object", block_data) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) -      assert data["type"] == "Undo" -      assert data["object"]["type"] == "Block" -      assert data["object"]["object"] == user.ap_id -      assert data["actor"] == "http://mastodon.example.org/users/admin" - -      blocker = User.get_cached_by_ap_id(data["actor"]) - -      refute User.blocks?(blocker, user) -    end -      test "it works for incoming accepts which were pre-accepted" do        follower = insert(:user)        followed = insert(:user) @@ -1066,6 +724,12 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        follower = User.get_cached_by_id(follower.id)        assert User.following?(follower, followed) == true + +      follower = User.get_by_id(follower.id) +      assert follower.following_count == 1 + +      followed = User.get_by_id(followed.id) +      assert followed.follower_count == 1      end      test "it fails for incoming accepts which cannot be correlated" do @@ -1223,7 +887,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        user = insert(:user)        other_user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "test post"})        object = Object.normalize(activity)        note_obj = %{ @@ -1367,13 +1031,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      setup do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "post1"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "post1"})        {:ok, reply1} = -        CommonAPI.post(user, %{"status" => "reply1", "in_reply_to_status_id" => activity.id}) +        CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})        {:ok, reply2} = -        CommonAPI.post(user, %{"status" => "reply2", "in_reply_to_status_id" => activity.id}) +        CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})        replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end) @@ -1413,9 +1077,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "it inlines private announced objects" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey", "visibility" => "private"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"}) -      {:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user) +      {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)        {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data) @@ -1428,31 +1092,36 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        other_user = insert(:user)        {:ok, activity} = -        CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"}) +        CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) -      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) -      object = modified["object"] +      with_mock Pleroma.Notification, +        get_notified_from_activity: fn _, _ -> [] end do +        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) -      expected_mention = %{ -        "href" => other_user.ap_id, -        "name" => "@#{other_user.nickname}", -        "type" => "Mention" -      } +        object = modified["object"] -      expected_tag = %{ -        "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", -        "type" => "Hashtag", -        "name" => "#2hu" -      } +        expected_mention = %{ +          "href" => other_user.ap_id, +          "name" => "@#{other_user.nickname}", +          "type" => "Mention" +        } + +        expected_tag = %{ +          "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", +          "type" => "Hashtag", +          "name" => "#2hu" +        } -      assert Enum.member?(object["tag"], expected_tag) -      assert Enum.member?(object["tag"], expected_mention) +        refute called(Pleroma.Notification.get_notified_from_activity(:_, :_)) +        assert Enum.member?(object["tag"], expected_tag) +        assert Enum.member?(object["tag"], expected_mention) +      end      end      test "it adds the sensitive property" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)        assert modified["object"]["sensitive"] @@ -1461,7 +1130,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "it adds the json-ld context and the conversation property" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "hey"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)        assert modified["@context"] == @@ -1473,7 +1142,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "hey"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)        assert modified["object"]["actor"] == modified["object"]["attributedTo"] @@ -1482,7 +1151,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "it strips internal hashtag data" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"})        expected_tag = %{          "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", @@ -1498,7 +1167,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "it strips internal fields" do        user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1530,14 +1199,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        user = insert(:user)        other_user = insert(:user) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)        assert modified["directMessage"] == false -      {:ok, activity} = -        CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1545,8 +1213,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => "@#{other_user.nickname} :moominmamma:", -          "visibility" => "direct" +          status: "@#{other_user.nickname} :moominmamma:", +          visibility: "direct"          })        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1558,8 +1226,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        user = insert(:user)        {:ok, list} = Pleroma.List.create("foo", user) -      {:ok, activity} = -        CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})        {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1594,8 +1261,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        user_two = insert(:user)        Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) -      {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) -      {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "test"}) +      {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"})        assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients        user = User.get_cached_by_id(user.id) @@ -1667,7 +1334,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        }        assert capture_log(fn -> -               :error = Transmogrifier.handle_incoming(data) +               {:error, _} = Transmogrifier.handle_incoming(data)               end) =~ "Object containment failed"      end @@ -1682,7 +1349,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        }        assert capture_log(fn -> -               :error = Transmogrifier.handle_incoming(data) +               {:error, _} = Transmogrifier.handle_incoming(data)               end) =~ "Object containment failed"      end @@ -1697,7 +1364,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        }        assert capture_log(fn -> -               :error = Transmogrifier.handle_incoming(data) +               {:error, _} = Transmogrifier.handle_incoming(data)               end) =~ "Object containment failed"      end    end @@ -1761,8 +1428,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      {:ok, poll_activity} =        CommonAPI.post(user, %{ -        "status" => "suya...", -        "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10} +        status: "suya...", +        poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10}        })      poll_object = Object.normalize(poll_activity) @@ -2105,28 +1772,27 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do      test "sets `replies` collection with a limited number of self-replies" do        [user, another_user] = insert_list(2, :user) -      {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{"status" => "1"}) +      {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"})        {:ok, %{id: id2} = self_reply1} = -        CommonAPI.post(user, %{"status" => "self-reply 1", "in_reply_to_status_id" => id1}) +        CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1})        {:ok, self_reply2} = -        CommonAPI.post(user, %{"status" => "self-reply 2", "in_reply_to_status_id" => id1}) +        CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1})        # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2 -      {:ok, _} = -        CommonAPI.post(user, %{"status" => "self-reply 3", "in_reply_to_status_id" => id1}) +      {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1})        {:ok, _} =          CommonAPI.post(user, %{ -          "status" => "self-reply to self-reply", -          "in_reply_to_status_id" => id2 +          status: "self-reply to self-reply", +          in_reply_to_status_id: id2          })        {:ok, _} =          CommonAPI.post(another_user, %{ -          "status" => "another user's reply", -          "in_reply_to_status_id" => id1 +          status: "another user's reply", +          in_reply_to_status_id: id1          })        object = Object.normalize(activity) diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index b0bfed917..15f03f193 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -102,34 +102,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do      end    end -  describe "make_unlike_data/3" do -    test "returns data for unlike activity" do -      user = insert(:user) -      like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"}) - -      object = Object.normalize(like_activity.data["object"]) - -      assert Utils.make_unlike_data(user, like_activity, nil) == %{ -               "type" => "Undo", -               "actor" => user.ap_id, -               "object" => like_activity.data, -               "to" => [user.follower_address, object.data["actor"]], -               "cc" => [Pleroma.Constants.as_public()], -               "context" => like_activity.data["context"] -             } - -      assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{ -               "type" => "Undo", -               "actor" => user.ap_id, -               "object" => like_activity.data, -               "to" => [user.follower_address, object.data["actor"]], -               "cc" => [Pleroma.Constants.as_public()], -               "context" => like_activity.data["context"], -               "id" => "9mJEZK0tky1w2xD2vY" -             } -    end -  end -    describe "make_like_data" do      setup do        user = insert(:user) @@ -148,7 +120,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => +          status:              "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"          }) @@ -167,8 +139,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", -          "visibility" => "private" +          status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", +          visibility: "private"          })        %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) @@ -196,11 +168,11 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => "How do I pronounce LaTeX?", -          "poll" => %{ -            "options" => ["laytekh", "lahtekh", "latex"], -            "expires_in" => 20, -            "multiple" => true +          status: "How do I pronounce LaTeX?", +          poll: %{ +            options: ["laytekh", "lahtekh", "latex"], +            expires_in: 20, +            multiple: true            }          }) @@ -215,10 +187,10 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        {:ok, activity} =          CommonAPI.post(user, %{ -          "status" => "Are we living in a society?", -          "poll" => %{ -            "options" => ["yes", "no"], -            "expires_in" => 20 +          status: "Are we living in a society?", +          poll: %{ +            options: ["yes", "no"], +            expires_in: 20            }          }) @@ -362,7 +334,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        assert object = Object.normalize(note_activity)        actor = insert(:user) -      {:ok, announce, _object} = ActivityPub.announce(actor, object) +      {:ok, announce} = CommonAPI.repeat(note_activity.id, actor)        assert Utils.get_existing_announce(actor.ap_id, object) == announce      end    end @@ -497,7 +469,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do      test "returns map with Flag object" do        reporter = insert(:user)        target_account = insert(:user) -      {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"}) +      {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})        context = Utils.generate_context_id()        content = "foobar" diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs index 6c006206b..f0389845d 100644 --- a/test/web/activity_pub/views/object_view_test.exs +++ b/test/web/activity_pub/views/object_view_test.exs @@ -44,7 +44,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do        activity = insert(:note_activity, user: user)        {:ok, self_reply1} = -        CommonAPI.post(user, %{"status" => "self-reply 1", "in_reply_to_status_id" => activity.id}) +        CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: activity.id})        replies_uris = [self_reply1.object.data["id"]]        result = ObjectView.render("object.json", %{object: refresh_record(activity)}) @@ -73,7 +73,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do      object = Object.normalize(note)      user = insert(:user) -    {:ok, announce_activity, _} = CommonAPI.repeat(note.id, user) +    {:ok, announce_activity} = CommonAPI.repeat(note.id, user)      result = ObjectView.render("object.json", %{object: announce_activity}) diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 8d00893a5..20b0f223c 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -164,7 +164,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do      posts =        for i <- 0..25 do -        {:ok, activity} = CommonAPI.post(user, %{"status" => "post #{i}"}) +        {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"})          activity        end diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs index 5b91630d4..8e9354c65 100644 --- a/test/web/activity_pub/visibilty_test.exs +++ b/test/web/activity_pub/visibilty_test.exs @@ -21,21 +21,21 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do      Pleroma.List.follow(list, unrelated)      {:ok, public} = -      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "public"}) +      CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "public"})      {:ok, private} = -      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "private"}) +      CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "private"})      {:ok, direct} = -      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "direct"}) +      CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "direct"})      {:ok, unlisted} = -      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "unlisted"}) +      CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "unlisted"})      {:ok, list} =        CommonAPI.post(user, %{ -        "status" => "@#{mentioned.nickname}", -        "visibility" => "list:#{list.id}" +        status: "@#{mentioned.nickname}", +        visibility: "list:#{list.id}"        })      %{  | 
