diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9df5250..e1792080 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,20 +12,15 @@ jobs: name: Build and test runs-on: ubuntu-latest strategy: + fail-fast: false matrix: include: - - elixir: 1.17.x + - elixir: 1.20.x + otp: 29 + - elixir: 1.19.x + otp: 28 + - elixir: 1.18.x otp: 27 - - elixir: 1.16.x - otp: 26 - - elixir: 1.15.x - otp: 26 - - elixir: 1.14.x - otp: 26 - check_formatted: "ignore" - - elixir: 1.13.x - otp: 25 - check_formatted: "ignore" services: postgres: @@ -61,7 +56,6 @@ jobs: run: mix deps.get - name: Check formatting - if: ${{ matrix.check_formatted != 'ignore' }} run: mix format --check-formatted - name: Setup EventStore test databases @@ -69,6 +63,7 @@ jobs: MIX_ENV=test mix event_store.setup MIX_ENV=jsonb mix event_store.setup MIX_ENV=text_ids mix event_store.setup + MIX_ENV=migration mix event_store.setup - name: Compile run: mix compile --warnings-as-errors diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index 1a5e6c89..00000000 --- a/.tool-versions +++ /dev/null @@ -1,2 +0,0 @@ -elixir 1.16.0-otp-26 -erlang 26.2.1 \ No newline at end of file diff --git a/config/bench.exs b/config/bench.exs index f5ce46da..b787dbfd 100644 --- a/config/bench.exs +++ b/config/bench.exs @@ -1,7 +1,7 @@ import Config # no logging for benchmarking -config :logger, backends: [] +config :logger, :default_handler, false config :ex_unit, assert_receive_timeout: 2_000, diff --git a/config/jsonb.exs b/config/jsonb.exs index 6b7bf9b3..f9f558ef 100644 --- a/config/jsonb.exs +++ b/config/jsonb.exs @@ -1,6 +1,6 @@ import Config -config :logger, backends: [] +config :logger, :default_handler, false config :ex_unit, capture_log: true, diff --git a/config/migration.exs b/config/migration.exs index c433d139..a9ac443a 100644 --- a/config/migration.exs +++ b/config/migration.exs @@ -1,6 +1,6 @@ import Config -config :logger, backends: [] +config :logger, :default_handler, false config :ex_unit, capture_log: true, diff --git a/config/test.exs b/config/test.exs index 5fe9f4b9..4cd36720 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,6 +1,6 @@ import Config -config :logger, backends: [] +config :logger, :default_handler, false config :ex_unit, capture_log: [level: :warning], diff --git a/config/text_ids.exs b/config/text_ids.exs index 21aaef93..56e14bb9 100644 --- a/config/text_ids.exs +++ b/config/text_ids.exs @@ -1,6 +1,6 @@ import Config -config :logger, backends: [] +config :logger, :default_handler, false config :ex_unit, capture_log: true, diff --git a/lib/event_store/fsm.ex b/lib/event_store/fsm.ex new file mode 100644 index 00000000..26f82d39 --- /dev/null +++ b/lib/event_store/fsm.ex @@ -0,0 +1,175 @@ +defmodule EventStore.Fsm do + @moduledoc false + + # Vendored from the `fsm` package (v0.3.1, MIT, Copyright (c) 2013 Saša Jurić) + # because upstream is unmaintained and its generated code does not compile + # cleanly under the Elixir type checker. + + defmacro __using__(opts) do + quote do + import EventStore.Fsm + + defstruct state: unquote(opts[:initial_state]), + data: unquote(opts[:initial_data]) + + @declaring_state nil + @declared_events MapSet.new() + + def new(params \\ []), do: struct!(__MODULE__, params) + + def state(%__MODULE__{state: state}), do: state + def data(%__MODULE__{data: data}), do: data + + defp change_state(%__MODULE__{} = fsm, {:action_responses, responses}), + do: parse_action_responses(fsm, responses) + + defp parse_action_responses(%__MODULE__{} = fsm, responses) do + Enum.reduce(responses, fsm, fn response, fsm -> + handle_action_response(fsm, response) + end) + end + + defp handle_action_response(%__MODULE__{} = fsm, {:next_state, next_state}) do + %__MODULE__{fsm | state: next_state} + end + + defp handle_action_response(%__MODULE__{} = fsm, {:new_data, new_data}) do + %__MODULE__{fsm | data: new_data} + end + + defp handle_action_response(%__MODULE__{} = fsm, {:respond, response}) do + {response, fsm} + end + end + end + + def next_state(state), do: {:action_responses, [next_state: state]} + def next_state(state, data), do: {:action_responses, [next_state: state, new_data: data]} + + def respond(response), do: {:action_responses, [respond: response]} + def respond(response, state), do: {:action_responses, [next_state: state, respond: response]} + + def respond(response, state, data), + do: {:action_responses, [next_state: state, new_data: data, respond: response]} + + defmacro defstate(state, state_def) do + quote do + state_name = + case unquote(Macro.escape(state, unquote: true)) do + name when is_atom(name) -> name + {name, _, _} -> name + end + + @declaring_state state_name + unquote(state_def) + @declaring_state nil + end + end + + defmacro defevent(event, opts) do + do_defevent(event, opts, opts[:do]) + end + + defmacro defevent(event, opts, do: event_def) do + do_defevent(event, opts, event_def) + end + + defmacro defeventp(event, opts) do + do_defevent(event, [{:private, true} | opts], opts[:do]) + end + + defmacro defeventp(event, opts, do: event_def) do + do_defevent(event, [{:private, true} | opts], event_def) + end + + defp do_defevent(event_decl, opts, event_def) do + quote do + unquote(extract_args(event_decl, opts, event_def)) + unquote(define_interface()) + unquote(implement_transition()) + end + end + + defp extract_args(event_decl, opts, event_def) do + quote do + {event_name, args} = + case unquote(Macro.escape(event_decl, unquote: true)) do + :_ -> {:_, []} + name when is_atom(name) -> {name, []} + {name, _, args} -> {name, args || []} + end + + private = unquote(opts[:private]) + state_arg = unquote(Macro.escape(opts[:state] || quote(do: _), unquote: true)) + data_arg = unquote(Macro.escape(opts[:data] || quote(do: _), unquote: true)) + event_arg = unquote(Macro.escape(opts[:event] || quote(do: _), unquote: true)) + args_arg = unquote(Macro.escape(opts[:args] || quote(do: _), unquote: true)) + event_def = unquote(Macro.escape(event_def, unquote: true)) + guard = unquote(Macro.escape(opts[:when])) + end + end + + defp define_interface do + quote bind_quoted: [] do + unless event_name == :_ or MapSet.member?(@declared_events, {event_name, length(args)}) do + interface_args = + Enum.reduce(args, {0, []}, fn _, {index, args} -> + { + index + 1, + [{:"arg#{index}", [], nil} | args] + } + end) + |> elem(1) + |> Enum.reverse() + + body = + quote do + transition(fsm, unquote(event_name), [unquote_splicing(interface_args)]) + end + + interface_args = [quote(do: fsm) | interface_args] + + if private do + defp unquote(event_name)(unquote_splicing(interface_args)), do: unquote(body) + else + def unquote(event_name)(unquote_splicing(interface_args)), do: unquote(body) + end + + @declared_events MapSet.put(@declared_events, {event_name, length(args)}) + end + end + end + + defp implement_transition do + quote bind_quoted: [] do + transition_args = [ + if @declaring_state do + quote do + %__MODULE__{ + state: unquote(@declaring_state) = unquote(state_arg), + data: unquote(data_arg) + } = fsm + end + else + quote do + %__MODULE__{state: unquote(state_arg), data: unquote(data_arg)} = fsm + end + end, + quote do + unquote(if event_name == :_, do: quote(do: _), else: event_name) = unquote(event_arg) + end, + quote do + unquote(if event_name == :_, do: quote(do: _), else: args) = unquote(args_arg) + end + ] + + body = quote(do: change_state(fsm, unquote(event_def))) + + if guard do + def transition(unquote_splicing(transition_args)) when unquote(guard), do: unquote(body) + else + def transition(unquote_splicing(transition_args)), do: unquote(body) + end + end + end +end diff --git a/lib/event_store/sql/statements/insert_events.sql.eex b/lib/event_store/sql/statements/insert_events.sql.eex index fa830f62..2348b5c9 100644 --- a/lib/event_store/sql/statements/insert_events.sql.eex +++ b/lib/event_store/sql/statements/insert_events.sql.eex @@ -1,4 +1,4 @@ -<% +<%!-- # Elixir template variables: # schema - string # stream_id - integer @@ -18,15 +18,15 @@ # 9 - created_at - timestamp # 10 - index - integer # 11 - stream_version - integer -%> +--%> WITH - <% + <%!-- # create a table variable with: # event_id - uuid - the id for the new event # index - integer - the increase in the stream version for any stream it is linked to # stream_version - integer - the final stream version after all of the events have been inserted - %> + --%> new_events_indexes (event_id, index, stream_version) AS ( VALUES <%= for i <- 0..(number_of_events - 1) do %> @@ -35,11 +35,11 @@ WITH <% end %> ), events AS ( - <% + <%!-- # insert the new events into the events table # using the 7 bind variables from 3 to 9 inclusive # n.b.: the bind for the event_id is re-generated here - %> + --%> INSERT INTO "<%= schema %>".events ( event_id, @@ -57,7 +57,7 @@ WITH <% end %> ), stream AS ( - <% # Increase the version to the stream version given %> + <%!-- Increase the version to the stream version given --%> <%= cond do %> <% stream_id -> %> UPDATE "<%= schema %>".streams @@ -65,10 +65,10 @@ WITH WHERE stream_id = $1::bigint returning stream_id <% created_at -> %> - <% + <%!-- # the event created_at date has been provided as the last bind variable # use that instead of generating one - %> + --%> INSERT INTO "<%= schema %>".streams (stream_uuid, stream_version, created_at) VALUES ($1, $2::bigint, $<%= number_of_events*9 + 3 %>) returning stream_id @@ -79,14 +79,14 @@ WITH <% end %> ), source_stream_events AS ( - <% + <%!-- # link the new events into it's source stream # we're using the passed in event_ids rather than reading/joining from tables # each insert uses the stream_version calculated for the corresponding event # we're joining here, so we'll get the product of: # the stream (1) # the rows in the table variable (number_of_events) - %> + --%> INSERT INTO "<%= schema %>".stream_events ( event_id, @@ -104,21 +104,21 @@ WITH FROM new_events_indexes, stream ), linked_stream AS ( - <% + <%!-- # Update the all streams version by the number of events # This is the value of the expected version at append time + the number of events # Returns the version before the update - %> + --%> UPDATE "<%= schema %>".streams SET stream_version = stream_version + $2::bigint WHERE stream_id = 0 RETURNING stream_version - $2::bigint as initial_stream_version ), linked_stream_events AS ( - <% + <%!-- # Link the new events into the $all stream # 1 row for each event - %> + --%> INSERT INTO "<%= schema %>".stream_events ( event_id, diff --git a/lib/event_store/storage/snapshot.ex b/lib/event_store/storage/snapshot.ex index 75cae185..d2b21adc 100644 --- a/lib/event_store/storage/snapshot.ex +++ b/lib/event_store/storage/snapshot.ex @@ -73,7 +73,14 @@ defmodule EventStore.Storage.Snapshot do end end - defp to_snapshot_from_row([source_uuid, source_version, source_type, data, metadata, created_at]) do + defp to_snapshot_from_row([ + source_uuid, + source_version, + source_type, + data, + metadata, + created_at + ]) do %SnapshotData{ source_uuid: source_uuid, source_version: source_version, diff --git a/lib/event_store/subscriptions/subscription_fsm.ex b/lib/event_store/subscriptions/subscription_fsm.ex index 492d5289..42bbe443 100644 --- a/lib/event_store/subscriptions/subscription_fsm.ex +++ b/lib/event_store/subscriptions/subscription_fsm.ex @@ -5,7 +5,7 @@ defmodule EventStore.Subscriptions.SubscriptionFsm do alias EventStore.Streams.Stream alias EventStore.Subscriptions.{SubscriptionState, Subscriber} - use Fsm, initial_state: :initial, initial_data: %SubscriptionState{} + use EventStore.Fsm, initial_state: :initial, initial_data: %SubscriptionState{} require Logger @@ -536,7 +536,11 @@ defmodule EventStore.Subscriptions.SubscriptionFsm do end end - defp notify_partition_subscriber(data, partition_key, events_to_send \\ []) do + defp notify_partition_subscriber( + %SubscriptionState{} = data, + partition_key, + events_to_send \\ [] + ) do %SubscriptionState{ partitions: partitions, subscribers: subscribers, diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000..b668a7bf --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +erlang = "29.0.6" +elixir = "1.20.4-otp-29" diff --git a/mix.exs b/mix.exs index 82493696..ab14c91a 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule EventStore.Mixfile do [ app: :eventstore, version: @version, - elixir: "~> 1.11", + elixir: "~> 1.18", elixirc_paths: elixirc_paths(Mix.env()), deps: deps(), description: description(), @@ -18,7 +18,6 @@ defmodule EventStore.Mixfile do start_permanent: Mix.env() == :prod, consolidate_protocols: Mix.env() == :prod, aliases: aliases(), - preferred_cli_env: preferred_cli_env(), dialyzer: dialyzer(), name: "EventStore", source_url: @source_url @@ -39,9 +38,8 @@ defmodule EventStore.Mixfile do defp deps do [ - {:fsm, "~> 0.3"}, {:gen_stage, "~> 1.2"}, - {:postgrex, "~> 0.17"}, + {:postgrex, "~> 0.22"}, {:telemetry, "~> 1.0"}, # Optional dependencies @@ -147,12 +145,14 @@ defmodule EventStore.Mixfile do ] end - defp preferred_cli_env do + def cli do [ - "test.all": :test, - "test.jsonb": :test, - "test.text_ids": :test, - "test.migration": :test + preferred_envs: [ + "test.all": :test, + "test.jsonb": :test, + "test.text_ids": :test, + "test.migration": :test + ] ] end diff --git a/mix.lock b/mix.lock index 210c2141..85f2ddcb 100644 --- a/mix.lock +++ b/mix.lock @@ -1,20 +1,19 @@ %{ "benchfella": {:hex, :benchfella, "0.3.5", "b2122c234117b3f91ed7b43b6e915e19e1ab216971154acd0a80ce0e9b8c05f5", [:mix], [], "hexpm", "23f27cbc482cbac03fc8926441eb60a5e111759c17642bac005c3225f5eb809d"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, - "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, - "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, - "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, - "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, - "fsm": {:hex, :fsm, "0.3.1", "087aa9b02779a84320dc7a2d8464452b5308e29877921b2bde81cdba32a12390", [:mix], [], "hexpm", "fbf0d53f89e9082b326b0b5828b94b4c549ff9d1452bbfd00b4d1ac082208e96"}, - "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, - "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, - "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "db_connection": {:hex, :db_connection, "2.10.2", "ae391e803a5adff104da913c2fc1c0c14a37f8b10001dcef568796e1fb7bf95c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "510b14482330f1af6490a2fa0efd8d4f1435d1529b165647df22ac0f2df0fa93"}, + "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"}, + "dialyxir": {:hex, :dialyxir, "1.4.8", "7ef671a8aff9948b091d8c30f09467fbb16e77305cda451bce48109a0f5e021c", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cbd5a851571e5dfeb32aaf2e840bfa98b7864cb3071bf2ef5d95d1276b12e072"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.46", "67607a0532e810c6f630a515c548d0b24949643f168cc556303bee4cf96105c7", [:mix], [], "hexpm", "9c44636e8a1c68c62f526b2dcd85d941dbbcee7ab82cf64ba06ce28bef8e89f5"}, + "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, + "ex_doc": {:hex, :ex_doc, "0.40.4", "66f2e42bf588594d5a8aab31cad87f2ddad09d0da1b1a2f379340ec2c2e497cb", [:mix], [{:earmark_parser, "~> 1.4.46", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "6222b9e423d76584ee34df2c82a5ed72c2d53dc153f7f483ad28b378694186cc"}, + "gen_stage": {:hex, :gen_stage, "1.3.2", "7c77e5d1e97de2c6c2f78f306f463bca64bf2f4c3cdd606affc0100b89743b7b", [:mix], [], "hexpm", "0ffae547fa777b3ed889a6b9e1e64566217413d018cabd825f786e843ffe63e7"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, + "makeup": {:hex, :makeup, "1.2.2", "882d46dc0905e9ff7abf2aab61a7e6b3dcc555533977d8a23b06019e6c89ac94", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "9a1a24e5b343b8ae16abea0822c10a6f75da27af7fa802ada5251f7579bfccfa"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"}, - "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "postgrex": {:hex, :postgrex, "0.22.4", "d271f595dfd25230b6398354e19d17bb5e2d20130fd2d9bdca7e15f125d43552", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "4aae45a2d60e35b04eea2602440be152fae332901f1fc7a60fc7cb7f0f9a9c5a"}, + "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, } diff --git a/test/event_store_test.exs b/test/event_store_test.exs index 630c0775..c2a6e138 100644 --- a/test/event_store_test.exs +++ b/test/event_store_test.exs @@ -167,7 +167,7 @@ defmodule EventStore.EventStoreTest do {:ok, recorded_events} = EventStore.read_stream_backward(stream_uuid) - assert_recorded_events(stream_uuid, 10..1, Enum.reverse(events), recorded_events) + assert_recorded_events(stream_uuid, 10..1//-1, Enum.reverse(events), recorded_events) end test "stream backward", %{stream_uuid: stream_uuid, events: events} do @@ -176,7 +176,7 @@ defmodule EventStore.EventStoreTest do recorded_events = EventStore.stream_backward(stream_uuid, -1, batch_size: 5) |> Enum.to_list() - assert_recorded_events(stream_uuid, 10..1, Enum.reverse(events), recorded_events) + assert_recorded_events(stream_uuid, 10..1//-1, Enum.reverse(events), recorded_events) end test "stream all backward", %{stream_uuid: stream_uuid, events: events} do @@ -184,7 +184,7 @@ defmodule EventStore.EventStoreTest do recorded_events = EventStore.stream_all_backward(-1, batch_size: 5) |> Enum.to_list() - assert_recorded_events(stream_uuid, 10..1, Enum.reverse(events), recorded_events) + assert_recorded_events(stream_uuid, 10..1//-1, Enum.reverse(events), recorded_events) end end @@ -485,7 +485,7 @@ defmodule EventStore.EventStoreTest do end test "record snapshot" do - assert record_snapshot() != nil + assert %SnapshotData{} = record_snapshot() end test "read a snapshot" do diff --git a/test/notifications/notifications_supervisor_test.exs b/test/notifications/notifications_supervisor_test.exs index 8877d3eb..0f54f8c3 100644 --- a/test/notifications/notifications_supervisor_test.exs +++ b/test/notifications/notifications_supervisor_test.exs @@ -47,8 +47,13 @@ defmodule EventStore.Notifications.NotificationsSupervisorTest do end end + # OTP 28 reports the `gen_server` hibernation loop where earlier releases + # reported `:erlang.hibernate/3`. + @hibernated_functions [{:erlang, :hibernate, 3}, {:gen_server, :loop_hibernate, 4}] + defp assert_hibernated(pid) do - assert Process.info(pid, :current_function) == {:current_function, {:erlang, :hibernate, 3}} + assert {:current_function, current_function} = Process.info(pid, :current_function) + assert current_function in @hibernated_functions end defp append_events(stream_uuid, count, expected_version \\ 0) do diff --git a/test/subscriptions/subscribe_to_stream_test.exs b/test/subscriptions/subscribe_to_stream_test.exs index 01d8c7b8..3938c05d 100644 --- a/test/subscriptions/subscribe_to_stream_test.exs +++ b/test/subscriptions/subscribe_to_stream_test.exs @@ -804,8 +804,13 @@ defmodule EventStore.Subscriptions.SubscribeToStreamTest do end end + # OTP 28 reports the `gen_server` hibernation loop where earlier releases + # reported `:erlang.hibernate/3`. + @hibernated_functions [{:erlang, :hibernate, 3}, {:gen_server, :loop_hibernate, 4}] + defp assert_hibernated(pid) do - assert Process.info(pid, :current_function) == {:current_function, {:erlang, :hibernate, 3}} + assert {:current_function, current_function} = Process.info(pid, :current_function) + assert current_function in @hibernated_functions end # Append events to another stream so that for single stream subscription tests