Merge branch 'preload-data' into 'develop'
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
index ebed65b7c950059c3ca2b008f7d70dfc0a7d77ac..100821056203140c700efd960336b8a9337824e9 100644 (file)
@@ -1,20 +1,20 @@
 # 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
-  alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Transmogrifier
+  alias Pleroma.Web.AdminAPI.AccountView
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.OStatus
-  alias Pleroma.Web.Websub.WebsubClientSubscription
 
   import Mock
   import Pleroma.Factory
 
   import Mock
   import Pleroma.Factory
@@ -25,9 +25,66 @@ 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
 
   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)
 
     test "it ignores an incoming notice if we already have it" do
       activity = insert(:note_activity)
 
@@ -41,7 +98,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!()
@@ -62,7 +120,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!()
@@ -74,7 +132,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)
@@ -148,7 +206,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       user = User.get_cached_by_ap_id(object_data["actor"])
 
 
       user = User.get_cached_by_ap_id(object_data["actor"])
 
-      assert user.info.note_count == 1
+      assert user.note_count == 1
     end
 
     test "it works for incoming notices with hashtags" do
     end
 
     test "it works for incoming notices with hashtags" do
@@ -177,13 +235,42 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
              end)
     end
 
              end)
     end
 
+    test "it works for incoming listens" do
+      data = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+        "cc" => [],
+        "type" => "Listen",
+        "id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
+        "actor" => "http://mastodon.example.org/users/admin",
+        "object" => %{
+          "type" => "Audio",
+          "id" => "http://mastodon.example.org/users/admin/listens/1234",
+          "attributedTo" => "http://mastodon.example.org/users/admin",
+          "title" => "lain radio episode 1",
+          "artist" => "lain",
+          "album" => "lain radio",
+          "length" => 180_000
+        }
+      }
+
+      {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
+
+      object = Object.normalize(activity)
+
+      assert object.data["title"] == "lain radio episode 1"
+      assert object.data["artist"] == "lain"
+      assert object.data["album"] == "lain radio"
+      assert object.data["length"] == 180_000
+    end
+
     test "it rewrites Note votes to Answers and increments vote counters on question activities" do
       user = insert(:user)
 
       {:ok, activity} =
         CommonAPI.post(user, %{
     test "it rewrites Note votes to Answers and increments vote counters on question activities" do
       user = insert(:user)
 
       {: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)
         })
 
       object = Object.normalize(activity)
@@ -230,180 +317,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
     end
 
                "<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 likes" do
-      user = insert(:user)
-      {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
-
-      data =
-        File.read!("test/fixtures/mastodon-like.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"] == "Like"
-      assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
-      assert data["object"] == activity.data["object"]
-    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 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 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)
 
     test "it ensures that as:Public activities make it to their followers collection" do
       user = insert(:user)
 
@@ -474,267 +387,79 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute Map.has_key?(object.data, "likes")
     end
 
       refute Map.has_key?(object.data, "likes")
     end
 
-    test "it works for incoming update activities" do
-      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
+    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{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
+      %{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 =
-        update_data["object"]
-        |> Map.put("actor", data["actor"])
-        |> Map.put("id", data["actor"])
+      object_data = Transmogrifier.strip_internal_fields(object.data)
+      refute Map.has_key?(object_data, "reactions")
+      refute Map.has_key?(object_data, "reaction_count")
+    end
 
 
-      update_data =
-        update_data
-        |> Map.put("actor", data["actor"])
-        |> Map.put("object", object)
+    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)
 
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
+      {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
 
 
-      user = User.get_cached_by_ap_id(data["actor"])
-      assert user.name == "gargle"
+      data =
+        File.read!("test/fixtures/mastodon-unfollow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", follow_data)
 
 
-      assert user.avatar["url"] == [
-               %{
-                 "href" =>
-                   "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
-               }
-             ]
+      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
 
 
-      assert user.info.banner["url"] == [
-               %{
-                 "href" =>
-                   "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
-               }
-             ]
+      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"
 
 
-      assert user.bio == "<p>Some bio</p>"
+      refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
     end
 
     end
 
-    test "it works with custom profile fields" do
-      {:ok, activity} =
-        "test/fixtures/mastodon-post-activity.json"
-        |> File.read!()
-        |> Poison.decode!()
-        |> Transmogrifier.handle_incoming()
+    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)
 
 
-      user = User.get_cached_by_ap_id(activity.actor)
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
 
 
-      assert User.Info.fields(user.info) == [
-               %{"name" => "foo", "value" => "bar"},
-               %{"name" => "foo1", "value" => "bar1"}
-             ]
+      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
 
 
-      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
+      assert data["type"] == "Follow"
+      assert data["object"] == user.ap_id
+      assert data["state"] == "pending"
+      assert data["actor"] == "http://mastodon.example.org/users/admin"
 
 
-      object =
-        update_data["object"]
-        |> Map.put("actor", user.ap_id)
-        |> Map.put("id", user.ap_id)
+      assert [^pending_follower] = User.get_follow_requests(user)
+    end
 
 
-      update_data =
-        update_data
-        |> Map.put("actor", user.ap_id)
-        |> Map.put("object", object)
+    test "it works for incoming blocks" do
+      user = insert(:user)
 
 
-      {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data)
+      data =
+        File.read!("test/fixtures/mastodon-block-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
 
 
-      user = User.get_cached_by_ap_id(user.ap_id)
+      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
 
 
-      assert User.Info.fields(user.info) == [
-               %{"name" => "foo", "value" => "updated"},
-               %{"name" => "foo1", "value" => "updated"}
-             ]
+      assert data["type"] == "Block"
+      assert data["object"] == user.ap_id
+      assert data["actor"] == "http://mastodon.example.org/users/admin"
 
 
-      Pleroma.Config.put([:instance, :max_remote_account_fields], 2)
-
-      update_data =
-        put_in(update_data, ["object", "attachment"], [
-          %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"},
-          %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"},
-          %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"}
-        ])
-
-      {:ok, _} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(user.ap_id)
-
-      assert User.Info.fields(user.info) == [
-               %{"name" => "foo", "value" => "updated"},
-               %{"name" => "foo1", "value" => "updated"}
-             ]
-
-      update_data = put_in(update_data, ["object", "attachment"], [])
-
-      {:ok, _} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(user.ap_id)
-
-      assert User.Info.fields(user.info) == []
-    end
-
-    test "it works for incoming update activities which lock the account" do
-      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
-
-      object =
-        update_data["object"]
-        |> Map.put("actor", data["actor"])
-        |> Map.put("id", data["actor"])
-        |> Map.put("manuallyApprovesFollowers", true)
-
-      update_data =
-        update_data
-        |> Map.put("actor", data["actor"])
-        |> Map.put("object", object)
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
-
-      user = User.get_cached_by_ap_id(data["actor"])
-      assert user.info.locked == true
-    end
-
-    test "it works for incoming deletes" 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)
-        |> Map.put("actor", activity.data["actor"])
-
-      {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
-
-      refute Activity.get_by_id(activity.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, {: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)
-
-      follow_data =
-        File.read!("test/fixtures/mastodon-follow-activity.json")
-        |> Poison.decode!()
-        |> Map.put("object", user.ap_id)
-
-      {: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 blocks" do
-      user = insert(:user)
-
-      data =
-        File.read!("test/fixtures/mastodon-block-activity.json")
-        |> Poison.decode!()
-        |> Map.put("object", user.ap_id)
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-
-      assert data["type"] == "Block"
-      assert data["object"] == user.ap_id
-      assert data["actor"] == "http://mastodon.example.org/users/admin"
-
-      blocker = User.get_cached_by_ap_id(data["actor"])
+      blocker = User.get_cached_by_ap_id(data["actor"])
 
       assert User.blocks?(blocker, user)
     end
 
       assert User.blocks?(blocker, user)
     end
@@ -770,32 +495,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)
@@ -822,6 +521,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       assert activity.data["object"] == follow_activity.data["id"]
 
 
       assert activity.data["object"] == follow_activity.data["id"]
 
+      assert activity.data["id"] == accept_data["id"]
+
       follower = User.get_cached_by_id(follower.id)
 
       assert User.following?(follower, followed) == true
       follower = User.get_cached_by_id(follower.id)
 
       assert User.following?(follower, followed) == true
@@ -829,7 +530,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming accepts which were orphaned" do
       follower = insert(:user)
 
     test "it works for incoming accepts which were orphaned" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
 
 
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
 
@@ -851,7 +552,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it works for incoming accepts which are referenced by IRI only" do
       follower = insert(:user)
 
     test "it works for incoming accepts which are referenced by IRI only" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
 
 
       {:ok, follow_activity} = ActivityPub.follow(follower, followed)
 
@@ -867,11 +568,17 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       follower = User.get_cached_by_id(follower.id)
 
       assert User.following?(follower, followed) == true
       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
       follower = insert(:user)
     end
 
     test "it fails for incoming accepts which cannot be correlated" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       accept_data =
         File.read!("test/fixtures/mastodon-accept-activity.json")
 
       accept_data =
         File.read!("test/fixtures/mastodon-accept-activity.json")
@@ -890,168 +597,375 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     test "it fails for incoming rejects which cannot be correlated" do
       follower = insert(:user)
 
     test "it fails for incoming rejects which cannot be correlated" do
       follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      followed = insert(:user, locked: true)
 
       accept_data =
         File.read!("test/fixtures/mastodon-reject-activity.json")
         |> Poison.decode!()
         |> Map.put("actor", followed.ap_id)
 
 
       accept_data =
         File.read!("test/fixtures/mastodon-reject-activity.json")
         |> Poison.decode!()
         |> Map.put("actor", followed.ap_id)
 
-      accept_data =
-        Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
+      accept_data =
+        Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
+
+      :error = Transmogrifier.handle_incoming(accept_data)
+
+      follower = User.get_cached_by_id(follower.id)
+
+      refute User.following?(follower, followed) == true
+    end
+
+    test "it works for incoming rejects which are orphaned" do
+      follower = insert(:user)
+      followed = insert(:user, locked: true)
+
+      {:ok, follower} = User.follow(follower, followed)
+      {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
+
+      assert User.following?(follower, followed) == true
+
+      reject_data =
+        File.read!("test/fixtures/mastodon-reject-activity.json")
+        |> Poison.decode!()
+        |> Map.put("actor", followed.ap_id)
+
+      reject_data =
+        Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
+
+      {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
+      refute activity.local
+      assert activity.data["id"] == reject_data["id"]
+
+      follower = User.get_cached_by_id(follower.id)
+
+      assert User.following?(follower, followed) == false
+    end
+
+    test "it works for incoming rejects which are referenced by IRI only" do
+      follower = insert(:user)
+      followed = insert(:user, locked: true)
+
+      {:ok, follower} = User.follow(follower, followed)
+      {:ok, follow_activity} = ActivityPub.follow(follower, followed)
+
+      assert User.following?(follower, followed) == true
+
+      reject_data =
+        File.read!("test/fixtures/mastodon-reject-activity.json")
+        |> Poison.decode!()
+        |> Map.put("actor", followed.ap_id)
+        |> Map.put("object", follow_activity.data["id"])
+
+      {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
+
+      follower = User.get_cached_by_id(follower.id)
+
+      assert User.following?(follower, followed) == false
+    end
+
+    test "it rejects activities without a valid ID" do
+      user = insert(:user)
+
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
+        |> Map.put("id", "")
+
+      :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(
+          "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
+        )
+
+      attachment = %{
+        "type" => "Link",
+        "mediaType" => "video/mp4",
+        "url" => [
+          %{
+            "href" =>
+              "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
+            "mediaType" => "video/mp4"
+          }
+        ]
+      }
+
+      assert object.data["url"] ==
+               "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
+
+      assert object.data["attachment"] == [attachment]
+    end
+
+    test "it accepts Flag activities" do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
+      object = Object.normalize(activity)
+
+      note_obj = %{
+        "type" => "Note",
+        "id" => activity.data["id"],
+        "content" => "test post",
+        "published" => object.data["published"],
+        "actor" => AccountView.render("show.json", %{user: user})
+      }
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "cc" => [user.ap_id],
+        "object" => [user.ap_id, activity.data["id"]],
+        "type" => "Flag",
+        "content" => "blocked AND reported!!!",
+        "actor" => other_user.ap_id
+      }
+
+      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+      assert activity.data["object"] == [user.ap_id, note_obj]
+      assert activity.data["content"] == "blocked AND reported!!!"
+      assert activity.data["actor"] == other_user.ap_id
+      assert activity.data["cc"] == [user.ap_id]
+    end
+
+    test "it correctly processes messages with non-array to field" do
+      user = insert(:user)
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "to" => "https://www.w3.org/ns/activitystreams#Public",
+        "type" => "Create",
+        "object" => %{
+          "content" => "blah blah blah",
+          "type" => "Note",
+          "attributedTo" => user.ap_id,
+          "inReplyTo" => nil
+        },
+        "actor" => user.ap_id
+      }
+
+      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+      assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
+    end
+
+    test "it correctly processes messages with non-array cc field" do
+      user = insert(:user)
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "to" => user.follower_address,
+        "cc" => "https://www.w3.org/ns/activitystreams#Public",
+        "type" => "Create",
+        "object" => %{
+          "content" => "blah blah blah",
+          "type" => "Note",
+          "attributedTo" => user.ap_id,
+          "inReplyTo" => nil
+        },
+        "actor" => user.ap_id
+      }
+
+      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+      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])
 
 
-      :error = Transmogrifier.handle_incoming(accept_data)
+    setup do
+      data =
+        "test/fixtures/mastodon-post-activity.json"
+        |> File.read!()
+        |> Poison.decode!()
 
 
-      follower = User.get_cached_by_id(follower.id)
+      items = get_in(data, ["object", "replies", "first", "items"])
+      assert length(items) > 0
 
 
-      refute User.following?(follower, followed) == true
+      %{data: data, items: items}
     end
 
     end
 
-    test "it works for incoming rejects which are orphaned" do
-      follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+    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, follower} = User.follow(follower, followed)
-      {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
+      {:ok, _activity} = Transmogrifier.handle_incoming(data)
 
 
-      assert User.following?(follower, followed) == true
+      for id <- items do
+        job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
+        assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
+      end
+    end
 
 
-      reject_data =
-        File.read!("test/fixtures/mastodon-reject-activity.json")
-        |> Poison.decode!()
-        |> Map.put("actor", followed.ap_id)
+    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)
 
 
-      reject_data =
-        Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
+      {:ok, _activity} = Transmogrifier.handle_incoming(data)
 
 
-      {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
-      refute activity.local
+      assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
+    end
+  end
 
 
-      follower = User.get_cached_by_id(follower.id)
+  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])
 
 
-      assert User.following?(follower, followed) == false
-    end
+    setup do
+      user = insert(:user)
 
 
-    test "it works for incoming rejects which are referenced by IRI only" do
-      follower = insert(:user)
-      followed = insert(:user, %{info: %User.Info{locked: true}})
+      {:ok, activity} = CommonAPI.post(user, %{status: "post1"})
 
 
-      {:ok, follower} = User.follow(follower, followed)
-      {:ok, follow_activity} = ActivityPub.follow(follower, followed)
+      {:ok, reply1} =
+        CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
 
 
-      assert User.following?(follower, followed) == true
+      {:ok, reply2} =
+        CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})
 
 
-      reject_data =
-        File.read!("test/fixtures/mastodon-reject-activity.json")
-        |> Poison.decode!()
-        |> Map.put("actor", followed.ap_id)
-        |> Map.put("object", follow_activity.data["id"])
+      replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
 
 
-      {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
+      {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
 
 
-      follower = User.get_cached_by_id(follower.id)
+      Repo.delete(activity.object)
+      Repo.delete(activity)
 
 
-      assert User.following?(follower, followed) == false
+      %{federation_output: federation_output, replies_uris: replies_uris}
     end
 
     end
 
-    test "it rejects activities without a valid ID" do
-      user = insert(:user)
+    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)
 
 
-      data =
-        File.read!("test/fixtures/mastodon-follow-activity.json")
-        |> Poison.decode!()
-        |> Map.put("object", user.ap_id)
-        |> Map.put("id", "")
+      {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
 
 
-      :error = Transmogrifier.handle_incoming(data)
+      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
 
     end
 
-    test "it remaps video URLs as attachments if necessary" do
-      {:ok, object} =
-        Fetcher.fetch_object_from_id(
-          "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
-        )
-
-      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",
-            "mediaType" => "video/mp4",
-            "type" => "Link"
-          }
-        ],
-        "width" => 480
-      }
+    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)
 
 
-      assert object.data["url"] ==
-               "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
+      {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
 
 
-      assert object.data["attachment"] == [attachment]
+      assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
     end
     end
+  end
 
 
-    test "it accepts Flag activities" do
+  describe "prepare outgoing" do
+    test "it inlines private announced objects" do
       user = insert(:user)
       user = insert(:user)
-      other_user = insert(:user)
 
 
-      {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
-      object = Object.normalize(activity)
+      {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
 
 
-      message = %{
-        "@context" => "https://www.w3.org/ns/activitystreams",
-        "cc" => [user.ap_id],
-        "object" => [user.ap_id, object.data["id"]],
-        "type" => "Flag",
-        "content" => "blocked AND reported!!!",
-        "actor" => other_user.ap_id
-      }
+      {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
 
 
-      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+      {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
 
 
-      assert activity.data["object"] == [user.ap_id, object.data["id"]]
-      assert activity.data["content"] == "blocked AND reported!!!"
-      assert activity.data["actor"] == other_user.ap_id
-      assert activity.data["cc"] == [user.ap_id]
+      assert modified["object"]["content"] == "hey"
+      assert modified["object"]["actor"] == modified["object"]["attributedTo"]
     end
     end
-  end
 
 
-  describe "prepare outgoing" do
     test "it turns mentions into tags" do
       user = insert(:user)
       other_user = insert(:user)
 
       {:ok, activity} =
     test "it turns mentions into tags" do
       user = insert(:user)
       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"
+        }
 
 
-      assert Enum.member?(object["tag"], expected_tag)
-      assert Enum.member?(object["tag"], expected_mention)
+        expected_tag = %{
+          "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
+          "type" => "Hashtag",
+          "name" => "#2hu"
+        }
+
+        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)
 
     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"]
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
       assert modified["object"]["sensitive"]
@@ -1060,7 +974,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
     test "it adds the json-ld context and the conversation property" do
       user = insert(:user)
 
     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"] ==
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
       assert modified["@context"] ==
@@ -1072,42 +986,16 @@ 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)
 
     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"]
     end
 
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
       assert modified["object"]["actor"] == modified["object"]["attributedTo"]
     end
 
-    test "it translates ostatus IDs to external URLs" do
-      incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-      {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
-
-      user = insert(:user)
-
-      {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
-      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
-
-      assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
-    end
-
-    test "it translates ostatus reply_to IDs to external URLs" do
-      incoming = File.read!("test/fixtures/incoming_note_activity.xml")
-      {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
-
-      user = insert(:user)
-
-      {:ok, activity} =
-        CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
-
-      {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
-
-      assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
-    end
-
     test "it strips internal hashtag data" do
       user = insert(:user)
 
     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",
 
       expected_tag = %{
         "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
@@ -1123,7 +1011,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
     test "it strips internal fields" do
       user = insert(:user)
 
     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)
 
 
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
@@ -1155,14 +1043,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       user = insert(:user)
       other_user = insert(:user)
 
       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, 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)
 
 
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
@@ -1170,8 +1057,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
       {:ok, activity} =
         CommonAPI.post(user, %{
 
       {: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)
         })
 
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
@@ -1183,13 +1070,26 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       user = insert(:user)
       {:ok, list} = Pleroma.List.create("foo", user)
 
       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)
 
       assert is_nil(modified["bcc"])
     end
 
       {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
 
       assert is_nil(modified["bcc"])
     end
+
+    test "it can handle Listen activities" do
+      listen_activity = insert(:listen)
+
+      {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
+
+      assert modified["type"] == "Listen"
+
+      user = insert(:user)
+
+      {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
+
+      {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
+    end
   end
 
   describe "user upgrade" do
   end
 
   describe "user upgrade" do
@@ -1202,25 +1102,26 @@ 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"})
       assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
 
       user = User.get_cached_by_id(user.id)
       assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
       ObanHelpers.perform_all()
 
 
       {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
       ObanHelpers.perform_all()
 
-      assert user.info.ap_enabled
-      assert user.info.note_count == 1
+      assert user.ap_enabled
+      assert user.note_count == 1
       assert user.follower_address == "https://niu.moe/users/rye/followers"
       assert user.following_address == "https://niu.moe/users/rye/following"
 
       user = User.get_cached_by_id(user.id)
       assert user.follower_address == "https://niu.moe/users/rye/followers"
       assert user.following_address == "https://niu.moe/users/rye/following"
 
       user = User.get_cached_by_id(user.id)
-      assert user.info.note_count == 1
+      assert user.note_count == 1
 
       activity = Activity.get_by_id(activity.id)
       assert user.follower_address in activity.recipients
 
       activity = Activity.get_by_id(activity.id)
       assert user.follower_address in activity.recipients
@@ -1241,7 +1142,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
                      "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
                  }
                ]
                      "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
                  }
                ]
-             } = user.info.banner
+             } = user.banner
 
       refute "..." in activity.recipients
 
 
       refute "..." in activity.recipients
 
@@ -1249,23 +1150,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
-    end
-  end
-
-  describe "maybe_retire_websub" do
-    test "it deletes all websub client subscripitions with the user as topic" do
-      subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
-      {:ok, ws} = Repo.insert(subscription)
-
-      subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
-      {:ok, ws2} = Repo.insert(subscription)
-
-      Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
-
-      refute Repo.get(WebsubClientSubscription, ws.id)
-      assert Repo.get(WebsubClientSubscription, ws2.id)
+      assert User.following?(user_two, user)
+      refute "..." in User.following(user_two)
     end
   end
 
     end
   end
 
@@ -1291,7 +1177,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
@@ -1304,7 +1192,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
@@ -1317,7 +1207,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
 
@@ -1380,8 +1272,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
 
     {:ok, poll_activity} =
       CommonAPI.post(user, %{
 
     {: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)
       })
 
     poll_object = Object.normalize(poll_activity)
@@ -1455,4 +1347,314 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       refute recipient.follower_address in fixed_object["to"]
     end
   end
       refute recipient.follower_address in fixed_object["to"]
     end
   end
+
+  describe "fix_summary/1" do
+    test "returns fixed object" do
+      assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
+      assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
+      assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
+    end
+  end
+
+  describe "fix_in_reply_to/2" do
+    setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
+
+    setup do
+      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
+      [data: data]
+    end
+
+    test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do
+      assert Transmogrifier.fix_in_reply_to(data) == data
+    end
+
+    test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do
+      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
+
+      object_with_reply =
+        Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873")
+
+      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
+      assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873"
+      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
+
+      object_with_reply =
+        Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"})
+
+      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
+      assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"}
+      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
+
+      object_with_reply =
+        Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"])
+
+      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
+      assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"]
+      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
+
+      object_with_reply = Map.put(data["object"], "inReplyTo", [])
+      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
+      assert modified_object["inReplyTo"] == []
+      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(
+          data["object"],
+          "inReplyTo",
+          "https://shitposter.club/notice/2827873"
+        )
+
+      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5)
+      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
+
+      assert modified_object["inReplyTo"] ==
+               "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
+
+      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
+
+      assert modified_object["context"] ==
+               "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
+    end
+  end
+
+  describe "fix_url/1" do
+    test "fixes data for object when url is map" do
+      object = %{
+        "url" => %{
+          "type" => "Link",
+          "mimeType" => "video/mp4",
+          "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
+        }
+      }
+
+      assert Transmogrifier.fix_url(object) == %{
+               "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
+             }
+    end
+
+    test "fixes data for video object" do
+      object = %{
+        "type" => "Video",
+        "url" => [
+          %{
+            "type" => "Link",
+            "mimeType" => "video/mp4",
+            "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
+          },
+          %{
+            "type" => "Link",
+            "mimeType" => "video/mp4",
+            "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4"
+          },
+          %{
+            "type" => "Link",
+            "mimeType" => "text/html",
+            "href" => "https://peertube.-2d4c2d1630e3"
+          },
+          %{
+            "type" => "Link",
+            "mimeType" => "text/html",
+            "href" => "https://peertube.-2d4c2d16377-42"
+          }
+        ]
+      }
+
+      assert Transmogrifier.fix_url(object) == %{
+               "attachment" => [
+                 %{
+                   "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4",
+                   "mimeType" => "video/mp4",
+                   "type" => "Link"
+                 }
+               ],
+               "type" => "Video",
+               "url" => "https://peertube.-2d4c2d1630e3"
+             }
+    end
+
+    test "fixes url for not Video object" do
+      object = %{
+        "type" => "Text",
+        "url" => [
+          %{
+            "type" => "Link",
+            "mimeType" => "text/html",
+            "href" => "https://peertube.-2d4c2d1630e3"
+          },
+          %{
+            "type" => "Link",
+            "mimeType" => "text/html",
+            "href" => "https://peertube.-2d4c2d16377-42"
+          }
+        ]
+      }
+
+      assert Transmogrifier.fix_url(object) == %{
+               "type" => "Text",
+               "url" => "https://peertube.-2d4c2d1630e3"
+             }
+
+      assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{
+               "type" => "Text",
+               "url" => ""
+             }
+    end
+
+    test "retunrs not modified object" do
+      assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
+    end
+  end
+
+  describe "get_obj_helper/2" do
+    test "returns nil when cannot normalize object" do
+      assert capture_log(fn ->
+               refute Transmogrifier.get_obj_helper("test-obj-id")
+             end) =~ "Unsupported URI scheme"
+    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")
+    end
+  end
+
+  describe "fix_attachments/1" do
+    test "returns not modified object" do
+      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
+      assert Transmogrifier.fix_attachments(data) == data
+    end
+
+    test "returns modified object when attachment is map" do
+      assert Transmogrifier.fix_attachments(%{
+               "attachment" => %{
+                 "mediaType" => "video/mp4",
+                 "url" => "https://peertube.moe/stat-480.mp4"
+               }
+             }) == %{
+               "attachment" => [
+                 %{
+                   "mediaType" => "video/mp4",
+                   "url" => [
+                     %{"href" => "https://peertube.moe/stat-480.mp4", "mediaType" => "video/mp4"}
+                   ]
+                 }
+               ]
+             }
+    end
+
+    test "returns modified object when attachment is list" do
+      assert Transmogrifier.fix_attachments(%{
+               "attachment" => [
+                 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"},
+                 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"}
+               ]
+             }) == %{
+               "attachment" => [
+                 %{
+                   "mediaType" => "video/mp4",
+                   "url" => [
+                     %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}
+                   ]
+                 },
+                 %{
+                   "mediaType" => "video/mp4",
+                   "url" => [
+                     %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}
+                   ]
+                 }
+               ]
+             }
+    end
+  end
+
+  describe "fix_emoji/1" do
+    test "returns not modified object when object not contains tags" do
+      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
+      assert Transmogrifier.fix_emoji(data) == data
+    end
+
+    test "returns object with emoji when object contains list tags" do
+      assert Transmogrifier.fix_emoji(%{
+               "tag" => [
+                 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}},
+                 %{"type" => "Hashtag"}
+               ]
+             }) == %{
+               "emoji" => %{"bib" => "/test"},
+               "tag" => [
+                 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"},
+                 %{"type" => "Hashtag"}
+               ]
+             }
+    end
+
+    test "returns object with emoji when object contains map tag" do
+      assert Transmogrifier.fix_emoji(%{
+               "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}
+             }) == %{
+               "emoji" => %{"bib" => "/test"},
+               "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}
+             }
+    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