Merge branch 'develop' into feature/reports-groups-and-multiple-state-update
[akkoma] / test / web / admin_api / admin_api_controller_test.exs
index 77c67011d4e6d3c177233dc91ff53038605ef892..35367bed3469cfef76e0dee9be5019524de2a6ee 100644 (file)
@@ -1,23 +1,32 @@
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2019 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
 
   alias Pleroma.Activity
   alias Pleroma.HTML
   alias Pleroma.ModerationLog
   alias Pleroma.Repo
+  alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
   alias Pleroma.UserInviteToken
+  alias Pleroma.Web.ActivityPub.Relay
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.MediaProxy
   import Pleroma.Factory
 
-  describe "/api/pleroma/admin/users" do
-    test "Delete" do
-      admin = insert(:user, info: %{is_admin: true})
+  setup_all do
+    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+    :ok
+  end
+
+  describe "DELETE /api/pleroma/admin/users" do
+    test "single user" do
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -28,17 +37,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
       log_entry = Repo.one(ModerationLog)
 
-      assert log_entry.data["subject"]["nickname"] == user.nickname
-      assert log_entry.data["action"] == "delete"
-
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deleted user @#{user.nickname}"
+               "@#{admin.nickname} deleted users: @#{user.nickname}"
 
       assert json_response(conn, 200) == user.nickname
     end
 
+    test "multiple users" do
+      admin = insert(:user, is_admin: true)
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
+
+      response = json_response(conn, 200)
+      assert response -- [user_one.nickname, user_two.nickname] == []
+    end
+  end
+
+  describe "/api/pleroma/admin/users" do
     test "Create" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -68,7 +98,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "Cannot create user with exisiting email" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -99,7 +129,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "Cannot create user with exisiting nickname" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -130,7 +160,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "Multiple user creation works in transaction" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -179,7 +209,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/:nickname" do
     test "Show", %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -202,7 +232,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "when the user doesn't exist", %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = build(:user)
 
       conn =
@@ -216,7 +246,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/follow" do
     test "allows to force-follow another user" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
       follower = insert(:user)
 
@@ -242,7 +272,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/unfollow" do
     test "allows to force-unfollow another user" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
       follower = insert(:user)
 
@@ -270,7 +300,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "PUT /api/pleroma/admin/users/tag" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user1 = insert(:user, %{tags: ["x"]})
       user2 = insert(:user, %{tags: ["y"]})
       user3 = insert(:user, %{tags: ["unchanged"]})
@@ -319,7 +349,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "DELETE /api/pleroma/admin/users/tag" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user1 = insert(:user, %{tags: ["x"]})
       user2 = insert(:user, %{tags: ["y", "z"]})
       user3 = insert(:user, %{tags: ["unchanged"]})
@@ -368,7 +398,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "/api/pleroma/admin/users/:nickname/permission_group" do
     test "GET is giving user_info" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -383,7 +413,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "/:right POST, can add to a permission group" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       conn =
@@ -402,88 +432,78 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "@#{admin.nickname} made @#{user.nickname} admin"
     end
 
-    test "/:right DELETE, can remove from a permission group" do
-      admin = insert(:user, info: %{is_admin: true})
-      user = insert(:user, info: %{is_admin: true})
+    test "/:right POST, can add to a permission group (multiple)" do
+      admin = insert(:user, is_admin: true)
+      user_one = insert(:user)
+      user_two = insert(:user)
 
       conn =
         build_conn()
         |> assign(:user, admin)
         |> put_req_header("accept", "application/json")
-        |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
+        |> post("/api/pleroma/admin/users/permission_group/admin", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
 
       assert json_response(conn, 200) == %{
-               "is_admin" => false
+               "is_admin" => true
              }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} revoked admin role from @#{user.nickname}"
+               "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
     end
-  end
 
-  describe "PUT /api/pleroma/admin/users/:nickname/activation_status" do
-    setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+    test "/:right DELETE, can remove from a permission group" do
+      admin = insert(:user, is_admin: true)
+      user = insert(:user, is_admin: true)
 
       conn =
-        conn
+        build_conn()
         |> assign(:user, admin)
         |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
 
-      %{conn: conn, admin: admin}
-    end
-
-    test "deactivates the user", %{conn: conn, admin: admin} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
-
-      user = User.get_cached_by_id(user.id)
-      assert user.info.deactivated == true
-      assert json_response(conn, :no_content)
+      assert json_response(conn, 200) == %{
+               "is_admin" => false
+             }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deactivated user @#{user.nickname}"
+               "@#{admin.nickname} revoked admin role from @#{user.nickname}"
     end
 
-    test "activates the user", %{conn: conn, admin: admin} do
-      user = insert(:user, info: %{deactivated: true})
+    test "/:right DELETE, can remove from a permission group (multiple)" do
+      admin = insert(:user, is_admin: true)
+      user_one = insert(:user, is_admin: true)
+      user_two = insert(:user, is_admin: true)
 
       conn =
-        conn
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: true})
+        build_conn()
+        |> assign(:user, admin)
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users/permission_group/admin", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
 
-      user = User.get_cached_by_id(user.id)
-      assert user.info.deactivated == false
-      assert json_response(conn, :no_content)
+      assert json_response(conn, 200) == %{
+               "is_admin" => false
+             }
 
       log_entry = Repo.one(ModerationLog)
 
       assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} activated user @#{user.nickname}"
-    end
-
-    test "returns 403 when requested by a non-admin", %{conn: conn} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> assign(:user, user)
-        |> put("/api/pleroma/admin/users/#{user.nickname}/activation_status", %{status: false})
-
-      assert json_response(conn, :forbidden)
+               "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
+                 user_two.nickname
+               }"
     end
   end
 
   describe "POST /api/pleroma/admin/email_invite, with valid config" do
     setup do
-      [user: insert(:user, info: %{is_admin: true})]
+      [user: insert(:user, is_admin: true)]
     end
 
     clear_config([:instance, :registrations_open]) do
@@ -543,7 +563,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
     setup do
-      [user: insert(:user, info: %{is_admin: true})]
+      [user: insert(:user, is_admin: true)]
     end
 
     clear_config([:instance, :registrations_open])
@@ -574,20 +594,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
-  test "/api/pleroma/admin/users/invite_token" do
-    admin = insert(:user, info: %{is_admin: true})
-
-    conn =
-      build_conn()
-      |> assign(:user, admin)
-      |> put_req_header("accept", "application/json")
-      |> get("/api/pleroma/admin/users/invite_token")
-
-    assert conn.status == 200
-  end
-
   test "/api/pleroma/admin/users/:nickname/password_reset" do
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
     user = insert(:user)
 
     conn =
@@ -603,7 +611,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/users" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -619,7 +627,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       users =
         [
           %{
-            "deactivated" => admin.info.deactivated,
+            "deactivated" => admin.deactivated,
             "id" => admin.id,
             "nickname" => admin.nickname,
             "roles" => %{"admin" => true, "moderator" => false},
@@ -629,7 +637,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "display_name" => HTML.strip_tags(admin.name || admin.nickname)
           },
           %{
-            "deactivated" => user.info.deactivated,
+            "deactivated" => user.deactivated,
             "id" => user.id,
             "nickname" => user.nickname,
             "roles" => %{"admin" => false, "moderator" => false},
@@ -670,7 +678,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -694,7 +702,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -718,7 +726,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -742,7 +750,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -766,7 +774,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -790,7 +798,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 1,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -809,7 +817,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 1,
                "users" => [
                  %{
-                   "deactivated" => user2.info.deactivated,
+                   "deactivated" => user2.deactivated,
                    "id" => user2.id,
                    "nickname" => user2.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -823,7 +831,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "only local users" do
-      admin = insert(:user, info: %{is_admin: true}, nickname: "john")
+      admin = insert(:user, is_admin: true, nickname: "john")
       user = insert(:user, nickname: "bob")
 
       insert(:user, nickname: "bobb", local: false)
@@ -838,7 +846,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -852,7 +860,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "only local users with no query", %{admin: old_admin} do
-      admin = insert(:user, info: %{is_admin: true}, nickname: "john")
+      admin = insert(:user, is_admin: true, nickname: "john")
       user = insert(:user, nickname: "bob")
 
       insert(:user, nickname: "bobb", local: false)
@@ -865,7 +873,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       users =
         [
           %{
-            "deactivated" => user.info.deactivated,
+            "deactivated" => user.deactivated,
             "id" => user.id,
             "nickname" => user.nickname,
             "roles" => %{"admin" => false, "moderator" => false},
@@ -875,7 +883,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
             "display_name" => HTML.strip_tags(user.name || user.nickname)
           },
           %{
-            "deactivated" => admin.info.deactivated,
+            "deactivated" => admin.deactivated,
             "id" => admin.id,
             "nickname" => admin.nickname,
             "roles" => %{"admin" => true, "moderator" => false},
@@ -905,7 +913,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "load only admins", %{conn: conn, admin: admin} do
-      second_admin = insert(:user, info: %{is_admin: true})
+      second_admin = insert(:user, is_admin: true)
       insert(:user)
       insert(:user)
 
@@ -944,7 +952,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "load only moderators", %{conn: conn} do
-      moderator = insert(:user, info: %{is_moderator: true})
+      moderator = insert(:user, is_moderator: true)
       insert(:user)
       insert(:user)
 
@@ -1009,11 +1017,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "it works with multiple filters" do
-      admin = insert(:user, nickname: "john", info: %{is_admin: true})
-      user = insert(:user, nickname: "bob", local: false, info: %{deactivated: true})
+      admin = insert(:user, nickname: "john", is_admin: true)
+      user = insert(:user, nickname: "bob", local: false, deactivated: true)
 
-      insert(:user, nickname: "ken", local: true, info: %{deactivated: true})
-      insert(:user, nickname: "bobb", local: false, info: %{deactivated: false})
+      insert(:user, nickname: "ken", local: true, deactivated: true)
+      insert(:user, nickname: "bobb", local: false, deactivated: false)
 
       conn =
         build_conn()
@@ -1025,7 +1033,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "page_size" => 50,
                "users" => [
                  %{
-                   "deactivated" => user.info.deactivated,
+                   "deactivated" => user.deactivated,
                    "id" => user.id,
                    "nickname" => user.nickname,
                    "roles" => %{"admin" => false, "moderator" => false},
@@ -1037,10 +1045,80 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                ]
              }
     end
+
+    test "it omits relay user", %{admin: admin} do
+      assert %User{} = Relay.get_actor()
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/users")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [
+                 %{
+                   "deactivated" => admin.deactivated,
+                   "id" => admin.id,
+                   "nickname" => admin.nickname,
+                   "roles" => %{"admin" => true, "moderator" => false},
+                   "local" => true,
+                   "tags" => [],
+                   "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
+                   "display_name" => HTML.strip_tags(admin.name || admin.nickname)
+                 }
+               ]
+             }
+    end
+  end
+
+  test "PATCH /api/pleroma/admin/users/activate" do
+    admin = insert(:user, is_admin: true)
+    user_one = insert(:user, deactivated: true)
+    user_two = insert(:user, deactivated: true)
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> patch(
+        "/api/pleroma/admin/users/activate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/deactivate" do
+    admin = insert(:user, is_admin: true)
+    user_one = insert(:user, deactivated: false)
+    user_two = insert(:user, deactivated: false)
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> patch(
+        "/api/pleroma/admin/users/deactivate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
   end
 
   test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
-    admin = insert(:user, info: %{is_admin: true})
+    admin = insert(:user, is_admin: true)
     user = insert(:user)
 
     conn =
@@ -1050,7 +1128,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
     assert json_response(conn, 200) ==
              %{
-               "deactivated" => !user.info.deactivated,
+               "deactivated" => !user.deactivated,
                "id" => user.id,
                "nickname" => user.nickname,
                "roles" => %{"admin" => false, "moderator" => false},
@@ -1063,12 +1141,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     log_entry = Repo.one(ModerationLog)
 
     assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} deactivated user @#{user.nickname}"
+             "@#{admin.nickname} deactivated users: @#{user.nickname}"
   end
 
-  describe "GET /api/pleroma/admin/users/invite_token" do
+  describe "POST /api/pleroma/admin/users/invite_token" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -1078,10 +1156,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "without options", %{conn: conn} do
-      conn = get(conn, "/api/pleroma/admin/users/invite_token")
+      conn = post(conn, "/api/pleroma/admin/users/invite_token")
 
-      token = json_response(conn, 200)
-      invite = UserInviteToken.find_by_token!(token)
+      invite_json = json_response(conn, 200)
+      invite = UserInviteToken.find_by_token!(invite_json["token"])
       refute invite.used
       refute invite.expires_at
       refute invite.max_use
@@ -1090,12 +1168,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
     test "with expires_at", %{conn: conn} do
       conn =
-        get(conn, "/api/pleroma/admin/users/invite_token", %{
-          "invite" => %{"expires_at" => Date.to_string(Date.utc_today())}
+        post(conn, "/api/pleroma/admin/users/invite_token", %{
+          "expires_at" => Date.to_string(Date.utc_today())
         })
 
-      token = json_response(conn, 200)
-      invite = UserInviteToken.find_by_token!(token)
+      invite_json = json_response(conn, 200)
+      invite = UserInviteToken.find_by_token!(invite_json["token"])
 
       refute invite.used
       assert invite.expires_at == Date.utc_today()
@@ -1104,13 +1182,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "with max_use", %{conn: conn} do
-      conn =
-        get(conn, "/api/pleroma/admin/users/invite_token", %{
-          "invite" => %{"max_use" => 150}
-        })
+      conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
 
-      token = json_response(conn, 200)
-      invite = UserInviteToken.find_by_token!(token)
+      invite_json = json_response(conn, 200)
+      invite = UserInviteToken.find_by_token!(invite_json["token"])
       refute invite.used
       refute invite.expires_at
       assert invite.max_use == 150
@@ -1119,12 +1194,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
     test "with max use and expires_at", %{conn: conn} do
       conn =
-        get(conn, "/api/pleroma/admin/users/invite_token", %{
-          "invite" => %{"max_use" => 150, "expires_at" => Date.to_string(Date.utc_today())}
+        post(conn, "/api/pleroma/admin/users/invite_token", %{
+          "max_use" => 150,
+          "expires_at" => Date.to_string(Date.utc_today())
         })
 
-      token = json_response(conn, 200)
-      invite = UserInviteToken.find_by_token!(token)
+      invite_json = json_response(conn, 200)
+      invite = UserInviteToken.find_by_token!(invite_json["token"])
       refute invite.used
       assert invite.expires_at == Date.utc_today()
       assert invite.max_use == 150
@@ -1134,7 +1210,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/users/invites" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -1172,7 +1248,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "POST /api/pleroma/admin/users/revoke_invite" do
     test "with token" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       {:ok, invite} = UserInviteToken.create_invite()
 
       conn =
@@ -1192,7 +1268,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "with invalid token" do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       conn =
         build_conn()
@@ -1205,7 +1281,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/reports/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin)}
     end
@@ -1236,9 +1312,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
-  describe "PUT /api/pleroma/admin/reports/:id" do
+  describe "PATCH /api/pleroma/admin/reports" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       [reporter, target_user] = insert_pair(:user)
       activity = insert(:note_activity, user: target_user)
 
@@ -1249,16 +1325,32 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
           "status_ids" => [activity.id]
         })
 
-      %{conn: assign(conn, :user, admin), id: report_id, admin: admin}
+      {:ok, %{id: second_report_id}} =
+        CommonAPI.report(reporter, %{
+          "account_id" => target_user.id,
+          "comment" => "I feel very offended",
+          "status_ids" => [activity.id]
+        })
+
+      %{
+        conn: assign(conn, :user, admin),
+        id: report_id,
+        admin: admin,
+        second_report_id: second_report_id
+      }
     end
 
     test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
-      response =
-        conn
-        |> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"})
-        |> json_response(:ok)
+      conn
+      |> patch("/api/pleroma/admin/reports", %{
+        "reports" => [
+          %{"state" => "resolved", "id" => id}
+        ]
+      })
+      |> json_response(:no_content)
 
-      assert response["state"] == "resolved"
+      activity = Activity.get_by_id(id)
+      assert activity.data["state"] == "resolved"
 
       log_entry = Repo.one(ModerationLog)
 
@@ -1267,12 +1359,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "closes report", %{conn: conn, id: id, admin: admin} do
-      response =
-        conn
-        |> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"})
-        |> json_response(:ok)
+      conn
+      |> patch("/api/pleroma/admin/reports", %{
+        "reports" => [
+          %{"state" => "closed", "id" => id}
+        ]
+      })
+      |> json_response(:no_content)
 
-      assert response["state"] == "closed"
+      activity = Activity.get_by_id(id)
+      assert activity.data["state"] == "closed"
 
       log_entry = Repo.one(ModerationLog)
 
@@ -1283,23 +1379,60 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     test "returns 400 when state is unknown", %{conn: conn, id: id} do
       conn =
         conn
-        |> put("/api/pleroma/admin/reports/#{id}", %{"state" => "test"})
+        |> patch("/api/pleroma/admin/reports", %{
+          "reports" => [
+            %{"state" => "test", "id" => id}
+          ]
+        })
 
-      assert json_response(conn, :bad_request) == "Unsupported state"
+      assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
     end
 
     test "returns 404 when report is not exist", %{conn: conn} do
       conn =
         conn
-        |> put("/api/pleroma/admin/reports/test", %{"state" => "closed"})
+        |> patch("/api/pleroma/admin/reports", %{
+          "reports" => [
+            %{"state" => "closed", "id" => "test"}
+          ]
+        })
 
-      assert json_response(conn, :not_found) == "Not found"
+      assert hd(json_response(conn, :bad_request))["error"] == "not_found"
+    end
+
+    test "updates state of multiple reports", %{
+      conn: conn,
+      id: id,
+      admin: admin,
+      second_report_id: second_report_id
+    } do
+      conn
+      |> patch("/api/pleroma/admin/reports", %{
+        "reports" => [
+          %{"state" => "resolved", "id" => id},
+          %{"state" => "closed", "id" => second_report_id}
+        ]
+      })
+      |> json_response(:no_content)
+
+      activity = Activity.get_by_id(id)
+      second_activity = Activity.get_by_id(second_report_id)
+      assert activity.data["state"] == "resolved"
+      assert second_activity.data["state"] == "closed"
+
+      [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(first_log_entry) ==
+               "@#{admin.nickname} updated report ##{id} with 'resolved' state"
+
+      assert ModerationLog.get_log_entry_message(second_log_entry) ==
+               "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
     end
   end
 
   describe "GET /api/pleroma/admin/reports" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin)}
     end
@@ -1416,10 +1549,77 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
-  #
-  describe "POST /api/pleroma/admin/reports/:id/respond" do
+  describe "GET /api/pleroma/admin/grouped_reports" do
     setup %{conn: conn} do
       admin = insert(:user, info: %{is_admin: true})
+      [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})
+
+      %{
+        conn: assign(conn, :user, admin),
+        reporter: reporter,
+        target_user: target_user,
+        first_status: first_status,
+        second_status: second_status,
+        third_status: third_status
+      }
+    end
+
+    test "returns reports grouped by status", %{
+      conn: conn,
+      reporter: reporter,
+      target_user: target_user,
+      first_status: first_status,
+      second_status: second_status,
+      third_status: third_status
+    } do
+      {:ok, %{id: _}} =
+        CommonAPI.report(reporter, %{
+          "account_id" => target_user.id,
+          "status_ids" => [first_status.id, second_status.id, third_status.id]
+        })
+
+      {:ok, %{id: _}} =
+        CommonAPI.report(reporter, %{
+          "account_id" => target_user.id,
+          "status_ids" => [first_status.id, second_status.id]
+        })
+
+      {:ok, %{id: _}} =
+        CommonAPI.report(reporter, %{
+          "account_id" => target_user.id,
+          "status_ids" => [first_status.id]
+        })
+
+      response =
+        conn
+        |> get("/api/pleroma/admin/grouped_reports")
+        |> json_response(:ok)
+
+      assert length(response["reports"]) == 3
+      [third_group, second_group, first_group] = response["reports"]
+
+      assert length(third_group["reports"]) == 3
+      assert length(second_group["reports"]) == 2
+      assert length(first_group["reports"]) == 1
+    end
+  end
+
+  describe "POST /api/pleroma/admin/reports/:id/respond" do
+    setup %{conn: conn} do
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin), admin: admin}
     end
@@ -1474,7 +1674,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "PUT /api/pleroma/admin/statuses/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       activity = insert(:note_activity)
 
       %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
@@ -1540,7 +1740,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "DELETE /api/pleroma/admin/statuses/:id" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       activity = insert(:note_activity)
 
       %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
@@ -1570,7 +1770,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/config" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       %{conn: assign(conn, :user, admin)}
     end
@@ -1607,7 +1807,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "POST /api/pleroma/admin/config" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       temp_file = "config/test.exported_from_db.secret.exs"
 
@@ -2175,7 +2375,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "config mix tasks run" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
 
       temp_file = "config/test.exported_from_db.secret.exs"
 
@@ -2211,7 +2411,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/users/:nickname/statuses" do
     setup do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       user = insert(:user)
 
       date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
@@ -2268,9 +2468,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/moderation_log" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
+      moderator = insert(:user, is_moderator: true)
 
-      %{conn: assign(conn, :user, admin), admin: admin}
+      %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator}
     end
 
     test "returns the log", %{conn: conn, admin: admin} do
@@ -2303,9 +2504,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       conn = get(conn, "/api/pleroma/admin/moderation_log")
 
       response = json_response(conn, 200)
-      [first_entry, second_entry] = response
+      [first_entry, second_entry] = response["items"]
 
-      assert response |> length() == 2
+      assert response["total"] == 2
       assert first_entry["data"]["action"] == "relay_unfollow"
 
       assert first_entry["message"] ==
@@ -2347,9 +2548,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
 
       response1 = json_response(conn1, 200)
-      [first_entry] = response1
+      [first_entry] = response1["items"]
 
-      assert response1 |> length() == 1
+      assert response1["total"] == 2
+      assert response1["items"] |> length() == 1
       assert first_entry["data"]["action"] == "relay_unfollow"
 
       assert first_entry["message"] ==
@@ -2358,14 +2560,209 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
 
       response2 = json_response(conn2, 200)
-      [second_entry] = response2
+      [second_entry] = response2["items"]
 
-      assert response2 |> length() == 1
+      assert response2["total"] == 2
+      assert response2["items"] |> length() == 1
       assert second_entry["data"]["action"] == "relay_follow"
 
       assert second_entry["message"] ==
                "@#{admin.nickname} followed relay: https://example.org/relay"
     end
+
+    test "filters log by date", %{conn: conn, admin: admin} do
+      first_date = "2017-08-15T15:47:06Z"
+      second_date = "2017-08-20T15:47:06Z"
+
+      Repo.insert(%ModerationLog{
+        data: %{
+          actor: %{
+            "id" => admin.id,
+            "nickname" => admin.nickname,
+            "type" => "user"
+          },
+          action: "relay_follow",
+          target: "https://example.org/relay"
+        },
+        inserted_at: NaiveDateTime.from_iso8601!(first_date)
+      })
+
+      Repo.insert(%ModerationLog{
+        data: %{
+          actor: %{
+            "id" => admin.id,
+            "nickname" => admin.nickname,
+            "type" => "user"
+          },
+          action: "relay_unfollow",
+          target: "https://example.org/relay"
+        },
+        inserted_at: NaiveDateTime.from_iso8601!(second_date)
+      })
+
+      conn1 =
+        get(
+          conn,
+          "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
+        )
+
+      response1 = json_response(conn1, 200)
+      [first_entry] = response1["items"]
+
+      assert response1["total"] == 1
+      assert first_entry["data"]["action"] == "relay_unfollow"
+
+      assert first_entry["message"] ==
+               "@#{admin.nickname} unfollowed relay: https://example.org/relay"
+    end
+
+    test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
+      Repo.insert(%ModerationLog{
+        data: %{
+          actor: %{
+            "id" => admin.id,
+            "nickname" => admin.nickname,
+            "type" => "user"
+          },
+          action: "relay_follow",
+          target: "https://example.org/relay"
+        }
+      })
+
+      Repo.insert(%ModerationLog{
+        data: %{
+          actor: %{
+            "id" => moderator.id,
+            "nickname" => moderator.nickname,
+            "type" => "user"
+          },
+          action: "relay_unfollow",
+          target: "https://example.org/relay"
+        }
+      })
+
+      conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
+
+      response1 = json_response(conn1, 200)
+      [first_entry] = response1["items"]
+
+      assert response1["total"] == 1
+      assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
+    end
+
+    test "returns log filtered by search", %{conn: conn, moderator: moderator} do
+      ModerationLog.insert_log(%{
+        actor: moderator,
+        action: "relay_follow",
+        target: "https://example.org/relay"
+      })
+
+      ModerationLog.insert_log(%{
+        actor: moderator,
+        action: "relay_unfollow",
+        target: "https://example.org/relay"
+      })
+
+      conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
+
+      response1 = json_response(conn1, 200)
+      [first_entry] = response1["items"]
+
+      assert response1["total"] == 1
+
+      assert get_in(first_entry, ["data", "message"]) ==
+               "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
+    end
+  end
+
+  describe "PATCH /users/:nickname/force_password_reset" do
+    setup %{conn: conn} do
+      admin = insert(:user, is_admin: true)
+      user = insert(:user)
+
+      %{conn: assign(conn, :user, admin), admin: admin, user: user}
+    end
+
+    test "sets password_reset_pending to true", %{admin: admin, user: user} do
+      assert user.password_reset_pending == false
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> patch("/api/pleroma/admin/users/#{user.nickname}/force_password_reset")
+
+      assert json_response(conn, 204) == ""
+
+      ObanHelpers.perform_all()
+
+      assert User.get_by_id(user.id).password_reset_pending == true
+    end
+  end
+
+  describe "relays" do
+    setup %{conn: conn} do
+      admin = insert(:user, is_admin: true)
+
+      %{conn: assign(conn, :user, admin), admin: admin}
+    end
+
+    test "POST /relay", %{admin: admin} do
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> post("/api/pleroma/admin/relay", %{
+          relay_url: "http://mastodon.example.org/users/admin"
+        })
+
+      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
+    end
+
+    test "GET /relay", %{admin: admin} do
+      relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
+
+      ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
+      |> Enum.each(fn ap_id ->
+        {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
+        User.follow(relay_user, user)
+      end)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/relay")
+
+      assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
+    end
+
+    test "DELETE /relay", %{admin: admin} do
+      build_conn()
+      |> assign(:user, admin)
+      |> post("/api/pleroma/admin/relay", %{
+        relay_url: "http://mastodon.example.org/users/admin"
+      })
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> delete("/api/pleroma/admin/relay", %{
+          relay_url: "http://mastodon.example.org/users/admin"
+        })
+
+      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
+
+      [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry_one) ==
+               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
+
+      assert ModerationLog.get_log_entry_message(log_entry_two) ==
+               "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
+    end
   end
 end