open-api-spex / open_api_spex

Open API Specifications for Elixir Plug applications
Mozilla Public License 2.0
706 stars 183 forks source link

Caught off-guard by assert_schema, am I doing something wrong? #556

Closed thbar closed 1 year ago

thbar commented 1 year ago

Somewhat similar in spirit to:

For the following type of definition:

  defmodule AOMResponse do
    @moduledoc false
    require OpenApiSpex

    OpenApiSpex.schema(%{
      title: "AOM",
      description: "AOM object",
      type: :object,
      properties: %{
        siren: %Schema{type: :string, nullable: true},
        name: %Schema{type: :string}
      }
    })
  end

I was surprised to see that something like this does not raise:

    import OpenApiSpex.TestAssertions

    # (json contains our response)
    assert json == %{
             "departement" => nil,
             "forme_juridique" => nil,
             "insee_commune_principale" => "38185",
             "nom" => "Grenoble",
             "siren" => nil
           }

    # this is what I would expect to raise
    assert_schema(json, "AOM", TransportWeb.API.Spec.spec())

name is a mandatory key and the response does not contain it. Also the JSON has extraneous keys etc.

Am I doing something incorrect here? Thanks!

thbar commented 1 year ago

Another hint: if the JSON contains siren key, and I set siren with nullable: false in the definition (or remove that flag completely, to get the default behaviour), then assert_schema will warn me with Value does not conform to schema AOM: null value where string expected at /siren.

But extraneous or missing properties seem to not result into errors (I am trying to use assert_schema to detect out-of-sync specifications).

thbar commented 1 year ago

I wrote a reproduction using test_assertions_test.exs in this repo:

    test "my stuff" do
      schema = %Schema{
        type: :object,
        properties: %{
          hello: %Schema{type: :string, nullable: false}
        }
      }

      api_spec = ApiSpec.empty_spec()
      api_spec = put_in(api_spec.components.schemas, %{schema.title => schema})

      # does not raise! How can we get an error here, since "incorrect" is not in the schema?
      TestAssertions.assert_response_schema(%{incorrect: "key"}, schema.title, api_spec)

      # also, "hello" is not nullable above, how can we get an error to warn us that the key also missing?
    end
Cowa commented 1 year ago

You have to use required and additionalProperties:

OpenApiSpex.schema(%{
  title: "AOM",
  description: "AOM object",
  type: :object,
  required: [
    :name
  ],
  properties: %{
    siren: %Schema{type: :string, nullable: true},
    name: %Schema{type: :string}
  },
  additionalProperties: false
})
thbar commented 1 year ago

Great, thank you! I will try that.

thbar commented 1 year ago

I seem to believe that:

More information at:

I could prepare a PR to improve the readme about this, @zorbash what do you think?

zorbash commented 1 year ago

I believe the source of confusion in this case lies in the OpenAPI spec itself and not this library so I wouldn't add it to the README. We can however start a cheatsheet / FAQ listing common gotchas, leveraging this lovely ex_doc feature.