diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a7e9d467..fa8c1ccc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,8 +13,8 @@ jobs: timeout-minutes: 30 name: Internal Typespecs env: - OTP: "25.3" - ELIXIR: "1.13" + OTP: "26.2" + ELIXIR: "1.14" steps: - uses: actions/checkout@v2 - uses: erlef/setup-beam@v1 @@ -50,8 +50,8 @@ jobs: runs-on: ubuntu-20.04 name: Code Style env: - OTP: "25.3" - ELIXIR: "1.13" + OTP: "26.2" + ELIXIR: "1.14" steps: - uses: actions/checkout@v2 - uses: erlef/setup-beam@v1 @@ -78,8 +78,8 @@ jobs: runs-on: ubuntu-20.04 name: Code Formatting env: - OTP: "25.3" - ELIXIR: "1.13" + OTP: "26.2" + ELIXIR: "1.14" steps: - uses: actions/checkout@v2 - uses: erlef/setup-beam@v1 @@ -107,8 +107,8 @@ jobs: timeout-minutes: 30 name: External Typespecs env: - OTP: "25.3" - ELIXIR: "1.13" + OTP: "26.2" + ELIXIR: "1.14" steps: - uses: actions/checkout@v2 - uses: erlef/setup-beam@v1 @@ -151,11 +151,9 @@ jobs: # NOTE: We are going to support 4 version from the official list of 5 # https://hexdocs.pm/elixir/compatibility-and-deprecations.html matrix: - otp: ["25.3"] - elixir: ["1.13.4"] + otp: ["26.2"] + elixir: ["1.14.5"] include: - - otp: "26.2" - elixir: "1.14.5" - otp: "26.2" elixir: "1.15.8" - otp: "26.2" diff --git a/config/config.exs b/config/config.exs index 5712b6af..b42bc942 100644 --- a/config/config.exs +++ b/config/config.exs @@ -9,4 +9,4 @@ config :avrora, convert_null_values: true, names_cache_ttl: :infinity -config :logger, :console, format: "$time $metadata[$level] $levelpad$message\n" +config :logger, :console, format: "$time $metadata[$level] $message\n" diff --git a/mix.exs b/mix.exs index ea7dc7b7..2394fba4 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule Avrora.MixProject do [ app: :avrora, version: @version, - elixir: "~> 1.12", + elixir: "~> 1.14", description: description(), package: package(), docs: docs(), diff --git a/test/avrora/storage/file_test.exs b/test/avrora/storage/file_test.exs index 4f25a39f..2fbf0b61 100644 --- a/test/avrora/storage/file_test.exs +++ b/test/avrora/storage/file_test.exs @@ -2,6 +2,7 @@ defmodule Avrora.Storage.FileTest do use ExUnit.Case, async: true doctest Avrora.Storage.File + import Mox import Support.Config import ExUnit.CaptureLog alias Avrora.Storage.File @@ -45,6 +46,16 @@ defmodule Avrora.Storage.FileTest do assert output =~ "schema file with version is not allowed" end + test "when references reuse same ets table" do + _ = start_link_supervised!(Support.AvroSchemaStore) + stub(Avrora.ConfigMock, :ets_lib, fn -> Support.AvroSchemaStore end) + + existing_ets_tables = Support.AvroSchemaStore.count() + {:ok, _} = File.get("io.acme.PaymentHistory") + + assert Support.AvroSchemaStore.count() - existing_ets_tables == 1 + end + test "when schema file contains invalid json" do assert File.get("io.acme.Wrong") == {:error, "argument error"} end diff --git a/test/avrora/storage/registry_test.exs b/test/avrora/storage/registry_test.exs index 982882e6..09e6c6ae 100644 --- a/test/avrora/storage/registry_test.exs +++ b/test/avrora/storage/registry_test.exs @@ -1,7 +1,5 @@ defmodule Avrora.Storage.RegistryTest do - # NOTE: Remove when Elixir 1.6 support ends - use ExUnit.Case - # use ExUnit.Case, async: true + use ExUnit.Case, async: true doctest Avrora.Storage.Registry import Mox @@ -9,9 +7,6 @@ defmodule Avrora.Storage.RegistryTest do import ExUnit.CaptureLog alias Avrora.Storage.Registry - # NOTE: Remove when Elixir 1.6 support ends - setup :set_mox_from_context - setup :verify_on_exit! setup :support_config @@ -283,6 +278,51 @@ defmodule Avrora.Storage.RegistryTest do assert Registry.get("anything") == {:error, :unconfigured_registry_url} end + + @tag skip: "This test will fail because Registry creates new table on each reference" + test "when references reuse same ets table" do + _ = start_link_supervised!(Support.AvroSchemaStore) + stub(Avrora.ConfigMock, :ets_lib, fn -> Support.AvroSchemaStore end) + + existing_ets_tables = Support.AvroSchemaStore.count() + + Avrora.HTTPClientMock + |> expect(:get, fn url, _ -> + assert url == "http://reg.loc/schemas/ids/43" + + { + :ok, + %{ + "subject" => "io.acme.Account", + "id" => 43, + "version" => 1, + "schema" => json_schema_with_reference(), + "references" => [ + %{"name" => "io.acme.User", "subject" => "io.acme.User", "version" => 1} + ] + } + } + end) + + Avrora.HTTPClientMock + |> expect(:get, fn url, _ -> + assert url == "http://reg.loc/subjects/io.acme.User/versions/1" + + { + :ok, + %{ + "subject" => "io.acme.User", + "id" => 44, + "version" => 1, + "schema" => json_schema_referenced() + } + } + end) + + {:ok, _} = Registry.get(43) + + assert Support.AvroSchemaStore.count() - existing_ets_tables == 1 + end end describe "put/2" do diff --git a/test/support/avro_schema_store.ex b/test/support/avro_schema_store.ex new file mode 100644 index 00000000..b2036266 --- /dev/null +++ b/test/support/avro_schema_store.ex @@ -0,0 +1,71 @@ +defmodule Support.AvroSchemaStore do + @moduledoc """ + A host process and a wrapper for :avro_schema_store produced tables. + Used only in tests + + See more: `Avrora.AvroSchemaStore` + + ## Examples: + + test "when we need test schema store creation" do + {:ok, _} = start_link_supervised!(Support.AvroSchemaStore) + stub(Avrora.ConfigMock, :ets_lib, fn -> Support.AvroSchemaStore end) + + assert Support.AvroSchemaStore.count() == 0 + + Support.AvroSchemaStore.new() + assert Support.AvroSchemaStore.count() == 1 + end + """ + + @prefix "avrora__test" + + use GenServer + + def start_link(opts \\ []) do + {name_opts, _} = Keyword.split(opts, [:name]) + opts = Keyword.merge([name: __MODULE__], name_opts) + + GenServer.start_link(__MODULE__, [], opts) + end + + @impl true + def init(_state \\ []), do: {:ok, []} + + @impl true + def handle_call({:new}, _from, state), + do: {:reply, :avro_schema_store.new(name: String.to_atom("#{@prefix}-#{rand()}")), state} + + def handle_call({:count}, _from, state), + do: {:reply, Enum.count(:ets.all(), &is_test_store/1), state} + + @doc """ + Creates a new Erlang Term Store. + + ## Examples: + + iex> {:ok, _} = Support.AvroSchemaStore.start_link() + iex> Support.AvroSchemaStore.new() |> :ets.info() |> Keyword.get(:size) + 0 + """ + @spec new() :: reference() + def new, do: GenServer.call(__MODULE__, {:new}) + + @doc """ + Returns a number of `:ets` tables matching the test prefix. + + ## Examples: + + iex> {:ok, _} = Support.AvroSchemaStore.start_link() + iex> Support.AvroSchemaStore.count() + 0 + iex> Support.AvroSchemaStore.new() + iex> Support.AvroSchemaStore.count() + 1 + """ + @spec count() :: non_neg_integer() + def count, do: GenServer.call(__MODULE__, {:count}) + + defp rand, do: :rand.uniform(1_000_000) + System.os_time() + defp is_test_store(id), do: !is_reference(id) && Atom.to_string(id) =~ @prefix +end