Packages
commanded
0.15.0
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.0
1.3.1
1.3.0
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc.1
1.0.0-rc.0
0.19.1
0.19.0
0.18.1
0.18.0
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.16.0-rc.1
0.16.0-rc.0
0.15.1
0.15.0
0.14.0
0.14.0-rc.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.5
0.8.4
0.8.3
0.8.1
0.8.0
0.7.1
0.6.2
0.6.1
0.6.0
0.4.0
0.3.1
0.3.0
0.2.1
0.2.0
0.1.0
Use Commanded to build your own Elixir applications following the CQRS/ES pattern.
Current section
Files
Jump to
Current section
Files
test/aggregates/execute_command_test.exs
defmodule Commanded.Aggregates.ExecuteCommandTest do
use Commanded.StorageCase
alias Commanded.Aggregates.{Aggregate,ExecutionContext}
alias Commanded.EventStore
alias Commanded.ExampleDomain.{BankAccount,OpenAccountHandler,DepositMoneyHandler}
alias Commanded.ExampleDomain.BankAccount.Commands.{OpenAccount,DepositMoney}
alias Commanded.ExampleDomain.BankAccount.Events.BankAccountOpened
alias Commanded.Helpers.{ProcessHelper,Wait}
alias Commanded.Registration
test "execute command against an aggregate" do
account_number = UUID.uuid4
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
command = %OpenAccount{account_number: account_number, initial_balance: 1_000}
context = %ExecutionContext{command: command, handler: BankAccount, function: :open_account}
{:ok, 1, events} = Aggregate.execute(BankAccount, account_number, context)
assert events == [%BankAccountOpened{account_number: account_number, initial_balance: 1000}]
ProcessHelper.shutdown_aggregate(BankAccount, account_number)
# reload aggregate to fetch persisted events from event store and rebuild state by applying saved events
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
assert Aggregate.aggregate_version(BankAccount, account_number) == 1
assert Aggregate.aggregate_state(BankAccount, account_number) == %BankAccount{account_number: account_number, balance: 1_000, state: :active}
end
test "execute command via a command handler" do
account_number = UUID.uuid4
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
command = %OpenAccount{account_number: account_number, initial_balance: 1_000}
context = %ExecutionContext{command: command, handler: OpenAccountHandler, function: :handle}
{:ok, 1, events} = Aggregate.execute(BankAccount, account_number, context)
assert events == [%BankAccountOpened{account_number: account_number, initial_balance: 1000}]
ProcessHelper.shutdown_aggregate(BankAccount, account_number)
# reload aggregate to fetch persisted events from event store and rebuild state by applying saved events
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
assert Aggregate.aggregate_version(BankAccount, account_number) == 1
assert Aggregate.aggregate_state(BankAccount, account_number) == %BankAccount{account_number: account_number, balance: 1_000, state: :active}
end
test "aggregate raising an exception should not persist pending events or state" do
account_number = UUID.uuid4
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
command = %OpenAccount{account_number: account_number, initial_balance: 1_000}
context = %ExecutionContext{command: command, handler: OpenAccountHandler, function: :handle}
{:ok, 1, _events} = Aggregate.execute(BankAccount, account_number, context)
state_before = Aggregate.aggregate_state(BankAccount, account_number)
assert_aggregate_exit(BankAccount, account_number, fn ->
command = %OpenAccount{account_number: account_number, initial_balance: 1}
context = %ExecutionContext{command: command, handler: OpenAccountHandler, function: :handle}
Aggregate.execute(BankAccount, account_number, context)
end)
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
assert state_before == Aggregate.aggregate_state(BankAccount, account_number)
end
test "executing a command against an aggregate with concurrency error should terminate aggregate process" do
account_number = UUID.uuid4
{:ok, ^account_number} = Commanded.Aggregates.Supervisor.open_aggregate(BankAccount, account_number)
# block until aggregate has loaded its initial (empty) state
Aggregate.aggregate_state(BankAccount, account_number)
# write an event to the aggregate's stream, bypassing the aggregate process (simulate concurrency error)
{:ok, _} = EventStore.append_to_stream(account_number, 0, [
%Commanded.EventStore.EventData{
event_type: "Elixir.Commanded.ExampleDomain.BankAccount.Events.BankAccountOpened",
data: %BankAccountOpened{account_number: account_number, initial_balance: 1_000}
}
])
assert_aggregate_exit(BankAccount, account_number, fn ->
command = %DepositMoney{account_number: account_number, transfer_uuid: UUID.uuid4, amount: 50}
context = %ExecutionContext{command: command, handler: DepositMoneyHandler, function: :handle}
Aggregate.execute(BankAccount, account_number, context)
end)
end
def assert_aggregate_exit(aggregate_module, aggregate_uuid, fun) do
pid = spawn(fun)
# wait for spawned function to terminate
ref = Process.monitor(pid)
assert_receive {:DOWN, ^ref, _, _, _}
# wait for aggregate process to terminate
Wait.until(fn ->
assert Registration.whereis_name({aggregate_module, aggregate_uuid}) == :undefined
end)
end
end