From 744b34709db9c11767a9bc57fe0bb21c96e826c7 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@feld.me>
Date: Wed, 30 Dec 2020 14:22:48 -0600
Subject: [PATCH 1/9] Do not reverse order of reports. We want newest ones
 sorted to the top.

---
 lib/pleroma/web/admin_api/views/report_view.ex | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
index 535556370..da949e306 100644
--- a/lib/pleroma/web/admin_api/views/report_view.ex
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -19,8 +19,7 @@ def render("index.json", %{reports: reports}) do
       reports:
         reports[:items]
         |> Enum.map(&Report.extract_report_info/1)
-        |> Enum.map(&render(__MODULE__, "show.json", &1))
-        |> Enum.reverse(),
+        |> Enum.map(&render(__MODULE__, "show.json", &1)),
       total: reports[:total]
     }
   end

From 4c5f75f4e96b8ee8ab1e37b03d46f4e7ead870ac Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@feld.me>
Date: Wed, 30 Dec 2020 14:36:04 -0600
Subject: [PATCH 2/9] Support pagination in AdminAPI for user statuses

---
 .../controllers/admin_api_controller.ex          |  5 +++--
 .../controllers/admin_api_controller_test.exs    | 16 +++++++++++++---
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
index 75525104f..9086c4928 100644
--- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -103,11 +103,12 @@ def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickna
     godmode = params["godmode"] == "true" || params["godmode"] == true
 
     with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
-      {_, page_size} = page_params(params)
+      {page, page_size} = page_params(params)
 
       activities =
-        ActivityPub.fetch_user_activities(user, nil, %{
+        ActivityPub.fetch_statuses(user, %{
           limit: page_size,
+          offset: (page - 1) * page_size,
           godmode: godmode,
           exclude_reblogs: not with_reblogs
         })
diff --git a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
index e50d1425b..90b25b782 100644
--- a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
+++ b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
@@ -422,10 +422,20 @@ test "renders user's statuses", %{conn: conn, user: user} do
       assert json_response(conn, 200) |> length() == 3
     end
 
-    test "renders user's statuses with a limit", %{conn: conn, user: user} do
-      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
+    test "renders user's statuses with pagination", %{conn: conn, user: user} do
+      conn1 = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=1")
 
-      assert json_response(conn, 200) |> length() == 2
+      response1 = json_response(conn1, 200)
+
+      assert response1 |> length() == 1
+
+      conn2 = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=2")
+
+      response2 = json_response(conn2, 200)
+
+      assert response2 |> length() == 1
+
+      refute response1 == response2
     end
 
     test "doesn't return private statuses by default", %{conn: conn, user: user} do

From 085d4e6cfcdecd967cbe6716d06d1ace0108a11a Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@feld.me>
Date: Wed, 30 Dec 2020 16:10:10 -0600
Subject: [PATCH 3/9] Continue to use ActivityPub.fetch_user_activities/3, make
 it pass :offset

---
 lib/pleroma/web/activity_pub/activity_pub.ex                  | 2 +-
 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 5059bff03..f82f34de5 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -608,7 +608,7 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
       reading_user: reading_user
     }
     |> user_activities_recipients()
-    |> fetch_activities(params)
+    |> fetch_activities(params, :offset)
     |> Enum.reverse()
   end
 
diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
index 9086c4928..6ef8d6061 100644
--- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -106,7 +106,7 @@ def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickna
       {page, page_size} = page_params(params)
 
       activities =
-        ActivityPub.fetch_statuses(user, %{
+        ActivityPub.fetch_user_activities(user, nil, %{
           limit: page_size,
           offset: (page - 1) * page_size,
           godmode: godmode,

From 2597b028f797c74eb16e7cd5bfc251dfe03ad934 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@feld.me>
Date: Wed, 30 Dec 2020 16:37:04 -0600
Subject: [PATCH 4/9] Make pagination type conditional

---
 lib/pleroma/web/activity_pub/activity_pub.ex | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index f82f34de5..68494f047 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -603,12 +603,18 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
         |> Map.put(:muting_user, reading_user)
       end
 
+    pagination_type =
+      cond do
+        is_nil(params[:offset]) -> :keyset
+        true -> :offset
+      end
+
     %{
       godmode: params[:godmode],
       reading_user: reading_user
     }
     |> user_activities_recipients()
-    |> fetch_activities(params, :offset)
+    |> fetch_activities(params, pagination_type)
     |> Enum.reverse()
   end
 

From 2aa60e7592104553fb5c330e8aafeaf4a21f1910 Mon Sep 17 00:00:00 2001
From: feld <feld@feld.me>
Date: Thu, 31 Dec 2020 15:55:44 +0000
Subject: [PATCH 5/9] Apply 1 suggestion(s) to 1 file(s)

---
 lib/pleroma/web/activity_pub/activity_pub.ex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 68494f047..15f298bb8 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -605,7 +605,7 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
 
     pagination_type =
       cond do
-        is_nil(params[:offset]) -> :keyset
+        !Map.has_key?(params, :offset) -> :keyset
         true -> :offset
       end
 

From e4791258d4483cd9dad6016ec453e6ca7ea10d73 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@feld.me>
Date: Thu, 31 Dec 2020 10:53:18 -0600
Subject: [PATCH 6/9] Ensure newest report is returned first in the list

---
 .../web/admin_api/views/report_view_test.exs  | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/test/pleroma/web/admin_api/views/report_view_test.exs b/test/pleroma/web/admin_api/views/report_view_test.exs
index ff3453208..3914751b5 100644
--- a/test/pleroma/web/admin_api/views/report_view_test.exs
+++ b/test/pleroma/web/admin_api/views/report_view_test.exs
@@ -143,4 +143,29 @@ test "doesn't error out when the user doesn't exists" do
 
     assert %{} = ReportView.render("show.json", Report.extract_report_info(activity))
   end
+
+  test "reports are ordered newest first" do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, report1} =
+      CommonAPI.report(user, %{
+        account_id: other_user.id,
+        comment: "first report"
+      })
+
+    {:ok, report2} =
+      CommonAPI.report(user, %{
+        account_id: other_user.id,
+        comment: "second report"
+      })
+
+    %{reports: rendered} =
+      ReportView.render("index.json",
+        reports: Pleroma.Web.ActivityPub.Utils.get_reports(%{}, 1, 50)
+      )
+
+    assert report2.id == rendered |> Enum.at(0) |> Map.get(:id)
+    assert report1.id == rendered |> Enum.at(1) |> Map.get(:id)
+  end
 end

From 83d97ab98e99a16d0ba25a57df1be182dfb9b938 Mon Sep 17 00:00:00 2001
From: Mark Felder <feld@feld.me>
Date: Thu, 31 Dec 2020 13:15:44 -0600
Subject: [PATCH 7/9] Document reports ordering change

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1604ab3a..77959c415 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - **Breaking:** Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
 - Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries.
 - Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators.
+- Admin API: Reports now ordered by newest
 
 ### Added
 

From 0ec7e9b8e98f6310f19d0b0a6ad82fc9c70a9d47 Mon Sep 17 00:00:00 2001
From: Alex Gleason <alex@alexgleason.me>
Date: Fri, 1 Jan 2021 11:58:42 -0600
Subject: [PATCH 8/9] AdminAPI: return id for moderation log entries

---
 docs/API/admin_api.md                                        | 1 +
 lib/pleroma/web/admin_api/views/moderation_log_view.ex       | 1 +
 .../pleroma/web/admin_api/views/moderation_log_view_test.exs | 5 +++++
 3 files changed, 7 insertions(+)

diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md
index 266f8cef8..5253dc668 100644
--- a/docs/API/admin_api.md
+++ b/docs/API/admin_api.md
@@ -1123,6 +1123,7 @@ Loads json generated from `config/descriptions.exs`.
 ```json
 [
   {
+    "id": 1234,
     "data": {
       "actor": {
         "id": 1,
diff --git a/lib/pleroma/web/admin_api/views/moderation_log_view.ex b/lib/pleroma/web/admin_api/views/moderation_log_view.ex
index 112f9e0e1..3fa778b0a 100644
--- a/lib/pleroma/web/admin_api/views/moderation_log_view.ex
+++ b/lib/pleroma/web/admin_api/views/moderation_log_view.ex
@@ -21,6 +21,7 @@ def render("show.json", %{log_entry: log_entry}) do
       |> DateTime.to_unix()
 
     %{
+      id: log_entry.id,
       data: log_entry.data,
       time: time,
       message: ModerationLog.get_log_entry_message(log_entry)
diff --git a/test/pleroma/web/admin_api/views/moderation_log_view_test.exs b/test/pleroma/web/admin_api/views/moderation_log_view_test.exs
index 390d7bbeb..a4748990e 100644
--- a/test/pleroma/web/admin_api/views/moderation_log_view_test.exs
+++ b/test/pleroma/web/admin_api/views/moderation_log_view_test.exs
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do
   describe "renders `report_note_delete` log messages" do
     setup do
       log1 = %Pleroma.ModerationLog{
+        id: 1,
         data: %{
           "action" => "report_note_delete",
           "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
@@ -21,6 +22,7 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do
       }
 
       log2 = %Pleroma.ModerationLog{
+        id: 2,
         data: %{
           "action" => "report_note_delete",
           "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
@@ -42,6 +44,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
              ) == %{
                items: [
                  %{
+                   id: 1,
                    data: %{
                      "action" => "report_note_delete",
                      "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
@@ -59,6 +62,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
                    time: 1_605_622_400
                  },
                  %{
+                   id: 2,
                    data: %{
                      "action" => "report_note_delete",
                      "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
@@ -82,6 +86,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
 
     test "renders `report_note_delete` log message", %{log1: log} do
       assert ModerationLogView.render("show.json", %{log_entry: log}) == %{
+               id: 1,
                data: %{
                  "action" => "report_note_delete",
                  "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},

From 8e5904daa59f9e7c85e1431605067b86506bcfc9 Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Mon, 4 Jan 2021 18:40:59 +0100
Subject: [PATCH 9/9] SideEffects.DeleteTest: asyncify.

Replace Mock with Mox, mock out Logger.
---
 config/test.exs                               |   4 +
 lib/pleroma/logging.ex                        |   7 +
 lib/pleroma/web/activity_pub/activity_pub.ex  |   5 +
 .../activity_pub/activity_pub/streaming.ex    |  12 ++
 lib/pleroma/web/activity_pub/side_effects.ex  |   8 +-
 .../activity_pub/side_effects/delete_test.exs | 147 ++++++++++++++++++
 .../web/activity_pub/side_effects_test.exs    | 110 -------------
 test/support/conn_case.ex                     |   2 +
 test/support/data_case.ex                     |   2 +
 test/support/mocks.ex                         |   7 +-
 10 files changed, 190 insertions(+), 114 deletions(-)
 create mode 100644 lib/pleroma/logging.ex
 create mode 100644 lib/pleroma/web/activity_pub/activity_pub/streaming.ex
 create mode 100644 test/pleroma/web/activity_pub/side_effects/delete_test.exs

diff --git a/config/test.exs b/config/test.exs
index a85881592..7fc457463 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -134,6 +134,10 @@
 
 config :pleroma, :cachex, provider: Pleroma.CachexMock
 
+config :pleroma, :side_effects,
+  ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
+  logger: Pleroma.LoggerMock
+
 if File.exists?("./config/test.secret.exs") do
   import_config "test.secret.exs"
 else
diff --git a/lib/pleroma/logging.ex b/lib/pleroma/logging.ex
new file mode 100644
index 000000000..37b201c29
--- /dev/null
+++ b/lib/pleroma/logging.ex
@@ -0,0 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Logging do
+  @callback error(String.t()) :: any()
+end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 15f298bb8..3e346d49a 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -33,6 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   require Pleroma.Constants
 
   @behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting
+  @behaviour Pleroma.Web.ActivityPub.ActivityPub.Streaming
 
   defp get_recipients(%{"type" => "Create"} = data) do
     to = Map.get(data, "to", [])
@@ -224,6 +225,7 @@ def stream_out_participations(participations) do
     Streamer.stream("participation", participations)
   end
 
+  @impl true
   def stream_out_participations(%Object{data: %{"context" => context}}, user) do
     with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
       conversation = Repo.preload(conversation, :participations)
@@ -240,8 +242,10 @@ def stream_out_participations(%Object{data: %{"context" => context}}, user) do
     end
   end
 
+  @impl true
   def stream_out_participations(_, _), do: :noop
 
+  @impl true
   def stream_out(%Activity{data: %{"type" => data_type}} = activity)
       when data_type in ["Create", "Announce", "Delete"] do
     activity
@@ -249,6 +253,7 @@ def stream_out(%Activity{data: %{"type" => data_type}} = activity)
     |> Streamer.stream(activity)
   end
 
+  @impl true
   def stream_out(_activity) do
     :noop
   end
diff --git a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
new file mode 100644
index 000000000..30009f2fb
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
@@ -0,0 +1,12 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do
+  alias Pleroma.Activity
+  alias Pleroma.Object
+  alias Pleroma.User
+
+  @callback stream_out(Activity.t()) :: any()
+  @callback stream_out_participations(Object.t(), User.t()) :: any()
+end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index 55c99ad0c..e37caf6a0 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -28,6 +28,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
   require Logger
 
   @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+  @ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
+  @logger Pleroma.Config.get([:side_effects, :logger], Logger)
 
   @behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
 
@@ -287,12 +289,12 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object,
 
             MessageReference.delete_for_object(deleted_object)
 
-            ActivityPub.stream_out(object)
-            ActivityPub.stream_out_participations(deleted_object, user)
+            @ap_streamer.stream_out(object)
+            @ap_streamer.stream_out_participations(deleted_object, user)
             :ok
           else
             {:actor, _} ->
-              Logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
+              @logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
               :no_object_actor
           end
 
diff --git a/test/pleroma/web/activity_pub/side_effects/delete_test.exs b/test/pleroma/web/activity_pub/side_effects/delete_test.exs
new file mode 100644
index 000000000..e4ad606a9
--- /dev/null
+++ b/test/pleroma/web/activity_pub/side_effects/delete_test.exs
@@ -0,0 +1,147 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.SideEffects.DeleteTest do
+  use Oban.Testing, repo: Pleroma.Repo
+  use Pleroma.DataCase, async: true
+
+  alias Pleroma.Activity
+  alias Pleroma.Object
+  alias Pleroma.Repo
+  alias Pleroma.Tests.ObanHelpers
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Builder
+  alias Pleroma.Web.ActivityPub.SideEffects
+  alias Pleroma.Web.CommonAPI
+
+  alias Pleroma.LoggerMock
+  alias Pleroma.Web.ActivityPub.ActivityPubMock
+
+  import Mox
+  import Pleroma.Factory
+
+  describe "user deletion" do
+    setup do
+      user = insert(:user)
+
+      {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
+      {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
+
+      %{
+        user: user,
+        delete_user: delete_user
+      }
+    end
+
+    test "it handles user deletions", %{delete_user: delete, user: user} do
+      {:ok, _delete, _} = SideEffects.handle(delete)
+      ObanHelpers.perform_all()
+
+      assert User.get_cached_by_ap_id(user.ap_id).deactivated
+    end
+  end
+
+  describe "object deletion" do
+    setup do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
+      {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
+      {:ok, favorite} = CommonAPI.favorite(user, post.id)
+      object = Object.normalize(post)
+      {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
+      {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
+
+      %{
+        user: user,
+        delete: delete,
+        post: post,
+        object: object,
+        op: op,
+        favorite: favorite
+      }
+    end
+
+    test "it handles object deletions", %{
+      delete: delete,
+      post: post,
+      object: object,
+      user: user,
+      op: op,
+      favorite: favorite
+    } do
+      object_id = object.id
+      user_id = user.id
+
+      ActivityPubMock
+      |> expect(:stream_out, fn ^delete -> nil end)
+      |> expect(:stream_out_participations, fn %Object{id: ^object_id}, %User{id: ^user_id} ->
+        nil
+      end)
+
+      {:ok, _delete, _} = SideEffects.handle(delete)
+      user = User.get_cached_by_ap_id(object.data["actor"])
+
+      object = Object.get_by_id(object.id)
+      assert object.data["type"] == "Tombstone"
+      refute Activity.get_by_id(post.id)
+      refute Activity.get_by_id(favorite.id)
+
+      user = User.get_by_id(user.id)
+      assert user.note_count == 0
+
+      object = Object.normalize(op.data["object"], false)
+
+      assert object.data["repliesCount"] == 0
+    end
+
+    test "it handles object deletions when the object itself has been pruned", %{
+      delete: delete,
+      post: post,
+      object: object,
+      user: user,
+      op: op
+    } do
+      object_id = object.id
+      user_id = user.id
+
+      ActivityPubMock
+      |> expect(:stream_out, fn ^delete -> nil end)
+      |> expect(:stream_out_participations, fn %Object{id: ^object_id}, %User{id: ^user_id} ->
+        nil
+      end)
+
+      {:ok, _delete, _} = SideEffects.handle(delete)
+      user = User.get_cached_by_ap_id(object.data["actor"])
+
+      object = Object.get_by_id(object.id)
+      assert object.data["type"] == "Tombstone"
+      refute Activity.get_by_id(post.id)
+
+      user = User.get_by_id(user.id)
+      assert user.note_count == 0
+
+      object = Object.normalize(op.data["object"], false)
+
+      assert object.data["repliesCount"] == 0
+    end
+
+    test "it logs issues with objects deletion", %{
+      delete: delete,
+      object: object
+    } do
+      {:ok, _object} =
+        object
+        |> Object.change(%{data: Map.delete(object.data, "actor")})
+        |> Repo.update()
+
+      LoggerMock
+      |> expect(:error, fn str -> assert str =~ "The object doesn't have an actor" end)
+
+      {:error, :no_object_actor} = SideEffects.handle(delete)
+    end
+  end
+end
diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs
index 297fc0b84..50af7a507 100644
--- a/test/pleroma/web/activity_pub/side_effects_test.exs
+++ b/test/pleroma/web/activity_pub/side_effects_test.exs
@@ -19,7 +19,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
   alias Pleroma.Web.ActivityPub.SideEffects
   alias Pleroma.Web.CommonAPI
 
-  import ExUnit.CaptureLog
   import Mock
   import Pleroma.Factory
 
@@ -131,115 +130,6 @@ test "it uses a given changeset to update", %{user: user, update: update} do
     end
   end
 
-  describe "delete objects" do
-    setup do
-      user = insert(:user)
-      other_user = insert(:user)
-
-      {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
-      {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
-      {:ok, favorite} = CommonAPI.favorite(user, post.id)
-      object = Object.normalize(post)
-      {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
-      {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
-      {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
-      {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
-
-      %{
-        user: user,
-        delete: delete,
-        post: post,
-        object: object,
-        delete_user: delete_user,
-        op: op,
-        favorite: favorite
-      }
-    end
-
-    test "it handles object deletions", %{
-      delete: delete,
-      post: post,
-      object: object,
-      user: user,
-      op: op,
-      favorite: favorite
-    } do
-      with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
-        stream_out: fn _ -> nil end,
-        stream_out_participations: fn _, _ -> nil end do
-        {:ok, delete, _} = SideEffects.handle(delete)
-        user = User.get_cached_by_ap_id(object.data["actor"])
-
-        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
-        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
-      end
-
-      object = Object.get_by_id(object.id)
-      assert object.data["type"] == "Tombstone"
-      refute Activity.get_by_id(post.id)
-      refute Activity.get_by_id(favorite.id)
-
-      user = User.get_by_id(user.id)
-      assert user.note_count == 0
-
-      object = Object.normalize(op.data["object"], false)
-
-      assert object.data["repliesCount"] == 0
-    end
-
-    test "it handles object deletions when the object itself has been pruned", %{
-      delete: delete,
-      post: post,
-      object: object,
-      user: user,
-      op: op
-    } do
-      with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
-        stream_out: fn _ -> nil end,
-        stream_out_participations: fn _, _ -> nil end do
-        {:ok, delete, _} = SideEffects.handle(delete)
-        user = User.get_cached_by_ap_id(object.data["actor"])
-
-        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
-        assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
-      end
-
-      object = Object.get_by_id(object.id)
-      assert object.data["type"] == "Tombstone"
-      refute Activity.get_by_id(post.id)
-
-      user = User.get_by_id(user.id)
-      assert user.note_count == 0
-
-      object = Object.normalize(op.data["object"], false)
-
-      assert object.data["repliesCount"] == 0
-    end
-
-    test "it handles user deletions", %{delete_user: delete, user: user} do
-      {:ok, _delete, _} = SideEffects.handle(delete)
-      ObanHelpers.perform_all()
-
-      assert User.get_cached_by_ap_id(user.ap_id).deactivated
-    end
-
-    test "it logs issues with objects deletion", %{
-      delete: delete,
-      object: object
-    } do
-      {:ok, object} =
-        object
-        |> Object.change(%{data: Map.delete(object.data, "actor")})
-        |> Repo.update()
-
-      Object.invalid_object_cache(object)
-
-      assert capture_log(fn ->
-               {:error, :no_object_actor} = SideEffects.handle(delete)
-             end) =~ "object doesn't have an actor"
-    end
-  end
-
   describe "EmojiReact objects" do
     setup do
       poster = insert(:user)
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index 02f49c590..f20e3d955 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -138,6 +138,8 @@ defp json_response_and_validate_schema(conn, _status) do
 
     Pleroma.DataCase.stub_pipeline()
 
+    Mox.verify_on_exit!()
+
     {:ok, conn: Phoenix.ConnTest.build_conn()}
   end
 end
diff --git a/test/support/data_case.ex b/test/support/data_case.ex
index 5c657c1d9..0b41f0f63 100644
--- a/test/support/data_case.ex
+++ b/test/support/data_case.ex
@@ -85,6 +85,8 @@ def clear_cachex do
 
     stub_pipeline()
 
+    Mox.verify_on_exit!()
+
     :ok
   end
 
diff --git a/test/support/mocks.ex b/test/support/mocks.ex
index a600a6458..442ff5b71 100644
--- a/test/support/mocks.ex
+++ b/test/support/mocks.ex
@@ -13,7 +13,10 @@
 )
 
 Mox.defmock(Pleroma.Web.ActivityPub.ActivityPubMock,
-  for: Pleroma.Web.ActivityPub.ActivityPub.Persisting
+  for: [
+    Pleroma.Web.ActivityPub.ActivityPub.Persisting,
+    Pleroma.Web.ActivityPub.ActivityPub.Streaming
+  ]
 )
 
 Mox.defmock(Pleroma.Web.ActivityPub.SideEffectsMock,
@@ -23,3 +26,5 @@
 Mox.defmock(Pleroma.Web.FederatorMock, for: Pleroma.Web.Federator.Publishing)
 
 Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)
+
+Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging)