defmodule Blunt.Testing.AggregateCase do
use ExUnit.CaseTemplate
alias Blunt.Testing.AggregateCase
alias Blunt.{AggregateRoot, Behaviour}
using aggregate: aggregate do
quote do
import Blunt.Testing.AggregateCase, only: :macros
@aggregate Behaviour.validate!(unquote(aggregate), AggregateRoot)
end
end
defstruct [:events, :error, :state]
defmacro execute_command(initial_events \\ [], command) do
quote do
AggregateCase.execute(@aggregate, unquote(initial_events), unquote(command))
end
end
@doc false
def execute(aggregate_module, initial_events, command) do
initial_state = struct(aggregate_module)
state = evolve(aggregate_module, initial_state, initial_events)
case aggregate_module.execute(state, command) do
{:error, _reason} = error ->
%__MODULE__{events: [], error: error, state: state}
events ->
final_state = evolve(aggregate_module, state, events)
%__MODULE__{events: List.wrap(events), error: nil, state: final_state}
end
end
@doc false
def evolve(aggregate_module, state, events) do
events
|> List.wrap()
|> Enum.reduce(state, &aggregate_module.apply(&2, &1))
end
end