Support validation for inline OpenAPI schema and automatic tests for examples
This commit is contained in:
parent
1b5f8d19ee
commit
f362836742
16 changed files with 267 additions and 244 deletions
57
test/support/api_spec_helpers.ex
Normal file
57
test/support/api_spec_helpers.ex
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Tests.ApiSpecHelpers do
|
||||
@moduledoc """
|
||||
OpenAPI spec test helpers
|
||||
"""
|
||||
|
||||
import ExUnit.Assertions
|
||||
|
||||
alias OpenApiSpex.Cast.Error
|
||||
alias OpenApiSpex.Reference
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
def assert_schema(value, schema) do
|
||||
api_spec = Pleroma.Web.ApiSpec.spec()
|
||||
|
||||
case OpenApiSpex.cast_value(value, schema, api_spec) do
|
||||
{:ok, data} ->
|
||||
data
|
||||
|
||||
{:error, errors} ->
|
||||
errors =
|
||||
Enum.map(errors, fn error ->
|
||||
message = Error.message(error)
|
||||
path = Error.path_to_string(error)
|
||||
"#{message} at #{path}"
|
||||
end)
|
||||
|
||||
flunk(
|
||||
"Value does not conform to schema #{schema.title}: #{Enum.join(errors, "\n")}\n#{
|
||||
inspect(value)
|
||||
}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_schema(%Schema{} = schema), do: schema
|
||||
|
||||
def resolve_schema(%Reference{} = ref) do
|
||||
schemas = Pleroma.Web.ApiSpec.spec().components.schemas
|
||||
Reference.resolve_schema(ref, schemas)
|
||||
end
|
||||
|
||||
def api_operations do
|
||||
paths = Pleroma.Web.ApiSpec.spec().paths
|
||||
|
||||
Enum.flat_map(paths, fn {_, path_item} ->
|
||||
path_item
|
||||
|> Map.take([:delete, :get, :head, :options, :patch, :post, :put, :trace])
|
||||
|> Map.values()
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.uniq()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
@ -51,6 +51,42 @@ defmodule Pleroma.Web.ConnCase do
|
|||
%{user: user, token: token, conn: conn}
|
||||
end
|
||||
|
||||
defp json_response_and_validate_schema(conn, status \\ nil) do
|
||||
content_type =
|
||||
conn
|
||||
|> Plug.Conn.get_resp_header("content-type")
|
||||
|> List.first()
|
||||
|> String.split(";")
|
||||
|> List.first()
|
||||
|
||||
status = status || conn.status
|
||||
|
||||
%{private: %{open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec}}} =
|
||||
conn
|
||||
|
||||
schema = lookup[op_id].responses[status].content[content_type].schema
|
||||
json = json_response(conn, status)
|
||||
|
||||
case OpenApiSpex.cast_value(json, schema, spec) do
|
||||
{:ok, _data} ->
|
||||
json
|
||||
|
||||
{:error, errors} ->
|
||||
errors =
|
||||
Enum.map(errors, fn error ->
|
||||
message = OpenApiSpex.Cast.Error.message(error)
|
||||
path = OpenApiSpex.Cast.Error.path_to_string(error)
|
||||
"#{message} at #{path}"
|
||||
end)
|
||||
|
||||
flunk(
|
||||
"Response does not conform to schema of #{op_id} operation: #{
|
||||
Enum.join(errors, "\n")
|
||||
}\n#{inspect(json)}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp ensure_federating_or_authenticated(conn, url, user) do
|
||||
initial_setting = Config.get([:instance, :federating])
|
||||
on_exit(fn -> Config.put([:instance, :federating], initial_setting) end)
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
# 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.AppOperationTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
|
||||
alias Pleroma.Web.ApiSpec
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse
|
||||
|
||||
import OpenApiSpex.TestAssertions
|
||||
import Pleroma.Factory
|
||||
|
||||
test "AppCreateRequest example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = AppCreateRequest.schema()
|
||||
assert_schema(schema.example, "AppCreateRequest", api_spec)
|
||||
end
|
||||
|
||||
test "AppCreateResponse example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = AppCreateResponse.schema()
|
||||
assert_schema(schema.example, "AppCreateResponse", api_spec)
|
||||
end
|
||||
|
||||
test "AppController produces a AppCreateResponse", %{conn: conn} do
|
||||
api_spec = ApiSpec.spec()
|
||||
app_attrs = build(:oauth_app)
|
||||
|
||||
json =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post(
|
||||
"/api/v1/apps",
|
||||
Jason.encode!(%{
|
||||
client_name: app_attrs.client_name,
|
||||
redirect_uris: app_attrs.redirect_uris
|
||||
})
|
||||
)
|
||||
|> json_response(200)
|
||||
|
||||
assert_schema(json, "AppCreateResponse", api_spec)
|
||||
end
|
||||
end
|
||||
43
test/web/api_spec/schema_examples_test.exs
Normal file
43
test/web/api_spec/schema_examples_test.exs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# 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.SchemaExamplesTest do
|
||||
use ExUnit.Case, async: true
|
||||
import Pleroma.Tests.ApiSpecHelpers
|
||||
|
||||
@content_type "application/json"
|
||||
|
||||
for operation <- api_operations() do
|
||||
describe operation.operationId <> " Request Body" do
|
||||
if operation.requestBody do
|
||||
@media_type operation.requestBody.content[@content_type]
|
||||
@schema resolve_schema(@media_type.schema)
|
||||
|
||||
if @media_type.example do
|
||||
test "request body media type example matches schema" do
|
||||
assert_schema(@media_type.example, @schema)
|
||||
end
|
||||
end
|
||||
|
||||
if @schema.example do
|
||||
test "request body schema example matches schema" do
|
||||
assert_schema(@schema.example, @schema)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for {status, response} <- operation.responses do
|
||||
describe "#{operation.operationId} - #{status} Response" do
|
||||
@schema resolve_schema(response.content[@content_type].schema)
|
||||
|
||||
if @schema.example do
|
||||
test "example matches schema" do
|
||||
assert_schema(@schema.example, @schema)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
|
|||
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||
}
|
||||
|
||||
assert expected == json_response(conn, 200)
|
||||
assert expected == json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
|
||||
test "creates an oauth app", %{conn: conn} do
|
||||
|
|
@ -55,6 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
|
|||
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||
}
|
||||
|
||||
assert expected == json_response(conn, 200)
|
||||
assert expected == json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,15 +5,13 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
alias Pleroma.Web.ApiSpec
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse
|
||||
import OpenApiSpex.TestAssertions
|
||||
|
||||
test "with tags", %{conn: conn} do
|
||||
assert resp =
|
||||
conn
|
||||
|> get("/api/v1/custom_emojis")
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [emoji | _body] = resp
|
||||
assert Map.has_key?(emoji, "shortcode")
|
||||
|
|
@ -23,19 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do
|
|||
assert Map.has_key?(emoji, "category")
|
||||
assert Map.has_key?(emoji, "url")
|
||||
assert Map.has_key?(emoji, "visible_in_picker")
|
||||
assert_schema(resp, "CustomEmojisResponse", ApiSpec.spec())
|
||||
assert_schema(emoji, "CustomEmoji", ApiSpec.spec())
|
||||
end
|
||||
|
||||
test "CustomEmoji example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = CustomEmoji.schema()
|
||||
assert_schema(schema.example, "CustomEmoji", api_spec)
|
||||
end
|
||||
|
||||
test "CustomEmojisResponse example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = CustomEmojisResponse.schema()
|
||||
assert_schema(schema.example, "CustomEmojisResponse", api_spec)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,11 +6,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
|||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ApiSpec
|
||||
alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse
|
||||
|
||||
import Pleroma.Factory
|
||||
import OpenApiSpex.TestAssertions
|
||||
|
||||
test "blocking / unblocking a domain" do
|
||||
%{user: user, conn: conn} = oauth_access(["write:blocks"])
|
||||
|
|
@ -21,7 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
|||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
|
||||
|
||||
assert %{} = json_response(ret_conn, 200)
|
||||
assert %{} == json_response_and_validate_schema(ret_conn, 200)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
assert User.blocks?(user, other_user)
|
||||
|
||||
|
|
@ -30,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
|||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
|
||||
|
||||
assert %{} = json_response(ret_conn, 200)
|
||||
assert %{} == json_response_and_validate_schema(ret_conn, 200)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
refute User.blocks?(user, other_user)
|
||||
end
|
||||
|
|
@ -41,21 +38,10 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
|||
{:ok, user} = User.block_domain(user, "bad.site")
|
||||
{:ok, user} = User.block_domain(user, "even.worse.site")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/domain_blocks")
|
||||
|
||||
domain_blocks = json_response(conn, 200)
|
||||
|
||||
assert "bad.site" in domain_blocks
|
||||
assert "even.worse.site" in domain_blocks
|
||||
assert_schema(domain_blocks, "DomainBlocksResponse", ApiSpec.spec())
|
||||
end
|
||||
|
||||
test "DomainBlocksResponse example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = DomainBlocksResponse.schema()
|
||||
assert_schema(schema.example, "DomainBlocksResponse", api_spec)
|
||||
assert ["even.worse.site", "bad.site"] ==
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/domain_blocks")
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue