Packages
commanded
0.16.0-rc.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/commands/correlation_causation_test.exs
defmodule Commanded.Commands.CorrelationCasuationTest do
use Commanded.StorageCase
import Commanded.Assertions.EventAssertions
alias Commanded.Commands.OpenAccountBonusHandler
alias Commanded.EventStore
alias Commanded.ExampleDomain.{
BankRouter,
TransferMoneyProcessManager,
}
alias Commanded.ExampleDomain.BankAccount.Commands.{
OpenAccount,
WithdrawMoney,
}
alias Commanded.ExampleDomain.BankAccount.Events.{
MoneyDeposited,
}
alias Commanded.ExampleDomain.MoneyTransfer.Commands.TransferMoney
alias Commanded.Helpers.{CommandAuditMiddleware,ProcessHelper}
setup do
CommandAuditMiddleware.start_link()
CommandAuditMiddleware.reset()
{:ok, process_manager} = TransferMoneyProcessManager.start_link()
on_exit fn ->
ProcessHelper.shutdown(process_manager)
end
end
describe "`causation_id`" do
test "should be `nil` when not provided" do
open_account = %OpenAccount{account_number: "ACC123", initial_balance: 500}
:ok = BankRouter.dispatch(open_account, causation_id: nil)
assert [nil] = CommandAuditMiddleware.dispatched_commands(&(&1.causation_id))
end
test "should be copied to dispatched command" do
causation_id = UUID.uuid4()
open_account = %OpenAccount{account_number: "ACC123", initial_balance: 500}
:ok = BankRouter.dispatch(open_account, causation_id: causation_id)
# a command's `causation_id` is set by the value passed to command dispatch
assert [^causation_id] = CommandAuditMiddleware.dispatched_commands(&(&1.causation_id))
end
test "should be set from `command_uuid` on created event" do
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500})
[command_uuid] = CommandAuditMiddleware.dispatched_commands(&(&1.command_uuid))
[event] = EventStore.stream_forward("ACC123") |> Enum.to_list()
# an event's `causation_id` is the dispatched command's `command_uuid`
assert event.causation_id == command_uuid
end
test "should be copied onto commands/events by process manager" do
transfer_uuid = UUID.uuid4()
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500})
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC456", initial_balance: 100})
CommandAuditMiddleware.reset()
:ok = BankRouter.dispatch(%TransferMoney{transfer_uuid: transfer_uuid, debit_account: "ACC123", credit_account: "ACC456", amount: 100})
assert_receive_event MoneyDeposited, fn event -> assert event.transfer_uuid == transfer_uuid end
# withdraw money command's `causation_id` should be money transfer requested event's id
transfer_requested = EventStore.stream_forward(transfer_uuid) |> Enum.at(0)
[_, causation_id, _] = CommandAuditMiddleware.dispatched_commands(&(&1.causation_id))
assert causation_id == transfer_requested.event_id
end
end
describe "`correlation_id`" do
test "should default to a generated UUID when not provided" do
open_account = %OpenAccount{account_number: "ACC123", initial_balance: 500}
:ok = BankRouter.dispatch(open_account, correlation_id: nil)
assert [correlation_id] = CommandAuditMiddleware.dispatched_commands(&(&1.correlation_id))
refute is_nil(correlation_id)
assert correlation_id |> UUID.string_to_binary!() |> is_binary()
end
test "should be copied to dispatched command" do
correlation_id = UUID.uuid4()
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500}, correlation_id: correlation_id)
assert [^correlation_id] = CommandAuditMiddleware.dispatched_commands(&(&1.correlation_id))
end
test "should be set on all created events" do
correlation_id = UUID.uuid4()
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500}, correlation_id: correlation_id)
:ok = BankRouter.dispatch(%WithdrawMoney{account_number: "ACC123", amount: 1_000}, correlation_id: correlation_id)
events = EventStore.stream_forward("ACC123") |> Enum.to_list()
assert length(events) == 3
for event <- events do
assert event.correlation_id == correlation_id
end
end
test "should be copied onto commands/events by process manager" do
correlation_id = UUID.uuid4()
transfer_uuid = UUID.uuid4()
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500})
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC456", initial_balance: 100})
CommandAuditMiddleware.reset()
:ok = BankRouter.dispatch(%TransferMoney{transfer_uuid: transfer_uuid, debit_account: "ACC123", credit_account: "ACC456", amount: 100}, correlation_id: correlation_id)
assert_receive_event MoneyDeposited, fn event -> assert event.transfer_uuid == transfer_uuid end
# `correlation_id` should be the same for all commands & events related to money transfer
assert [correlation_id, correlation_id, correlation_id] =
CommandAuditMiddleware.dispatched_commands(&(&1.correlation_id))
event = EventStore.stream_forward(transfer_uuid) |> Enum.at(0)
assert event.correlation_id == correlation_id
end
end
describe "event handler dispatch command" do
setup [:start_account_bonus_handler]
test "should copy `correlation_id` from handled event" do
correlation_id = UUID.uuid4()
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500}, correlation_id: correlation_id)
assert_receive_event MoneyDeposited, fn event ->
assert event.account_number == "ACC123"
assert event.amount == 100
end
money_deposited = EventStore.stream_forward("ACC123") |> Enum.to_list() |> Enum.at(-1)
assert money_deposited.correlation_id == correlation_id
end
test "should copy `causation_id` from handled event" do
:ok = BankRouter.dispatch(%OpenAccount{account_number: "ACC123", initial_balance: 500})
assert_receive_event MoneyDeposited, fn event ->
assert event.account_number == "ACC123"
assert event.amount == 100
end
[account_opened, money_deposited] = EventStore.stream_forward("ACC123") |> Enum.to_list()
# should set command causation id as handled event id
[_causation_id, causation_id] = CommandAuditMiddleware.dispatched_commands(&(&1.causation_id))
assert causation_id == account_opened.event_id
# should set created event causation id as the command uuid
[_command_uuid, command_uuid] = CommandAuditMiddleware.dispatched_commands(&(&1.command_uuid))
assert money_deposited.causation_id == command_uuid
end
def start_account_bonus_handler(_context) do
{:ok, handler} = OpenAccountBonusHandler.start_link()
on_exit fn ->
ProcessHelper.shutdown(handler)
end
end
end
end