Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into feature/emojireactv...
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
index 1069ade02a0e8751ba11502b1c6a6fd696073265..14c0f57ae86d98614beb8e97c438a23fd234c2bd 100644 (file)
@@ -1,9 +1,11 @@
 # Pleroma: A lightweight social networking server
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
+  use Oban.Testing, repo: Pleroma.Repo
   use Pleroma.DataCase
   use Pleroma.DataCase
+
   alias Pleroma.Activity
   alias Pleroma.Object
   alias Pleroma.Object.Fetcher
   alias Pleroma.Activity
   alias Pleroma.Object
   alias Pleroma.Object.Fetcher
@@ -23,7 +25,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
     :ok
   end
 
     :ok
   end
 
-  clear_config([:instance, :max_remote_account_fields])
+  setup do: clear_config([:instance, :max_remote_account_fields])
 
   describe "handle_incoming" do
     test "it ignores an incoming notice if we already have it" do
 
   describe "handle_incoming" do
     test "it ignores an incoming notice if we already have it" do
@@ -39,7 +41,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert activity == returned_activity
     end
 
       assert activity == returned_activity
     end
 
-    test "it fetches replied-to activities if we don't have them" do
+    @tag capture_log: true
+    test "it fetches reply-to activities if we don't have them" do
       data =
         File.read!("test/fixtures/mastodon-post-activity.json")
         |> Poison.decode!()
       data =
         File.read!("test/fixtures/mastodon-post-activity.json")
         |> Poison.decode!()
@@ -60,7 +63,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
     end
 
       assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
     end
 
-    test "it does not fetch replied-to activities beyond max_replies_depth" do
+    test "it does not fetch reply-to activities beyond max replies depth limit" do
       data =
         File.read!("test/fixtures/mastodon-post-activity.json")
         |> Poison.decode!()
       data =
         File.read!("test/fixtures/mastodon-post-activity.json")
         |> Poison.decode!()
@@ -72,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       data = Map.put(data, "object", object)
 
       with_mock Pleroma.Web.Federator,
       data = Map.put(data, "object", object)
 
       with_mock Pleroma.Web.Federator,
-        allowed_incoming_reply_depth?: fn _ -> false end do
+        allowed_thread_distance?: fn _ -> false end do
         {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
 
         returned_object = Object.normalize(returned_activity, false)
         {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
 
         returned_object = Object.normalize(returned_activity, false)
@@ -322,21 +325,23 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert object_data["cc"] == to
     end
 
       assert object_data["cc"] == to
     end
 
-    test "it works for incoming likes" do
+    test "it works for incoming emoji reaction undos" do
       user = insert(:user)
       user = insert(:user)
+
       {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
       {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
+      {:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌")
 
       data =
 
       data =
-        File.read!("test/fixtures/mastodon-like.json")
+        File.read!("test/fixtures/mastodon-undo-like.json")
         |> Poison.decode!()
         |> Poison.decode!()
-        |> Map.put("object", activity.data["object"])
+        |> Map.put("object", reaction_activity.data["id"])
+        |> Map.put("actor", user.ap_id)
 
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, activity} = Transmogrifier.handle_incoming(data)
 
 
-      assert data["actor"] == "http://mastodon.example.org/users/admin"
-      assert data["type"] == "Like"
-      assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
-      assert data["object"] == activity.data["object"]
+      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
     end
 
     test "it returns an error for incoming unlikes wihout a like activity" do
@@ -373,7 +378,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       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["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"
+      assert data["object"] == "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
     end
 
     test "it works for incoming unlikes with an existing like activity and a compact object" do
@@ -398,7 +403,44 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       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["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"
+      assert data["object"] == "http://mastodon.example.org/users/admin#likes/2"
+    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 announces" do
     end
 
     test "it works for incoming announces" do
@@ -459,6 +501,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert object.data["content"] == "this is a private toot"
     end
 
       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")
     test "it rejects incoming announces with an inlined activity from another origin" do
       data =
         File.read!("test/fixtures/bogus-mastodon-announce.json")
@@ -553,6 +596,20 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute Map.has_key?(object.data, "likes")
     end
 
       refute Map.has_key?(object.data, "likes")
     end
 
+    test "it strips internal reactions" do
+      user = insert(: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")
+      assert Map.has_key?(object.data, "reaction_count")
+
+      object_data = Transmogrifier.strip_internal_fields(object.data)
+      refute Map.has_key?(object_data, "reactions")
+      refute Map.has_key?(object_data, "reaction_count")
+    end
+
     test "it works for incoming update activities" do
       data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
 
     test "it works for incoming update activities" do
       data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
 
@@ -593,6 +650,37 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert user.bio == "<p>Some bio</p>"
     end
 
       assert user.bio == "<p>Some bio</p>"
     end
 
+    test "it works with alsoKnownAs" do
+      {:ok, %Activity{data: %{"actor" => actor}}} =
+        "test/fixtures/mastodon-post-activity.json"
+        |> File.read!()
+        |> Poison.decode!()
+        |> Transmogrifier.handle_incoming()
+
+      assert User.get_cached_by_ap_id(actor).also_known_as == ["http://example.org/users/foo"]
+
+      {:ok, _activity} =
+        "test/fixtures/mastodon-update.json"
+        |> File.read!()
+        |> Poison.decode!()
+        |> Map.put("actor", actor)
+        |> Map.update!("object", fn object ->
+          object
+          |> Map.put("actor", actor)
+          |> Map.put("id", actor)
+          |> Map.put("alsoKnownAs", [
+            "http://mastodon.example.org/users/foo",
+            "http://example.org/users/bar"
+          ])
+        end)
+        |> Transmogrifier.handle_incoming()
+
+      assert User.get_cached_by_ap_id(actor).also_known_as == [
+               "http://mastodon.example.org/users/foo",
+               "http://example.org/users/bar"
+             ]
+    end
+
     test "it works with custom profile fields" do
       {:ok, activity} =
         "test/fixtures/mastodon-post-activity.json"
     test "it works with custom profile fields" do
       {:ok, activity} =
         "test/fixtures/mastodon-post-activity.json"
@@ -602,7 +690,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(activity.actor)
 
 
       user = User.get_cached_by_ap_id(activity.actor)
 
-      assert User.fields(user) == [
+      assert user.fields == [
                %{"name" => "foo", "value" => "bar"},
                %{"name" => "foo1", "value" => "bar1"}
              ]
                %{"name" => "foo", "value" => "bar"},
                %{"name" => "foo1", "value" => "bar1"}
              ]
@@ -623,7 +711,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
-      assert User.fields(user) == [
+      assert user.fields == [
                %{"name" => "foo", "value" => "updated"},
                %{"name" => "foo1", "value" => "updated"}
              ]
                %{"name" => "foo", "value" => "updated"},
                %{"name" => "foo1", "value" => "updated"}
              ]
@@ -641,7 +729,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
-      assert User.fields(user) == [
+      assert user.fields == [
                %{"name" => "foo", "value" => "updated"},
                %{"name" => "foo1", "value" => "updated"}
              ]
                %{"name" => "foo", "value" => "updated"},
                %{"name" => "foo1", "value" => "updated"}
              ]
@@ -652,7 +740,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
 
       user = User.get_cached_by_ap_id(user.ap_id)
 
-      assert User.fields(user) == []
+      assert user.fields == []
     end
 
     test "it works for incoming update activities which lock the account" do
     end
 
     test "it works for incoming update activities which lock the account" do
@@ -678,108 +766,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert user.locked == true
     end
 
       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
-
-    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()
-
-      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 :error == Transmogrifier.handle_incoming(data)
-      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)
 
     test "it works for incomming unfollows with an existing follow" do
       user = insert(:user)
 
@@ -805,6 +791,25 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
     end
 
       refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
     end
 
+    test "it works for incoming follows to locked account" do
+      pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
+      user = insert(:user, locked: true)
+
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
+
+      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+      assert data["type"] == "Follow"
+      assert data["object"] == user.ap_id
+      assert data["state"] == "pending"
+      assert data["actor"] == "http://mastodon.example.org/users/admin"
+
+      assert [^pending_follower] = User.get_follow_requests(user)
+    end
+
     test "it works for incoming blocks" do
       user = insert(:user)
 
     test "it works for incoming blocks" do
       user = insert(:user)
 
@@ -855,32 +860,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute User.following?(blocked, blocker)
     end
 
       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)
     test "it works for incoming accepts which were pre-accepted" do
       follower = insert(:user)
       followed = insert(:user)
@@ -1054,6 +1033,35 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       :error = Transmogrifier.handle_incoming(data)
     end
 
       :error = Transmogrifier.handle_incoming(data)
     end
 
+    test "skip converting the content when it is nil" do
+      object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe"
+
+      {:ok, object} = Fetcher.fetch_and_contain_remote_object_from_id(object_id)
+
+      result =
+        Pleroma.Web.ActivityPub.Transmogrifier.fix_object(Map.merge(object, %{"content" => nil}))
+
+      assert result["content"] == nil
+    end
+
+    test "it converts content of object to html" do
+      object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe"
+
+      {:ok, %{"content" => content_markdown}} =
+        Fetcher.fetch_and_contain_remote_object_from_id(object_id)
+
+      {:ok, %Pleroma.Object{data: %{"content" => content}} = object} =
+        Fetcher.fetch_object_from_id(object_id)
+
+      assert content_markdown ==
+               "Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership\n\nTwenty Years in Jail: FreeBSD's Jails, Then and Now\n\nJails started as a limited virtualization system, but over the last two years they've..."
+
+      assert content ==
+               "<p>Support this and our other Michigan!/usr/group videos and meetings. Learn more at <a href=\"http://mug.org/membership\">http://mug.org/membership</a></p><p>Twenty Years in Jail: FreeBSD’s Jails, Then and Now</p><p>Jails started as a limited virtualization system, but over the last two years they’ve…</p>"
+
+      assert object.data["mediaType"] == "text/html"
+    end
+
     test "it remaps video URLs as attachments if necessary" do
       {:ok, object} =
         Fetcher.fetch_object_from_id(
     test "it remaps video URLs as attachments if necessary" do
       {:ok, object} =
         Fetcher.fetch_object_from_id(
@@ -1063,19 +1071,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       attachment = %{
         "type" => "Link",
         "mediaType" => "video/mp4",
       attachment = %{
         "type" => "Link",
         "mediaType" => "video/mp4",
-        "href" =>
-          "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
-        "mimeType" => "video/mp4",
-        "size" => 5_015_880,
         "url" => [
           %{
             "href" =>
               "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
         "url" => [
           %{
             "href" =>
               "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
-            "mediaType" => "video/mp4",
-            "type" => "Link"
+            "mediaType" => "video/mp4"
           }
           }
-        ],
-        "width" => 480
+        ]
       }
 
       assert object.data["url"] ==
       }
 
       assert object.data["url"] ==
@@ -1102,7 +1104,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       message = %{
         "@context" => "https://www.w3.org/ns/activitystreams",
         "cc" => [user.ap_id],
       message = %{
         "@context" => "https://www.w3.org/ns/activitystreams",
         "cc" => [user.ap_id],
-        "object" => [user.ap_id, activity],
+        "object" => [user.ap_id, activity.data["id"]],
         "type" => "Flag",
         "content" => "blocked AND reported!!!",
         "actor" => other_user.ap_id
         "type" => "Flag",
         "content" => "blocked AND reported!!!",
         "actor" => other_user.ap_id
@@ -1159,6 +1161,119 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
       assert [user.follower_address] == activity.data["to"]
     end
       assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
       assert [user.follower_address] == activity.data["to"]
     end
+
+    test "it accepts Move activities" do
+      old_user = insert(:user)
+      new_user = insert(:user)
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "type" => "Move",
+        "actor" => old_user.ap_id,
+        "object" => old_user.ap_id,
+        "target" => new_user.ap_id
+      }
+
+      assert :error = Transmogrifier.handle_incoming(message)
+
+      {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
+
+      assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
+      assert activity.actor == old_user.ap_id
+      assert activity.data["actor"] == old_user.ap_id
+      assert activity.data["object"] == old_user.ap_id
+      assert activity.data["target"] == new_user.ap_id
+      assert activity.data["type"] == "Move"
+    end
+  end
+
+  describe "`handle_incoming/2`, Mastodon format `replies` handling" do
+    setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
+    setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
+
+    setup do
+      data =
+        "test/fixtures/mastodon-post-activity.json"
+        |> File.read!()
+        |> Poison.decode!()
+
+      items = get_in(data, ["object", "replies", "first", "items"])
+      assert length(items) > 0
+
+      %{data: data, items: items}
+    end
+
+    test "schedules background fetching of `replies` items if max thread depth limit allows", %{
+      data: data,
+      items: items
+    } do
+      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10)
+
+      {:ok, _activity} = Transmogrifier.handle_incoming(data)
+
+      for id <- items do
+        job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
+        assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
+      end
+    end
+
+    test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
+         %{data: data} do
+      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
+
+      {:ok, _activity} = Transmogrifier.handle_incoming(data)
+
+      assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
+    end
+  end
+
+  describe "`handle_incoming/2`, Pleroma format `replies` handling" do
+    setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
+    setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
+
+    setup do
+      user = insert(:user)
+
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "post1"})
+
+      {:ok, reply1} =
+        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})
+
+      replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
+
+      {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
+
+      Repo.delete(activity.object)
+      Repo.delete(activity)
+
+      %{federation_output: federation_output, replies_uris: replies_uris}
+    end
+
+    test "schedules background fetching of `replies` items if max thread depth limit allows", %{
+      federation_output: federation_output,
+      replies_uris: replies_uris
+    } do
+      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1)
+
+      {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
+
+      for id <- replies_uris do
+        job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
+        assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
+      end
+    end
+
+    test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
+         %{federation_output: federation_output} do
+      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
+
+      {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
+
+      assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
+    end
   end
 
   describe "prepare outgoing" do
   end
 
   describe "prepare outgoing" do
@@ -1343,7 +1458,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
           follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
         })
 
           follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
         })
 
-      user_two = insert(:user, %{following: [user.follower_address]})
+      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"})
@@ -1390,8 +1506,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute user.follower_address in unrelated_activity.recipients
 
       user_two = User.get_cached_by_id(user_two.id)
       refute user.follower_address in unrelated_activity.recipients
 
       user_two = User.get_cached_by_id(user_two.id)
-      assert user.follower_address in user_two.following
-      refute "..." in user_two.following
+      assert User.following?(user_two, user)
+      refute "..." in User.following(user_two)
     end
   end
 
     end
   end
 
@@ -1417,7 +1533,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
         "type" => "Announce"
       }
 
         "type" => "Announce"
       }
 
-      :error = Transmogrifier.handle_incoming(data)
+      assert capture_log(fn ->
+               :error = Transmogrifier.handle_incoming(data)
+             end) =~ "Object containment failed"
     end
 
     test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
     end
 
     test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
@@ -1430,7 +1548,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
         "type" => "Announce"
       }
 
         "type" => "Announce"
       }
 
-      :error = Transmogrifier.handle_incoming(data)
+      assert capture_log(fn ->
+               :error = Transmogrifier.handle_incoming(data)
+             end) =~ "Object containment failed"
     end
 
     test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
     end
 
     test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
@@ -1443,7 +1563,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
         "type" => "Announce"
       }
 
         "type" => "Announce"
       }
 
-      :error = Transmogrifier.handle_incoming(data)
+      assert capture_log(fn ->
+               :error = Transmogrifier.handle_incoming(data)
+             end) =~ "Object containment failed"
     end
   end
 
     end
   end
 
@@ -1591,7 +1713,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
   end
 
   describe "fix_in_reply_to/2" do
   end
 
   describe "fix_in_reply_to/2" do
-    clear_config([:instance, :federation_incoming_replies_max_depth])
+    setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
 
     setup do
       data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
 
     setup do
       data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
@@ -1632,6 +1754,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert modified_object["inReplyToAtomUri"] == ""
     end
 
       assert modified_object["inReplyToAtomUri"] == ""
     end
 
+    @tag capture_log: true
     test "returns modified object when allowed incoming reply", %{data: data} do
       object_with_reply =
         Map.put(
     test "returns modified object when allowed incoming reply", %{data: data} do
       object_with_reply =
         Map.put(
@@ -1746,9 +1869,12 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
   describe "get_obj_helper/2" do
     test "returns nil when cannot normalize object" do
 
   describe "get_obj_helper/2" do
     test "returns nil when cannot normalize object" do
-      refute Transmogrifier.get_obj_helper("test-obj-id")
+      assert capture_log(fn ->
+               refute Transmogrifier.get_obj_helper("test-obj-id")
+             end) =~ "Unsupported URI scheme"
     end
 
     end
 
+    @tag capture_log: true
     test "returns {:ok, %Object{}} for success case" do
       assert {:ok, %Object{}} =
                Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
     test "returns {:ok, %Object{}} for success case" do
       assert {:ok, %Object{}} =
                Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
@@ -1772,11 +1898,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                  %{
                    "mediaType" => "video/mp4",
                    "url" => [
                  %{
                    "mediaType" => "video/mp4",
                    "url" => [
-                     %{
-                       "href" => "https://peertube.moe/stat-480.mp4",
-                       "mediaType" => "video/mp4",
-                       "type" => "Link"
-                     }
+                     %{"href" => "https://peertube.moe/stat-480.mp4", "mediaType" => "video/mp4"}
                    ]
                  }
                ]
                    ]
                  }
                ]
@@ -1794,23 +1916,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                  %{
                    "mediaType" => "video/mp4",
                    "url" => [
                  %{
                    "mediaType" => "video/mp4",
                    "url" => [
-                     %{
-                       "href" => "https://pe.er/stat-480.mp4",
-                       "mediaType" => "video/mp4",
-                       "type" => "Link"
-                     }
+                     %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}
                    ]
                  },
                  %{
                    ]
                  },
                  %{
-                   "href" => "https://pe.er/stat-480.mp4",
                    "mediaType" => "video/mp4",
                    "mediaType" => "video/mp4",
-                   "mimeType" => "video/mp4",
                    "url" => [
                    "url" => [
-                     %{
-                       "href" => "https://pe.er/stat-480.mp4",
-                       "mediaType" => "video/mp4",
-                       "type" => "Link"
-                     }
+                     %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}
                    ]
                  }
                ]
                    ]
                  }
                ]
@@ -1848,4 +1960,61 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
              }
     end
   end
              }
     end
   end
+
+  describe "set_replies/1" do
+    setup do: clear_config([:activitypub, :note_replies_output_limit], 2)
+
+    test "returns unmodified object if activity doesn't have self-replies" do
+      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
+      assert Transmogrifier.set_replies(data) == data
+    end
+
+    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: id2} = self_reply1} =
+        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})
+
+      # 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 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
+        })
+
+      object = Object.normalize(activity)
+      replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end)
+
+      assert %{"type" => "Collection", "items" => ^replies_uris} =
+               Transmogrifier.set_replies(object.data)["replies"]
+    end
+  end
+
+  test "take_emoji_tags/1" do
+    user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}})
+
+    assert Transmogrifier.take_emoji_tags(user) == [
+             %{
+               "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"},
+               "id" => "https://example.org/firefox.png",
+               "name" => ":firefox:",
+               "type" => "Emoji",
+               "updated" => "1970-01-01T00:00:00Z"
+             }
+           ]
+  end
 end
 end