Add OpenAPI spec for PleromaAPI.AccountController
authorEgor Kislitsyn <egor@kislitsyn.com>
Wed, 13 May 2020 15:06:25 +0000 (19:06 +0400)
committerEgor Kislitsyn <egor@kislitsyn.com>
Wed, 13 May 2020 15:06:46 +0000 (19:06 +0400)
lib/pleroma/upload.ex
lib/pleroma/web/api_spec/helpers.ex
lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex [new file with mode: 0644]
lib/pleroma/web/api_spec/operations/status_operation.ex
lib/pleroma/web/pleroma_api/controllers/account_controller.ex
test/web/activity_pub/activity_pub_test.exs
test/web/pleroma_api/controllers/account_controller_test.exs

index 762d813d98a279f409f248e148a5742a26a0e9d7..1be1a3a5b3e03c371b89549374b8cf9579187e0c 100644 (file)
@@ -134,7 +134,7 @@ defmodule Pleroma.Upload do
     end
   end
 
-  defp prepare_upload(%{"img" => "data:image/" <> image_data}, opts) do
+  defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
     parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
     data = Base.decode64!(parsed["data"], ignore: :whitespace)
     hash = String.downcase(Base.encode16(:crypto.hash(:sha256, data)))
index 183df43eea9a03819753219ec33b31edac1b3a25..f0b558aa533aa25ff23aa199288fcefa40112c58 100644 (file)
@@ -54,4 +54,8 @@ defmodule Pleroma.Web.ApiSpec.Helpers do
   def empty_array_response do
     Operation.response("Empty array", "application/json", %Schema{type: :array, example: []})
   end
+
+  def no_content_response do
+    Operation.response("No Content", "application/json", %Schema{type: :string, example: ""})
+  end
 end
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
new file mode 100644 (file)
index 0000000..af231b4
--- /dev/null
@@ -0,0 +1,185 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
+  alias OpenApiSpex.Operation
+  alias OpenApiSpex.Schema
+  alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
+  alias Pleroma.Web.ApiSpec.Schemas.ApiError
+  alias Pleroma.Web.ApiSpec.Schemas.FlakeID
+  alias Pleroma.Web.ApiSpec.StatusOperation
+
+  import Pleroma.Web.ApiSpec.Helpers
+
+  def open_api_operation(action) do
+    operation = String.to_existing_atom("#{action}_operation")
+    apply(__MODULE__, operation, [])
+  end
+
+  def confirmation_resend_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Resend confirmation email. Expects `email` or `nic`",
+      operationId: "PleromaAPI.AccountController.confirmation_resend",
+      parameters: [
+        Operation.parameter(:email, :query, :string, "Email of that needs to be verified",
+          example: "cofe@cofe.io"
+        ),
+        Operation.parameter(
+          :nickname,
+          :query,
+          :string,
+          "Nickname of user that needs to be verified",
+          example: "cofefe"
+        )
+      ],
+      responses: %{
+        204 => no_content_response()
+      }
+    }
+  end
+
+  def update_avatar_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Set/clear user avatar image",
+      operationId: "PleromaAPI.AccountController.update_avatar",
+      requestBody:
+        request_body("Parameters", update_avatar_or_background_request(), required: true),
+      security: [%{"oAuth" => ["write:accounts"]}],
+      responses: %{
+        200 => update_response(),
+        403 => Operation.response("Forbidden", "application/json", ApiError)
+      }
+    }
+  end
+
+  def update_banner_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Set/clear user banner image",
+      operationId: "PleromaAPI.AccountController.update_banner",
+      requestBody: request_body("Parameters", update_banner_request(), required: true),
+      security: [%{"oAuth" => ["write:accounts"]}],
+      responses: %{
+        200 => update_response()
+      }
+    }
+  end
+
+  def update_background_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Set/clear user background image",
+      operationId: "PleromaAPI.AccountController.update_background",
+      security: [%{"oAuth" => ["write:accounts"]}],
+      requestBody:
+        request_body("Parameters", update_avatar_or_background_request(), required: true),
+      responses: %{
+        200 => update_response()
+      }
+    }
+  end
+
+  def favourites_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Returns favorites timeline of any user",
+      operationId: "PleromaAPI.AccountController.favourites",
+      parameters: [id_param() | pagination_params()],
+      security: [%{"oAuth" => ["read:favourites"]}],
+      responses: %{
+        200 =>
+          Operation.response(
+            "Array of Statuses",
+            "application/json",
+            StatusOperation.array_of_statuses()
+          ),
+        403 => Operation.response("Forbidden", "application/json", ApiError),
+        404 => Operation.response("Not Found", "application/json", ApiError)
+      }
+    }
+  end
+
+  def subscribe_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Subscribe to receive notifications for all statuses posted by a user",
+      operationId: "PleromaAPI.AccountController.subscribe",
+      parameters: [id_param()],
+      security: [%{"oAuth" => ["follow", "write:follows"]}],
+      responses: %{
+        200 => Operation.response("Relationship", "application/json", AccountRelationship),
+        404 => Operation.response("Not Found", "application/json", ApiError)
+      }
+    }
+  end
+
+  def unsubscribe_operation do
+    %Operation{
+      tags: ["Accounts"],
+      summary: "Unsubscribe to stop receiving notifications from user statuses¶",
+      operationId: "PleromaAPI.AccountController.unsubscribe",
+      parameters: [id_param()],
+      security: [%{"oAuth" => ["follow", "write:follows"]}],
+      responses: %{
+        200 => Operation.response("Relationship", "application/json", AccountRelationship),
+        404 => Operation.response("Not Found", "application/json", ApiError)
+      }
+    }
+  end
+
+  defp id_param do
+    Operation.parameter(:id, :path, FlakeID, "Account ID",
+      example: "9umDrYheeY451cQnEe",
+      required: true
+    )
+  end
+
+  defp update_avatar_or_background_request do
+    %Schema{
+      title: "PleromaAccountUpdateAvatarOrBackgroundRequest",
+      type: :object,
+      properties: %{
+        img: %Schema{
+          type: :string,
+          format: :binary,
+          description: "Image encoded using `multipart/form-data` or an empty string to clear"
+        }
+      }
+    }
+  end
+
+  defp update_banner_request do
+    %Schema{
+      title: "PleromaAccountUpdateBannerRequest",
+      type: :object,
+      properties: %{
+        banner: %Schema{
+          type: :string,
+          format: :binary,
+          description: "Image encoded using `multipart/form-data` or an empty string to clear"
+        }
+      }
+    }
+  end
+
+  defp update_response do
+    Operation.response("PleromaAccountUpdateResponse", "application/json", %Schema{
+      type: :object,
+      properties: %{
+        url: %Schema{
+          type: :string,
+          format: :uri,
+          nullable: true,
+          description: "Image URL"
+        }
+      },
+      example: %{
+        "url" =>
+          "https://cofe.party/media/9d0add56-bcb6-4c0f-8225-cbbd0b6dd773/13eadb6972c9ccd3f4ffa3b8196f0e0d38b4d2f27594457c52e52946c054cd9a.gif"
+      }
+    })
+  end
+end
index a6bb87560c749187f05ce6290db43ae2e6004e16..561db3bce91e745feb8b3fd812ee5bc5dab97672 100644 (file)
@@ -360,7 +360,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
     }
   end
 
-  defp array_of_statuses do
+  def array_of_statuses do
     %Schema{type: :array, items: Status, example: [Status.schema().example]}
   end
 
index be7477867b3592766fa10170331922c90c50c6a9..07078d41572bf84752ae531e31cd632ac82e6fea 100644 (file)
@@ -18,6 +18,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
 
   require Pleroma.Constants
 
+  plug(
+    OpenApiSpex.Plug.PutApiSpec,
+    [module: Pleroma.Web.ApiSpec] when action == :confirmation_resend
+  )
+
+  plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
   plug(
     :skip_plug,
     [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirmation_resend
@@ -49,9 +56,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
   plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
   plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
 
+  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
+
   @doc "POST /api/v1/pleroma/accounts/confirmation_resend"
   def confirmation_resend(conn, params) do
-    nickname_or_email = params["email"] || params["nickname"]
+    nickname_or_email = params[:email] || params[:nickname]
 
     with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email),
          {:ok, _} <- User.try_send_confirmation_email(user) do
@@ -60,7 +69,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
   end
 
   @doc "PATCH /api/v1/pleroma/accounts/update_avatar"
-  def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
+  def update_avatar(%{assigns: %{user: user}, body_params: %{img: ""}} = conn, _) do
     {:ok, _user} =
       user
       |> Changeset.change(%{avatar: nil})
@@ -69,7 +78,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
     json(conn, %{url: nil})
   end
 
-  def update_avatar(%{assigns: %{user: user}} = conn, params) do
+  def update_avatar(%{assigns: %{user: user}, body_params: params} = conn, _params) do
     {:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar)
     {:ok, _user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache()
     %{"url" => [%{"href" => href} | _]} = data
@@ -78,14 +87,14 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
   end
 
   @doc "PATCH /api/v1/pleroma/accounts/update_banner"
-  def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
+  def update_banner(%{assigns: %{user: user}, body_params: %{banner: ""}} = conn, _) do
     with {:ok, _user} <- User.update_banner(user, %{}) do
       json(conn, %{url: nil})
     end
   end
 
-  def update_banner(%{assigns: %{user: user}} = conn, params) do
-    with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
+  def update_banner(%{assigns: %{user: user}, body_params: params} = conn, _) do
+    with {:ok, object} <- ActivityPub.upload(%{img: params[:banner]}, type: :banner),
          {:ok, _user} <- User.update_banner(user, object.data) do
       %{"url" => [%{"href" => href} | _]} = object.data
 
@@ -94,13 +103,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
   end
 
   @doc "PATCH /api/v1/pleroma/accounts/update_background"
-  def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
+  def update_background(%{assigns: %{user: user}, body_params: %{img: ""}} = conn, _) do
     with {:ok, _user} <- User.update_background(user, %{}) do
       json(conn, %{url: nil})
     end
   end
 
-  def update_background(%{assigns: %{user: user}} = conn, params) do
+  def update_background(%{assigns: %{user: user}, body_params: params} = conn, _) do
     with {:ok, object} <- ActivityPub.upload(params, type: :background),
          {:ok, _user} <- User.update_background(user, object.data) do
       %{"url" => [%{"href" => href} | _]} = object.data
@@ -117,6 +126,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
   def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
     params =
       params
+      |> Map.new(fn {key, value} -> {to_string(key), value} end)
       |> Map.put("type", "Create")
       |> Map.put("favorited_by", user.ap_id)
       |> Map.put("blocking_user", for_user)
index 56fde97e7f106bfa4814ec24f222c0a27bbcaa6d..77bd07edf24332897a94177b7aae0a64771531b8 100644 (file)
@@ -951,7 +951,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
 
     test "works with base64 encoded images" do
       file = %{
-        "img" => data_uri()
+        img: data_uri()
       }
 
       {:ok, %Object{}} = ActivityPub.upload(file)
index 34fc4aa23115d66155e2f0816537e44ce06396f2..103997c31b394030a3b1f829c4d7efdc36adbae6 100644 (file)
@@ -31,8 +31,28 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
 
     test "resend account confirmation email", %{conn: conn, user: user} do
       conn
+      |> put_req_header("content-type", "application/json")
       |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
-      |> json_response(:no_content)
+      |> json_response_and_validate_schema(:no_content)
+
+      ObanHelpers.perform_all()
+
+      email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
+      notify_email = Config.get([:instance, :notify_email])
+      instance_name = Config.get([:instance, :name])
+
+      assert_email_sent(
+        from: {instance_name, notify_email},
+        to: {user.name, user.email},
+        html_body: email.html_body
+      )
+    end
+
+    test "resend account confirmation email (with nickname)", %{conn: conn, user: user} do
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> post("/api/v1/pleroma/accounts/confirmation_resend?nickname=#{user.nickname}")
+      |> json_response_and_validate_schema(:no_content)
 
       ObanHelpers.perform_all()
 
@@ -54,7 +74,10 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     test "user avatar can be set", %{user: user, conn: conn} do
       avatar_image = File.read!("test/fixtures/avatar_data_uri")
 
-      conn = patch(conn, "/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
 
       user = refresh_record(user)
 
@@ -70,17 +93,20 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
                ]
              } = user.avatar
 
-      assert %{"url" => _} = json_response(conn, 200)
+      assert %{"url" => _} = json_response_and_validate_schema(conn, 200)
     end
 
     test "user avatar can be reset", %{user: user, conn: conn} do
-      conn = patch(conn, "/api/v1/pleroma/accounts/update_avatar", %{img: ""})
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
 
       user = User.get_cached_by_id(user.id)
 
       assert user.avatar == nil
 
-      assert %{"url" => nil} = json_response(conn, 200)
+      assert %{"url" => nil} = json_response_and_validate_schema(conn, 200)
     end
   end
 
@@ -88,21 +114,27 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     setup do: oauth_access(["write:accounts"])
 
     test "can set profile banner", %{user: user, conn: conn} do
-      conn = patch(conn, "/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
 
       user = refresh_record(user)
       assert user.banner["type"] == "Image"
 
-      assert %{"url" => _} = json_response(conn, 200)
+      assert %{"url" => _} = json_response_and_validate_schema(conn, 200)
     end
 
     test "can reset profile banner", %{user: user, conn: conn} do
-      conn = patch(conn, "/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
 
       user = refresh_record(user)
       assert user.banner == %{}
 
-      assert %{"url" => nil} = json_response(conn, 200)
+      assert %{"url" => nil} = json_response_and_validate_schema(conn, 200)
     end
   end
 
@@ -110,19 +142,26 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     setup do: oauth_access(["write:accounts"])
 
     test "background image can be set", %{user: user, conn: conn} do
-      conn = patch(conn, "/api/v1/pleroma/accounts/update_background", %{"img" => @image})
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
 
       user = refresh_record(user)
       assert user.background["type"] == "Image"
-      assert %{"url" => _} = json_response(conn, 200)
+      # assert %{"url" => _} = json_response(conn, 200)
+      assert %{"url" => _} = json_response_and_validate_schema(conn, 200)
     end
 
     test "background image can be reset", %{user: user, conn: conn} do
-      conn = patch(conn, "/api/v1/pleroma/accounts/update_background", %{"img" => ""})
+      conn =
+        conn
+        |> put_req_header("content-type", "multipart/form-data")
+        |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
 
       user = refresh_record(user)
       assert user.background == %{}
-      assert %{"url" => nil} = json_response(conn, 200)
+      assert %{"url" => nil} = json_response_and_validate_schema(conn, 200)
     end
   end
 
@@ -143,7 +182,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       response =
         conn
         |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
-        |> json_response(:ok)
+        |> json_response_and_validate_schema(:ok)
 
       [like] = response
 
@@ -160,7 +199,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       response =
         build_conn()
         |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
-        |> json_response(200)
+        |> json_response_and_validate_schema(200)
 
       assert length(response) == 1
     end
@@ -183,7 +222,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
           |> assign(:user, u)
           |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"]))
           |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
-          |> json_response(:ok)
+          |> json_response_and_validate_schema(:ok)
 
         assert length(response) == 1
       end
@@ -191,7 +230,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       response =
         build_conn()
         |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
-        |> json_response(200)
+        |> json_response_and_validate_schema(200)
 
       assert length(response) == 0
     end
@@ -213,7 +252,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       response =
         conn
         |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
-        |> json_response(:ok)
+        |> json_response_and_validate_schema(:ok)
 
       assert Enum.empty?(response)
     end
@@ -233,11 +272,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
 
       response =
         conn
-        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
-          since_id: third_activity.id,
-          max_id: seventh_activity.id
-        })
-        |> json_response(:ok)
+        |> get(
+          "/api/v1/pleroma/accounts/#{user.id}/favourites?since_id=#{third_activity.id}&max_id=#{
+            seventh_activity.id
+          }"
+        )
+        |> json_response_and_validate_schema(:ok)
 
       assert length(response) == 3
       refute third_activity in response
@@ -256,8 +296,8 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
 
       response =
         conn
-        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
-        |> json_response(:ok)
+        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites?limit=3")
+        |> json_response_and_validate_schema(:ok)
 
       assert length(response) == 3
     end
@@ -269,7 +309,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       response =
         conn
         |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
-        |> json_response(:ok)
+        |> json_response_and_validate_schema(:ok)
 
       assert Enum.empty?(response)
     end
@@ -277,7 +317,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     test "returns 404 error when specified user is not exist", %{conn: conn} do
       conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
 
-      assert json_response(conn, 404) == %{"error" => "Record not found"}
+      assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
     end
 
     test "returns 403 error when user has hidden own favorites", %{conn: conn} do
@@ -287,7 +327,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
 
       conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
 
-      assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
+      assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"}
     end
 
     test "hides favorites for new users by default", %{conn: conn} do
@@ -298,7 +338,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       assert user.hide_favorites
       conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
 
-      assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
+      assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"}
     end
   end
 
@@ -312,11 +352,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
         |> assign(:user, user)
         |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
 
-      assert %{"id" => _id, "subscribing" => true} = json_response(ret_conn, 200)
+      assert %{"id" => _id, "subscribing" => true} =
+               json_response_and_validate_schema(ret_conn, 200)
 
       conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
 
-      assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
+      assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(conn, 200)
     end
   end
 
@@ -326,7 +367,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
 
       conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe")
 
-      assert %{"error" => "Record not found"} = json_response(conn, 404)
+      assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
     end
   end
 
@@ -336,7 +377,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
 
       conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe")
 
-      assert %{"error" => "Record not found"} = json_response(conn, 404)
+      assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
     end
   end
 end