Current section

Files

Jump to
commanded test support aggregate_case.ex
Raw

test/support/aggregate_case.ex

defmodule Commanded.AggregateCase do
@moduledoc """
This module defines the test case to be used by aggregate tests.
"""
use ExUnit.CaseTemplate
alias Commanded.Aggregate.Multi
using opts do
quote do
@aggregate Keyword.fetch!(unquote(opts), :aggregate)
# Assert that the expected events are returned when the given commands have been executed
defp assert_events(commands, expected_events) do
assert_events([], commands, expected_events)
end
defp assert_events(initial_events, commands, expected_events) do
assert {:ok, _state, events} = aggregate_run(initial_events, commands)
actual_events = List.wrap(events)
expected_events = List.wrap(expected_events)
assert actual_events == expected_events
end
# Assert that the aggregate will have the expected_state after the given commands have been executed
defp assert_state(commands, expected_state) do
assert_state([], commands, expected_state)
end
defp assert_state(initial_events, commands, expected_state) do
assert {:ok, state, events} = aggregate_run(initial_events, commands)
assert state == expected_state
end
defp assert_error(commands, expected_error) do
assert_error([], commands, expected_error)
end
defp assert_error(initial_events, commands, expected_error) do
assert ^expected_error = aggregate_run(initial_events, commands)
end
# Apply the given commands to the aggregate hydrated with the given initial_events
defp aggregate_run(initial_events, commands) do
@aggregate
|> struct()
|> evolve(initial_events)
|> execute(commands)
end
# Execute one or more commands against an aggregate.
defp execute(state, commands) do
try do
{state, events} =
commands
|> List.wrap()
|> Enum.reduce({state, []}, fn command, {state, events} ->
case @aggregate.execute(state, command) do
{:error, _error} = error ->
throw(error)
%Multi{} = multi ->
case Multi.run(multi) do
{:error, _reason} = error ->
throw(error)
{state, new_events} ->
{state, events ++ new_events}
end
none when none in [:ok, nil, []] ->
{state, events}
{:ok, new_events} ->
{evolve(state, new_events), events ++ List.wrap(new_events)}
new_events when is_list(new_events) ->
{evolve(state, new_events), events ++ new_events}
new_event when is_map(new_event) ->
{evolve(state, new_event), events ++ [new_event]}
invalid ->
flunk("unexpected: " <> inspect(invalid))
end
end)
{:ok, state, events}
catch
{:error, _error} = reply -> reply
end
end
# Apply the given events to the aggregate state
defp evolve(state, events) do
events
|> List.wrap()
|> Enum.reduce(state, &@aggregate.apply(&2, &1))
end
end
end
end