Merge branch 'fix/1658-invite-send' into 'develop'
[akkoma] / test / web / admin_api / admin_api_controller_test.exs
index 02ffbfa0b056c3653a893f8bc2799846bf76bbda..f02f6ae7afedeeb5244d02b3708f0191a591913e 100644 (file)
@@ -1,12 +1,16 @@
 # 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.AdminAPI.AdminAPIControllerTest do
   use Pleroma.Web.ConnCase
   use Oban.Testing, repo: Pleroma.Repo
 
+  import Pleroma.Factory
+  import ExUnit.CaptureLog
+
   alias Pleroma.Activity
+  alias Pleroma.Config
   alias Pleroma.ConfigDB
   alias Pleroma.HTML
   alias Pleroma.ModerationLog
@@ -17,9 +21,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   alias Pleroma.UserInviteToken
   alias Pleroma.Web.ActivityPub.Relay
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.MediaProxy
-  import Pleroma.Factory
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -40,9 +42,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   end
 
   describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
-    clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
-      Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
-    end
+    setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
 
     test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
          %{admin: admin} do
@@ -90,9 +90,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   end
 
   describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
-    clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
-      Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
-    end
+    setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
 
     test "GET /api/pleroma/admin/users/:nickname requires " <>
            "read:accounts or admin:read:accounts or broader scope",
@@ -578,13 +576,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   end
 
   describe "POST /api/pleroma/admin/email_invite, with valid config" do
-    clear_config([:instance, :registrations_open]) do
-      Pleroma.Config.put([:instance, :registrations_open], false)
-    end
-
-    clear_config([:instance, :invites_enabled]) do
-      Pleroma.Config.put([:instance, :invites_enabled], true)
-    end
+    setup do: clear_config([:instance, :registrations_open], false)
+    setup do: clear_config([:instance, :invites_enabled], true)
 
     test "sends invitation and returns 204", %{admin: admin, conn: conn} do
       recipient_email = "foo@bar.com"
@@ -602,8 +595,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert token_record
       refute token_record.used
 
-      notify_email = Pleroma.Config.get([:instance, :notify_email])
-      instance_name = Pleroma.Config.get([:instance, :name])
+      notify_email = Config.get([:instance, :notify_email])
+      instance_name = Config.get([:instance, :name])
 
       email =
         Pleroma.Emails.UserEmail.user_invitation_email(
@@ -632,28 +625,63 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       assert json_response(conn, :forbidden)
     end
+
+    test "email with +", %{conn: conn, admin: admin} do
+      recipient_email = "foo+bar@baz.com"
+
+      conn
+      |> put_req_header("content-type", "application/json;charset=utf-8")
+      |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
+      |> json_response(:no_content)
+
+      token_record =
+        Pleroma.UserInviteToken
+        |> Repo.all()
+        |> List.last()
+
+      assert token_record
+      refute token_record.used
+
+      notify_email = Config.get([:instance, :notify_email])
+      instance_name = Config.get([:instance, :name])
+
+      email =
+        Pleroma.Emails.UserEmail.user_invitation_email(
+          admin,
+          token_record,
+          recipient_email
+        )
+
+      Swoosh.TestAssertions.assert_email_sent(
+        from: {instance_name, notify_email},
+        to: recipient_email,
+        html_body: email.html_body
+      )
+    end
   end
 
   describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
-    clear_config([:instance, :registrations_open])
-    clear_config([:instance, :invites_enabled])
+    setup do: clear_config([:instance, :registrations_open])
+    setup do: clear_config([:instance, :invites_enabled])
 
     test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
-      Pleroma.Config.put([:instance, :registrations_open], false)
-      Pleroma.Config.put([:instance, :invites_enabled], false)
+      Config.put([:instance, :registrations_open], false)
+      Config.put([:instance, :invites_enabled], false)
 
       conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
 
-      assert json_response(conn, :internal_server_error)
+      assert json_response(conn, :bad_request) ==
+               "To send invites you need to set the `invites_enabled` option to true."
     end
 
     test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
-      Pleroma.Config.put([:instance, :registrations_open], true)
-      Pleroma.Config.put([:instance, :invites_enabled], true)
+      Config.put([:instance, :registrations_open], true)
+      Config.put([:instance, :invites_enabled], true)
 
       conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
 
-      assert json_response(conn, :internal_server_error)
+      assert json_response(conn, :bad_request) ==
+               "To send invites you need to set the `registrations_open` option to false."
     end
   end
 
@@ -1592,208 +1620,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
-  describe "GET /api/pleroma/admin/grouped_reports" do
-    setup do
-      [reporter, target_user] = insert_pair(:user)
-
-      date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
-      date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
-      date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
-
-      first_status =
-        insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
-
-      second_status =
-        insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
-
-      third_status =
-        insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
-
-      {:ok, first_report} =
-        CommonAPI.report(reporter, %{
-          "account_id" => target_user.id,
-          "status_ids" => [first_status.id, second_status.id, third_status.id]
-        })
-
-      {:ok, second_report} =
-        CommonAPI.report(reporter, %{
-          "account_id" => target_user.id,
-          "status_ids" => [first_status.id, second_status.id]
-        })
-
-      {:ok, third_report} =
-        CommonAPI.report(reporter, %{
-          "account_id" => target_user.id,
-          "status_ids" => [first_status.id]
-        })
-
-      %{
-        first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
-        second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
-        third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
-        first_report: first_report,
-        first_status_reports: [first_report, second_report, third_report],
-        second_status_reports: [first_report, second_report],
-        third_status_reports: [first_report],
-        target_user: target_user,
-        reporter: reporter
-      }
-    end
-
-    test "returns reports grouped by status", %{
-      conn: conn,
-      first_status: first_status,
-      second_status: second_status,
-      third_status: third_status,
-      first_status_reports: first_status_reports,
-      second_status_reports: second_status_reports,
-      third_status_reports: third_status_reports,
-      target_user: target_user,
-      reporter: reporter
-    } do
-      response =
-        conn
-        |> get("/api/pleroma/admin/grouped_reports")
-        |> json_response(:ok)
-
-      assert length(response["reports"]) == 3
-
-      first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
-
-      second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id))
-
-      third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id))
-
-      assert length(first_group["reports"]) == 3
-      assert length(second_group["reports"]) == 2
-      assert length(third_group["reports"]) == 1
-
-      assert first_group["date"] ==
-               Enum.max_by(first_status_reports, fn act ->
-                 NaiveDateTime.from_iso8601!(act.data["published"])
-               end).data["published"]
-
-      assert first_group["status"] ==
-               Map.put(
-                 stringify_keys(StatusView.render("show.json", %{activity: first_status})),
-                 "deleted",
-                 false
-               )
-
-      assert(first_group["account"]["id"] == target_user.id)
-
-      assert length(first_group["actors"]) == 1
-      assert hd(first_group["actors"])["id"] == reporter.id
-
-      assert Enum.map(first_group["reports"], & &1["id"]) --
-               Enum.map(first_status_reports, & &1.id) == []
-
-      assert second_group["date"] ==
-               Enum.max_by(second_status_reports, fn act ->
-                 NaiveDateTime.from_iso8601!(act.data["published"])
-               end).data["published"]
-
-      assert second_group["status"] ==
-               Map.put(
-                 stringify_keys(StatusView.render("show.json", %{activity: second_status})),
-                 "deleted",
-                 false
-               )
-
-      assert second_group["account"]["id"] == target_user.id
-
-      assert length(second_group["actors"]) == 1
-      assert hd(second_group["actors"])["id"] == reporter.id
-
-      assert Enum.map(second_group["reports"], & &1["id"]) --
-               Enum.map(second_status_reports, & &1.id) == []
-
-      assert third_group["date"] ==
-               Enum.max_by(third_status_reports, fn act ->
-                 NaiveDateTime.from_iso8601!(act.data["published"])
-               end).data["published"]
-
-      assert third_group["status"] ==
-               Map.put(
-                 stringify_keys(StatusView.render("show.json", %{activity: third_status})),
-                 "deleted",
-                 false
-               )
-
-      assert third_group["account"]["id"] == target_user.id
-
-      assert length(third_group["actors"]) == 1
-      assert hd(third_group["actors"])["id"] == reporter.id
-
-      assert Enum.map(third_group["reports"], & &1["id"]) --
-               Enum.map(third_status_reports, & &1.id) == []
-    end
-
-    test "reopened report renders status data", %{
-      conn: conn,
-      first_report: first_report,
-      first_status: first_status
-    } do
-      {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
-
-      response =
-        conn
-        |> get("/api/pleroma/admin/grouped_reports")
-        |> json_response(:ok)
-
-      first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
-
-      assert first_group["status"] ==
-               Map.put(
-                 stringify_keys(StatusView.render("show.json", %{activity: first_status})),
-                 "deleted",
-                 false
-               )
-    end
-
-    test "reopened report does not render status data if status has been deleted", %{
-      conn: conn,
-      first_report: first_report,
-      first_status: first_status,
-      target_user: target_user
-    } do
-      {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
-      {:ok, _} = CommonAPI.delete(first_status.id, target_user)
-
-      refute Activity.get_by_ap_id(first_status.id)
-
-      response =
-        conn
-        |> get("/api/pleroma/admin/grouped_reports")
-        |> json_response(:ok)
-
-      assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["status"][
-               "deleted"
-             ] == true
-
-      assert length(Enum.filter(response["reports"], &(&1["status"]["deleted"] == false))) == 2
-    end
-
-    test "account not empty if status was deleted", %{
-      conn: conn,
-      first_report: first_report,
-      first_status: first_status,
-      target_user: target_user
-    } do
-      {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
-      {:ok, _} = CommonAPI.delete(first_status.id, target_user)
-
-      refute Activity.get_by_ap_id(first_status.id)
-
-      response =
-        conn
-        |> get("/api/pleroma/admin/grouped_reports")
-        |> json_response(:ok)
-
-      assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["account"]
-    end
-  end
-
   describe "PUT /api/pleroma/admin/statuses/:id" do
     setup do
       activity = insert(:note_activity)
@@ -1877,22 +1703,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "@#{admin.nickname} deleted status ##{id}"
     end
 
-    test "returns error when status is not exist", %{conn: conn} do
+    test "returns 404 when the status does not exist", %{conn: conn} do
       conn = delete(conn, "/api/pleroma/admin/statuses/test")
 
-      assert json_response(conn, :bad_request) == "Could not delete"
+      assert json_response(conn, :not_found) == "Not found"
     end
   end
 
   describe "GET /api/pleroma/admin/config" do
-    clear_config(:configurable_from_database) do
-      Pleroma.Config.put(:configurable_from_database, true)
-    end
+    setup do: clear_config(:configurable_from_database, true)
 
     test "when configuration from database is off", %{conn: conn} do
-      initial = Pleroma.Config.get(:configurable_from_database)
-      Pleroma.Config.put(:configurable_from_database, false)
-      on_exit(fn -> Pleroma.Config.put(:configurable_from_database, initial) end)
+      Config.put(:configurable_from_database, false)
       conn = get(conn, "/api/pleroma/admin/config")
 
       assert json_response(conn, 400) ==
@@ -2036,12 +1858,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
         Application.put_env(:pleroma, :http, http)
         Application.put_env(:tesla, :adapter, Tesla.Mock)
+        Restarter.Pleroma.refresh()
       end)
     end
 
-    clear_config(:configurable_from_database) do
-      Pleroma.Config.put(:configurable_from_database, true)
-    end
+    setup do: clear_config(:configurable_from_database, true)
 
     @tag capture_log: true
     test "create new config setting in db", %{conn: conn} do
@@ -2249,21 +2070,63 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "saving config which need pleroma reboot", %{conn: conn} do
-      chat = Pleroma.Config.get(:chat)
-      on_exit(fn -> Pleroma.Config.put(:chat, chat) end)
+      chat = Config.get(:chat)
+      on_exit(fn -> Config.put(:chat, chat) end)
 
-      conn =
-        post(
-          conn,
-          "/api/pleroma/admin/config",
-          %{
-            configs: [
-              %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
-            ]
-          }
-        )
+      assert post(
+               conn,
+               "/api/pleroma/admin/config",
+               %{
+                 configs: [
+                   %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
+                 ]
+               }
+             )
+             |> json_response(200) == %{
+               "configs" => [
+                 %{
+                   "db" => [":enabled"],
+                   "group" => ":pleroma",
+                   "key" => ":chat",
+                   "value" => [%{"tuple" => [":enabled", true]}]
+                 }
+               ],
+               "need_reboot" => true
+             }
 
-      assert json_response(conn, 200) == %{
+      configs =
+        conn
+        |> get("/api/pleroma/admin/config")
+        |> json_response(200)
+
+      assert configs["need_reboot"]
+
+      capture_log(fn ->
+        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
+      end) =~ "pleroma restarted"
+
+      configs =
+        conn
+        |> get("/api/pleroma/admin/config")
+        |> json_response(200)
+
+      refute Map.has_key?(configs, "need_reboot")
+    end
+
+    test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
+      chat = Config.get(:chat)
+      on_exit(fn -> Config.put(:chat, chat) end)
+
+      assert post(
+               conn,
+               "/api/pleroma/admin/config",
+               %{
+                 configs: [
+                   %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
+                 ]
+               }
+             )
+             |> json_response(200) == %{
                "configs" => [
                  %{
                    "db" => [":enabled"],
@@ -2274,6 +2137,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                ],
                "need_reboot" => true
              }
+
+      assert post(conn, "/api/pleroma/admin/config", %{
+               configs: [
+                 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
+               ]
+             })
+             |> json_response(200) == %{
+               "configs" => [
+                 %{
+                   "group" => ":pleroma",
+                   "key" => ":key1",
+                   "value" => [
+                     %{"tuple" => [":key3", 3]}
+                   ],
+                   "db" => [":key3"]
+                 }
+               ],
+               "need_reboot" => true
+             }
+
+      capture_log(fn ->
+        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
+      end) =~ "pleroma restarted"
+
+      configs =
+        conn
+        |> get("/api/pleroma/admin/config")
+        |> json_response(200)
+
+      refute Map.has_key?(configs, "need_reboot")
     end
 
     test "saving config with nested merge", %{conn: conn} do
@@ -2410,7 +2303,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                {ExSyslogger, :ex_syslogger}
              ]
 
-      ExUnit.CaptureLog.capture_log(fn ->
+      capture_log(fn ->
         require Logger
         Logger.warn("Ooops...")
       end) =~ "Ooops..."
@@ -2439,8 +2332,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "value" => "Tesla.Adapter.Httpc",
                    "db" => [":adapter"]
                  }
-               ],
-               "need_reboot" => true
+               ]
              }
     end
 
@@ -2512,9 +2404,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "common config example", %{conn: conn} do
-      adapter = Application.get_env(:tesla, :adapter)
-      on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
-
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
@@ -2533,17 +2422,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
                 %{"tuple" => [":name", "Pleroma"]}
               ]
-            },
-            %{
-              "group" => ":tesla",
-              "key" => ":adapter",
-              "value" => "Tesla.Adapter.Httpc"
             }
           ]
         })
 
-      assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
-      assert Pleroma.Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
+      assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
 
       assert json_response(conn, 200) == %{
                "configs" => [
@@ -2574,15 +2457,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                      ":regex4",
                      ":name"
                    ]
-                 },
-                 %{
-                   "group" => ":tesla",
-                   "key" => ":adapter",
-                   "value" => "Tesla.Adapter.Httpc",
-                   "db" => [":adapter"]
                  }
-               ],
-               "need_reboot" => true
+               ]
              }
     end
 
@@ -2977,14 +2853,64 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   end
 
   describe "GET /api/pleroma/admin/restart" do
-    clear_config(:configurable_from_database) do
-      Pleroma.Config.put(:configurable_from_database, true)
-    end
+    setup do: clear_config(:configurable_from_database, true)
 
     test "pleroma restarts", %{conn: conn} do
-      ExUnit.CaptureLog.capture_log(fn ->
+      capture_log(fn ->
         assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
       end) =~ "pleroma restarted"
+
+      refute Restarter.Pleroma.need_reboot?()
+    end
+  end
+
+  describe "GET /api/pleroma/admin/statuses" do
+    test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
+      blocked = insert(:user)
+      user = insert(:user)
+      User.block(admin, blocked)
+
+      {:ok, _} =
+        CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
+
+      {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+      {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+      {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+      {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
+
+      response =
+        conn
+        |> get("/api/pleroma/admin/statuses")
+        |> json_response(200)
+
+      refute "private" in Enum.map(response, & &1["visibility"])
+      assert length(response) == 3
+    end
+
+    test "returns only local statuses with local_only on", %{conn: conn} do
+      user = insert(:user)
+      remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
+      insert(:note_activity, user: user, local: true)
+      insert(:note_activity, user: remote_user, local: false)
+
+      response =
+        conn
+        |> get("/api/pleroma/admin/statuses?local_only=true")
+        |> json_response(200)
+
+      assert length(response) == 1
+    end
+
+    test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
+      user = insert(:user)
+
+      {:ok, _} =
+        CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
+
+      {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+      {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+      conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
+      assert json_response(conn, 200) |> length() == 3
     end
   end
 
@@ -3038,6 +2964,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       assert json_response(conn, 200) |> length() == 5
     end
+
+    test "excludes reblogs by default", %{conn: conn, user: user} do
+      other_user = insert(:user)
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
+      {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
+
+      conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
+      assert json_response(conn_res, 200) |> length() == 0
+
+      conn_res =
+        get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
+
+      assert json_response(conn_res, 200) |> length() == 1
+    end
   end
 
   describe "GET /api/pleroma/admin/moderation_log" do
@@ -3248,6 +3188,75 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
+  describe "GET /users/:nickname/credentials" do
+    test "gets the user credentials", %{conn: conn} do
+      user = insert(:user)
+      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
+
+      response = assert json_response(conn, 200)
+      assert response["email"] == user.email
+    end
+
+    test "returns 403 if requested by a non-admin" do
+      user = insert(:user)
+
+      conn =
+        build_conn()
+        |> assign(:user, user)
+        |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
+
+      assert json_response(conn, :forbidden)
+    end
+  end
+
+  describe "PATCH /users/:nickname/credentials" do
+    test "changes password and email", %{conn: conn, admin: admin} do
+      user = insert(:user)
+      assert user.password_reset_pending == false
+
+      conn =
+        patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
+          "password" => "new_password",
+          "email" => "new_email@example.com",
+          "name" => "new_name"
+        })
+
+      assert json_response(conn, 200) == %{"status" => "success"}
+
+      ObanHelpers.perform_all()
+
+      updated_user = User.get_by_id(user.id)
+
+      assert updated_user.email == "new_email@example.com"
+      assert updated_user.name == "new_name"
+      assert updated_user.password_hash != user.password_hash
+      assert updated_user.password_reset_pending == true
+
+      [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
+
+      assert ModerationLog.get_log_entry_message(log_entry1) ==
+               "@#{admin.nickname} updated users: @#{user.nickname}"
+
+      assert ModerationLog.get_log_entry_message(log_entry2) ==
+               "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
+    end
+
+    test "returns 403 if requested by a non-admin" do
+      user = insert(:user)
+
+      conn =
+        build_conn()
+        |> assign(:user, user)
+        |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
+          "password" => "new_password",
+          "email" => "new_email@example.com",
+          "name" => "new_name"
+        })
+
+      assert json_response(conn, :forbidden)
+    end
+  end
+
   describe "PATCH /users/:nickname/force_password_reset" do
     test "sets password_reset_pending to true", %{conn: conn} do
       user = insert(:user)
@@ -3320,7 +3329,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       user = insert(:user, local: false, nickname: "archaeme@archae.me")
       user2 = insert(:user, local: false, nickname: "test@test.com")
       insert_pair(:note_activity, user: user)
-      insert(:note_activity, user: user2)
+      activity = insert(:note_activity, user: user2)
 
       ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
 
@@ -3339,6 +3348,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       response = json_response(ret_conn, 200)
 
       assert Enum.empty?(response)
+
+      CommonAPI.repeat(activity.id, user)
+
+      ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
+      response = json_response(ret_conn, 200)
+      assert length(response) == 2
+
+      ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
+      response = json_response(ret_conn, 200)
+      assert length(response) == 3
     end
   end
 
@@ -3468,6 +3487,25 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     assert String.starts_with?(child["group"], ":")
     assert child["description"]
   end
+
+  describe "/api/pleroma/admin/stats" do
+    test "status visibility count", %{conn: conn} do
+      admin = insert(:user, is_admin: true)
+      user = insert(:user)
+      CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
+      CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
+      CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
+
+      response =
+        conn
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/stats")
+        |> json_response(200)
+
+      assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
+               response["status_visibility"]
+    end
+  end
 end
 
 # Needed for testing