From edc024b2491a474e5f28a097be8c51a25db19714 Mon Sep 17 00:00:00 2001 From: kyleVsteger Date: Thu, 31 Oct 2024 10:01:14 -0400 Subject: [PATCH 1/5] map.update! -> map.update --- lib/channel_spec/schema.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/channel_spec/schema.ex b/lib/channel_spec/schema.ex index da523a5..f9f3816 100644 --- a/lib/channel_spec/schema.ex +++ b/lib/channel_spec/schema.ex @@ -111,7 +111,7 @@ defmodule ChannelSpec.Schema do refs -> schema = ref.schema() refs = Map.put(refs, ref, schema) - refs = Map.update!(refs, :__unresolved_refs, &(&1 -- [ref])) + refs = Map.update(refs, :__unresolved_refs, [], &(&1 -- [ref])) {schema, refs} = compile_refs(schema, refs, []) From d2d6e0fbca416ab681f86dd01e3fbcae61e96eca Mon Sep 17 00:00:00 2001 From: kyleVsteger Date: Thu, 31 Oct 2024 14:36:37 -0400 Subject: [PATCH 2/5] add test that exercises new code --- mix.exs | 7 ++- test/channel_spec/socket_test.exs | 77 +++++++++++++++++++++++++++++++ test/support/test_schema.ex | 76 ++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 test/support/test_schema.ex diff --git a/mix.exs b/mix.exs index 54d56a8..d9f8f7f 100644 --- a/mix.exs +++ b/mix.exs @@ -11,7 +11,8 @@ defmodule ChannelSpec.MixProject do elixir: "~> 1.13", deps: deps(), docs: docs(), - package: package() + package: package(), + elixirc_paths: elixirc_paths(Mix.env()) ] end @@ -51,4 +52,8 @@ defmodule ChannelSpec.MixProject do formatters: ["html"] ] end + + # Specifies which paths to compile per environment. + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] end diff --git a/test/channel_spec/socket_test.exs b/test/channel_spec/socket_test.exs index 22ea4f4..8ec92da 100644 --- a/test/channel_spec/socket_test.exs +++ b/test/channel_spec/socket_test.exs @@ -595,5 +595,82 @@ defmodule ChannelSpec.SocketTest do refute File.exists?(Path.join(tmp_dir, "schema.json")) end + + @tag :tmp_dir + test "schemas nested in a single module with references work", %{ + mod: mod, + tmp_dir: tmp_dir + } do + defmodule LotsOfRefsSchema.MyChannel do + use ChannelHandler.Router + use ChannelSpec.Operations + + operation "foo", payload: %{"$ref": LotsOfRefsSchema.Base} + handle "foo", fn _params, _context, socket -> {:noreply, socket} end + + subscription "sub", %{type: :integer} + end + + defmodule LotsOfRefsSchema do + use ChannelSpec.Socket, schema_path: Path.join(tmp_dir, "schema.json") + + channel "foo", __MODULE__.MyChannel, schema_file: Path.join(tmp_dir, "schema.json") + end + + saved_json = Path.join(tmp_dir, "schema.json") |> File.read!() |> Jason.decode!() + + auto_assert %{ + "channels" => %{ + "foo" => %{ + "messages" => %{ + "foo" => %{"payload" => %{"$ref" => "#/definitions/Base"}} + }, + "subscriptions" => %{"sub" => %{"type" => "integer"}} + } + }, + "definitions" => %{ + "Bar" => %{ + "properties" => %{"baz" => %{"$ref" => "#/definitions/Baz"}}, + "type" => "object" + }, + "Base" => %{ + "additionalProperties" => false, + "properties" => %{ + "bar" => %{ + "items" => [%{"$ref" => "#/definitions/Bar"}], + "type" => "array" + }, + "flim" => %{ + "items" => [%{"$ref" => "#/definitions/Flim"}], + "type" => "array" + }, + "foo" => %{"$ref" => "#/definitions/Foo"} + }, + "type" => "object" + }, + "Baz" => %{"oneOf" => [%{"type" => "string"}, %{"type" => "null"}]}, + "Flam" => %{ + "additionalProperties" => false, + "properties" => %{ + "whatever" => %{ + "items" => [%{"items" => [%{"type" => "string"}], "type" => "array"}], + "type" => "array" + } + }, + "type" => "object" + }, + "Flim" => %{ + "additionalProperties" => false, + "properties" => %{ + "flam" => %{ + "oneOf" => [%{"type" => "null"}, %{"$ref" => "#/definitions/Flam"}] + } + }, + "type" => "object" + }, + "Foo" => %{"oneOf" => [%{"type" => "null"}, %{"type" => "string"}]} + } + } <- saved_json + end end end diff --git a/test/support/test_schema.ex b/test/support/test_schema.ex new file mode 100644 index 0000000..995be38 --- /dev/null +++ b/test/support/test_schema.ex @@ -0,0 +1,76 @@ +defmodule ChannelSpec.SocketTest.LotsOfRefsSchema do + @moduledoc """ + This specific setup causes `__unresolved_refs` to get into a state that makes the schema + fail to compile. The PR in which this file was introduced switches from `Map.update!` to + `Map.update` with a default and generates a valid schema. + """ + + defmodule Base do + @behaviour ChannelSpec.Schema + + def schema() do + %{ + type: :object, + properties: %{ + foo: %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Foo}, + bar: %{type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Bar}]}, + flim: %{ type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Flim}] } + }, + additionalProperties: false + } + end + end + + defmodule Flim do + @behaviour ChannelSpec.Schema + + def schema() do + %{ + type: :object, + properties: %{ + flam: %{ + oneOf: [ + %{type: :null}, + %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Flam} + ] + } + }, + additionalProperties: false + } + end + end + + defmodule Foo do + def schema() do + %{ oneOf: [ %{type: :null}, %{type: :string} ] } + end + end + + defmodule Bar do + def schema() do + %{type: :object, properties: %{baz: %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Baz}}} + end + end + + defmodule Baz do + def schema() do + %{oneOf: [%{type: :string}, %{type: :null}]} + end + end +end + +defmodule ChannelSpec.SocketTest.LotsOfRefsSchema.Flam do + def schema() do + %{ + type: :object, + properties: %{ + whatever: %{ + type: :array, + items: [ %{ type: :array, items: [%{type: :string}] } + ] + } + }, + additionalProperties: false + } + end +end From 4ead28b18aaef60229a927eb1fc6ac7488d02fa0 Mon Sep 17 00:00:00 2001 From: kyleVsteger Date: Thu, 31 Oct 2024 15:33:49 -0400 Subject: [PATCH 3/5] format --- test/support/test_schema.ex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/support/test_schema.ex b/test/support/test_schema.ex index 995be38..93dd90b 100644 --- a/test/support/test_schema.ex +++ b/test/support/test_schema.ex @@ -14,7 +14,7 @@ defmodule ChannelSpec.SocketTest.LotsOfRefsSchema do properties: %{ foo: %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Foo}, bar: %{type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Bar}]}, - flim: %{ type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Flim}] } + flim: %{type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Flim}]} }, additionalProperties: false } @@ -42,7 +42,7 @@ defmodule ChannelSpec.SocketTest.LotsOfRefsSchema do defmodule Foo do def schema() do - %{ oneOf: [ %{type: :null}, %{type: :string} ] } + %{oneOf: [%{type: :null}, %{type: :string}]} end end @@ -66,8 +66,7 @@ defmodule ChannelSpec.SocketTest.LotsOfRefsSchema.Flam do properties: %{ whatever: %{ type: :array, - items: [ %{ type: :array, items: [%{type: :string}] } - ] + items: [%{type: :array, items: [%{type: :string}]}] } }, additionalProperties: false From 6e16044c5bf9d96dc7e17ddb3b18f644a71a66e3 Mon Sep 17 00:00:00 2001 From: kyleVsteger Date: Thu, 31 Oct 2024 15:54:09 -0400 Subject: [PATCH 4/5] remove helper module --- mix.exs | 7 +-- test/channel_spec/socket_test.exs | 84 +++++++++++++++++++++++++++++-- test/support/test_schema.ex | 75 --------------------------- 3 files changed, 82 insertions(+), 84 deletions(-) delete mode 100644 test/support/test_schema.ex diff --git a/mix.exs b/mix.exs index d9f8f7f..54d56a8 100644 --- a/mix.exs +++ b/mix.exs @@ -11,8 +11,7 @@ defmodule ChannelSpec.MixProject do elixir: "~> 1.13", deps: deps(), docs: docs(), - package: package(), - elixirc_paths: elixirc_paths(Mix.env()) + package: package() ] end @@ -52,8 +51,4 @@ defmodule ChannelSpec.MixProject do formatters: ["html"] ] end - - # Specifies which paths to compile per environment. - defp elixirc_paths(:test), do: ["lib", "test/support"] - defp elixirc_paths(_), do: ["lib"] end diff --git a/test/channel_spec/socket_test.exs b/test/channel_spec/socket_test.exs index 8ec92da..cf7c84a 100644 --- a/test/channel_spec/socket_test.exs +++ b/test/channel_spec/socket_test.exs @@ -601,17 +601,95 @@ defmodule ChannelSpec.SocketTest do mod: mod, tmp_dir: tmp_dir } do - defmodule LotsOfRefsSchema.MyChannel do + defmodule :"#{mod}.LotsOfRefsSchema" do + @moduledoc """ + This specific setup causes `__unresolved_refs` to get into a state that makes the schema + fail to compile. The PR in which this file was introduced switches from `Map.update!` to + `Map.update` with a default and generates a valid schema. + """ + + defmodule Base do + @mod_base Module.split(__MODULE__) |> Enum.drop(-1) |> Module.concat() + + def schema() do + %{ + type: :object, + properties: %{ + foo: %{"$ref": :"#{@mod_base}.Foo"}, + bar: %{type: :array, items: [%{"$ref": :"#{@mod_base}.Bar"}]}, + flim: %{type: :array, items: [%{"$ref": :"#{@mod_base}.Flim"}]} + }, + additionalProperties: false + } + end + end + + defmodule Flim do + @mod_base Module.split(__MODULE__) |> Enum.drop(-1) |> Module.concat() + + def schema() do + %{ + type: :object, + properties: %{ + flam: %{ + oneOf: [ + %{type: :null}, + %{"$ref": :"#{@mod_base}.Flam"} + ] + } + }, + additionalProperties: false + } + end + end + + defmodule Foo do + def schema() do + %{oneOf: [%{type: :null}, %{type: :string}]} + end + end + + defmodule Bar do + @mod_base Module.split(__MODULE__) |> Enum.drop(-1) |> Module.concat() + + def schema() do + %{type: :object, properties: %{baz: %{"$ref": :"#{@mod_base}.Baz"}}} + end + end + + defmodule Baz do + def schema() do + %{oneOf: [%{type: :string}, %{type: :null}]} + end + end + end + + defmodule :"#{mod}.LotsOfRefsSchema.Flam" do + def schema() do + %{ + type: :object, + properties: %{ + whatever: %{ + type: :array, + items: [%{type: :array, items: [%{type: :string}]}] + } + }, + additionalProperties: false + } + end + end + + defmodule :"#{mod}.MyChannel" do use ChannelHandler.Router use ChannelSpec.Operations - operation "foo", payload: %{"$ref": LotsOfRefsSchema.Base} + operation "foo", payload: %{"$ref": :"#{mod}.LotsOfRefsSchema.Base"} handle "foo", fn _params, _context, socket -> {:noreply, socket} end subscription "sub", %{type: :integer} end - defmodule LotsOfRefsSchema do + defmodule :"#{mod}" do use ChannelSpec.Socket, schema_path: Path.join(tmp_dir, "schema.json") channel "foo", __MODULE__.MyChannel, schema_file: Path.join(tmp_dir, "schema.json") diff --git a/test/support/test_schema.ex b/test/support/test_schema.ex deleted file mode 100644 index 93dd90b..0000000 --- a/test/support/test_schema.ex +++ /dev/null @@ -1,75 +0,0 @@ -defmodule ChannelSpec.SocketTest.LotsOfRefsSchema do - @moduledoc """ - This specific setup causes `__unresolved_refs` to get into a state that makes the schema - fail to compile. The PR in which this file was introduced switches from `Map.update!` to - `Map.update` with a default and generates a valid schema. - """ - - defmodule Base do - @behaviour ChannelSpec.Schema - - def schema() do - %{ - type: :object, - properties: %{ - foo: %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Foo}, - bar: %{type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Bar}]}, - flim: %{type: :array, items: [%{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Flim}]} - }, - additionalProperties: false - } - end - end - - defmodule Flim do - @behaviour ChannelSpec.Schema - - def schema() do - %{ - type: :object, - properties: %{ - flam: %{ - oneOf: [ - %{type: :null}, - %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Flam} - ] - } - }, - additionalProperties: false - } - end - end - - defmodule Foo do - def schema() do - %{oneOf: [%{type: :null}, %{type: :string}]} - end - end - - defmodule Bar do - def schema() do - %{type: :object, properties: %{baz: %{"$ref": ChannelSpec.SocketTest.LotsOfRefsSchema.Baz}}} - end - end - - defmodule Baz do - def schema() do - %{oneOf: [%{type: :string}, %{type: :null}]} - end - end -end - -defmodule ChannelSpec.SocketTest.LotsOfRefsSchema.Flam do - def schema() do - %{ - type: :object, - properties: %{ - whatever: %{ - type: :array, - items: [%{type: :array, items: [%{type: :string}]}] - } - }, - additionalProperties: false - } - end -end From 3fe90cca4d9180b42955a01797369de5b0cc036e Mon Sep 17 00:00:00 2001 From: kyleVsteger Date: Thu, 31 Oct 2024 16:52:39 -0400 Subject: [PATCH 5/5] make mod_base simpler --- test/channel_spec/socket_test.exs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/channel_spec/socket_test.exs b/test/channel_spec/socket_test.exs index cf7c84a..8d6d77b 100644 --- a/test/channel_spec/socket_test.exs +++ b/test/channel_spec/socket_test.exs @@ -608,8 +608,10 @@ defmodule ChannelSpec.SocketTest do `Map.update` with a default and generates a valid schema. """ + mod_base = __MODULE__ + defmodule Base do - @mod_base Module.split(__MODULE__) |> Enum.drop(-1) |> Module.concat() + @mod_base mod_base def schema() do %{ @@ -625,7 +627,7 @@ defmodule ChannelSpec.SocketTest do end defmodule Flim do - @mod_base Module.split(__MODULE__) |> Enum.drop(-1) |> Module.concat() + @mod_base mod_base def schema() do %{ @@ -650,7 +652,7 @@ defmodule ChannelSpec.SocketTest do end defmodule Bar do - @mod_base Module.split(__MODULE__) |> Enum.drop(-1) |> Module.concat() + @mod_base mod_base def schema() do %{type: :object, properties: %{baz: %{"$ref": :"#{@mod_base}.Baz"}}}