Repository: commanded/commanded (elixir)

Issue:
Include stacktrace in event handler and process manager `error` callback functions
To assist with debugging exceptions the stacktrace should be included in the failure context struct passed to the event handler and process manager error callback functions. This would allow the user to log the error and the source stacktrace.

Pull request:
Include stacktrace in event handler and process manager `error` callback functions
- Include `__STACKTRACE__` in event handler failure context.
- Include `__STACKTRACE__` in process manager failure context.
- Log aggregate and process manager exceptions and stacktrace.
- Extend aggregate lifespan to support `{:stop, reason}`.

Closes #303.

Patch:
diff --git a/guides/Commands.md b/guides/Commands.md
index c5ec102..2748eb4 100644
--- a/guides/Commands.md
+++ b/guides/Commands.md
@@ -411,7 +411,7 @@ end
 
 The inactivity timeout is specified in milliseconds, after which time the aggregate process will be stopped if no other messages are received.
 
-Return `:stop` to immediately shutdown the aggregate process. Return `:infinity` to prevent the aggregate instance from shutting down.
+Return `:stop` or `{:stop, reason}` to immediately shutdown the aggregate process. Return `:infinity` to prevent the aggregate instance from shutting down.
 
 You can also return `:hibernate` and the process is hibernated, it will continue its loop once a message is in its message queue. Hibernating an aggregate causes garbage collection and minimises the memory used by the process. Hibernating should not be used aggressively as too much time could be spent garbage collecting.
 
diff --git a/lib/commanded/aggregates/aggregate.ex b/lib/commanded/aggregates/aggregate.ex
index 6059633..ac266fc 100644
--- a/lib/commanded/aggregates/aggregate.ex
+++ b/lib/commanded/aggregates/aggregate.ex
@@ -212,8 +212,8 @@ defmodule Commanded.Aggregates.Aggregate do
       end
 
     case lifespan_timeout do
-      :stop ->
-        {:stop, :normal, state}
+      {:stop, reason} ->
+        {:stop, reason, state}
 
       lifespan_timeout ->
         {:noreply, state, lifespan_timeout}
@@ -251,7 +251,7 @@ defmodule Commanded.Aggregates.Aggregate do
       {:reply, reply, state}
     else
       case lifespan_timeout do
-        :stop -> {:stop, :normal, reply, state}
+        {:stop, reason} -> {:stop, reason, reply, state}
         lifespan_timeout -> {:reply, reply, state, lifespan_timeout}
       end
     end
@@ -391,43 +391,34 @@ defmodule Commanded.Aggregates.Aggregate do
     end
   end
 
-  # Rebuild aggregate state from a `Stream` of its events
+  # Rebuild aggregate state from a `Stream` of its events.
   defp rebuild_from_event_stream(event_stream, %Aggregate{} = state) do
-    %Aggregate{aggregate_module: aggregate_module} = state
-
-    event_stream
-    |> Stream.map(fn event ->
-      {event.data, event.stream_version}
-    end)
-    |> Enum.reduce_while(state, fn {event, stream_version}, state ->
-      case event do
-        nil ->
-          {:halt, state}
+    Enum.reduce(event_stream, state, fn event, state ->
+      %RecordedEvent{data: data, stream_version: stream_version} = event
+      %Aggregate{aggregate_module: aggregate_module, aggregate_state: aggregate_state} = state
 
-        event ->
-          state = %Aggregate{
-            state
-            | aggregate_version: stream_version,
-              aggregate_state: aggregate_module.apply(state.aggregate_state, event)
-          }
-
-          {:cont, state}
-      end
+      %Aggregate{
+        state
+        | aggregate_version: stream_version,
+          aggregate_state: aggregate_module.apply(aggregate_state, data)
+      }
     end)
-    |> case do
-      nil -> state
-      state -> state
-    end
   end
 
-  defp aggregate_lifespan_timeout(lifespan, timeout_function_name, args) do
-    # Take the last event or the command/error
+  defp aggregate_lifespan_timeout(lifespan, function_name, args) do
+    # Take the last event or the command or error
     args = args |> List.wrap() |> Enum.take(-1)
 
-    case apply(lifespan, timeout_function_name, args) do
-      timeout when timeout in [:infinity, :hibernate, :stop] ->
+    case apply(lifespan, function_name, args) do
+      timeout when timeout in [:infinity, :hibernate] ->
         timeout
 
+      :stop ->
+        {:stop, :normal}
+
+      {:stop, _reason} = reply ->
+        reply
+
       timeout when is_integer(timeout) and timeout >= 0 ->
         timeout
 
@@ -435,7 +426,7 @@ defmodule Commanded.Aggregates.Aggregate do
         Logger.warn(fn ->
           "Invalid timeout for aggregate lifespan " <>
             inspect(lifespan) <>
-            ", expected a non-negative integer, `:infinity`, `:hibernate`, or `:stop` but got: " <>
+            ", expected a non-negative integer, `:infinity`, `:hibernate`, `:stop`, or `{:stop, reason}` but got: " <>
             inspect(invalid)
         end)
 
@@ -447,7 +438,7 @@ defmodule Commanded.Aggregates.Aggregate do
     %ExecutionContext{command: command, handler: handler, function: function} = context
     %Aggregate{aggregate_state: aggregate_state} = state
 
-    Logger.debug(fn -> describe(state) <> " executing command: #{inspect(command)}" end)
+    Logger.debug(fn -> describe(state) <> " executing command: " <> inspect(command) end)
 
     case Kernel.apply(handler, function, [aggregate_state, command]) do
       {:error, _error} = reply ->
@@ -471,6 +462,11 @@ defmodule Commanded.Aggregates.Aggregate do
       pending_events ->
         apply_and_persist_events(pending_events, context, state)
     end
+  rescue
+    error ->
+      Logger.error(fn -> Exception.format(:error, error, __STACKTRACE__) end)
+
+      {{:error, error}, state}
   end
 
   defp apply_and_persist_events(pending_events, context, %Aggregate{} = state) do
diff --git a/lib/commanded/aggregates/aggregate_lifespan.ex b/lib/commanded/aggregates/aggregate_lifespan.ex
index 45d3cac..5b7d60e 100644
--- a/lib/commanded/aggregates/aggregate_lifespan.ex
+++ b/lib/commanded/aggregates/aggregate_lifespan.ex
@@ -26,7 +26,10 @@ defmodule Commanded.Aggregates.AggregateLifespan do
     - Non-negative integer - specify an inactivity timeout, in millisconds.
     - `:infinity` - prevent the aggregate instance from shutting down.
     - `:hibernate` - send the process into hibernation.
-    - `:stop` - immediately shutdown the aggregate process.
+    - `:stop` - immediately shutdown the aggregate process with a `:normal` exit
+      reason.
+    - `{:stop, reason}` - immediately shutdown the aggregate process with the
+      given reason.
 
   ### Hibernation
 
@@ -68,7 +71,7 @@ defmodule Commanded.Aggregates.AggregateLifespan do
 
   """
 
-  @type lifespan :: timeout | :hibernate | :stop
+  @type lifespan :: timeout | :hibernate | :stop | {:stop, reason :: term()}
 
   @doc """
   Aggregate process will be stopped after specified inactivity timeout unless
diff --git a/lib/commanded/aggregates/default_lifespan.ex b/lib/commanded/aggregates/default_lifespan.ex
index 53ebc5d..6fa6101 100644
--- a/lib/commanded/aggregates/default_lifespan.ex
+++ b/lib/commanded/aggregates/default_lifespan.ex
@@ -4,7 +4,7 @@ defmodule Commanded.Aggregates.DefaultLifespan do
   behaviour.
 
   It will ensure that an aggregate instance process runs indefinitely once
-  started.
+  started, unless an exception is encountered.
   """
 
   @behaviour Commanded.Aggregates.AggregateLifespan
@@ -20,7 +20,14 @@ defmodule Commanded.Aggregates.DefaultLifespan do
   def after_command(_command), do: :infinity
 
   @doc """
-  Aggregate will run indefinitely once started.
+  Aggregate is stopped on exception, but will run indefinitely for any non-
+  exception error.
   """
-  def after_error(_error), do: :infinity
+  def after_error(error) do
+    if Exception.exception?(error) do
+      {:stop, error}
+    else
+      :infinity
+    end
+  end
 end
diff --git a/lib/commanded/aggregates/multi.ex b/lib/commanded/aggregates/multi.ex
index dc0857a..6ed3649 100644
--- a/lib/commanded/aggregates/multi.ex
+++ b/lib/commanded/aggregates/multi.ex
@@ -12,14 +12,10 @@ defmodule Commanded.Aggregate.Multi do
   updated balance is used to check whether the account is overdrawn.
 
       defmodule BankAccount do
-        defstruct [
-          account_number: nil,
-          balance: 0,
-          state: nil,
-        ]
-
         alias Commanded.Aggregate.Multi
 
+        defstruct [:account_number, :state, balance: 0]
+
         def withdraw(
           %BankAccount{state: :active} = account,
           %WithdrawMoney{amount: amount})
diff --git a/lib/commanded/event/failure_context.ex b/lib/commanded/event/failure_context.ex
index 3a7940e..f199d4c 100644
--- a/lib/commanded/event/failure_context.ex
+++ b/lib/commanded/event/failure_context.ex
@@ -1,23 +1,26 @@
 defmodule Commanded.Event.FailureContext do
   @moduledoc """
-  Contains the data related to an event handling failure.
+  Data related to an event handling failure.
 
   The available fields are:
 
-  - `context` - a map that is passed between each failure. Use it to store any
-    transient state between failures. As an example it could be used to count
-    error failures and stop or skip the problematic event after too many.
-  - `metadata` - the metadata associated with the failed event.
+    - `context` - a map that is passed between each failure. Use it to store any
+      transient state between failures. As an example it could be used to count
+      error failures and stop or skip the problematic event after too many.
+    - `metadata` - the metadata associated with the failed event.
+    - `stacktrace` - the stacktrace if the error was an unhandled exception.
 
   """
 
   @type t :: %__MODULE__{
           context: map(),
-          metadata: map()
+          metadata: map(),
+          stacktrace: Exception.stacktrace() | nil
         }
 
   defstruct [
     :context,
-    :metadata
+    :metadata,
+    :stacktrace
   ]
 end
diff --git a/lib/commanded/event/handler.ex b/lib/commanded/event/handler.ex
index 3b68569..95445ea 100644
--- a/lib/commanded/event/handler.ex
+++ b/lib/commanded/event/handler.ex
@@ -634,27 +634,15 @@ defmodule Commanded.Event.Handler do
       {:error, :already_seen_event} ->
         confirm_receipt(event, state)
 
-      {:error, reason} = error ->
-        Logger.error(fn ->
-          describe(state) <>
-            " failed to handle event #{inspect(event, pretty: true)} due to: #{
-              inspect(reason, pretty: true)
-            }"
-        end)
+      {:error, _reason} = error ->
+        failure_context = build_failure_context(event, context)
 
-        handle_event_error(error, event, state, context)
+        handle_event_error(error, event, failure_context, state)
 
       {:error, reason, stacktrace} ->
-        Logger.error(fn ->
-          describe(state) <>
-            " failed to handle event #{inspect(event, pretty: true)} due to: #{
-              inspect(reason, pretty: true)
-            }"
-        end)
+        failure_context = build_failure_context(event, context, stacktrace)
 
-        Logger.error(fn -> Exception.format(:error, reason, stacktrace) end)
-
-        handle_event_error({:error, reason}, event, state, context)
+        handle_event_error({:error, reason}, event, failure_context, state)
     end
   end
 
@@ -667,18 +655,38 @@ defmodule Commanded.Event.Handler do
     try do
       handler_module.handle(data, metadata)
     rescue
-      e -> {:error, e, __STACKTRACE__}
+      error ->
+        stacktrace = __STACKTRACE__
+        Logger.error(fn -> Exception.format(:error, error, stacktrace) end)
+
+        {:error, error, stacktrace}
     end
   end
 
-  defp handle_event_error(error, %RecordedEvent{} = failed_event, %Handler{} = state, context) do
-    %RecordedEvent{data: data} = failed_event
-    %Handler{handler_module: handler_module} = state
-
-    failure_context = %FailureContext{
+  defp build_failure_context(%RecordedEvent{} = failed_event, context, stacktrace \\ nil) do
+    %FailureContext{
       context: context,
+      stacktrace: stacktrace,
       metadata: enrich_metadata(failed_event)
     }
+  end
+
+  defp handle_event_error(
+         {:error, reason} = error,
+         %RecordedEvent{} = failed_event,
+         %FailureContext{} = failure_context,
+         %Handler{} = state
+       ) do
+    %RecordedEvent{data: data} = failed_event
+    %Handler{handler_module: handler_module} = state
+
+    Logger.error(fn ->
+      describe(state) <>
+        " failed to handle event " <>
+        inspect(failed_event, pretty: true) <>
+        " due to: " <>
+        inspect(reason, pretty: true)
+    end)
 
     case handler_module.error(error, data, failure_context) do
       {:retry, context} when is_map(context) ->
diff --git a/lib/commanded/process_managers/failure_context.ex b/lib/commanded/process_managers/failure_context.ex
index 349b093..9060a71 100644
--- a/lib/commanded/process_managers/failure_context.ex
+++ b/lib/commanded/process_managers/failure_context.ex
@@ -1,27 +1,31 @@
 defmodule Commanded.ProcessManagers.FailureContext do
   @moduledoc """
-  Contains the data related to a failure.
+  Data related to a process manager event handling or command dispatch failure.
 
   The available fields are:
 
-  - `pending_commands` - the pending commands that were not executed yet
-  - `process_manager_state` - the state the process manager would be in
-    if the command would not fail.
-  - `last_event` - the last event the process manager received
-  - `context` - the context of failures (to be passed between each failure),
-    used in example to count retries.
+    - `context` - the context map passed between each failure and may be used
+      to track state between retries, such as to count failures.
+    - `last_event` - the last event the process manager received
+    - `pending_commands` - the pending commands that were not executed yet
+    - `process_manager_state` - the state the process manager would be in
+      if the command would not fail.
+    - `stacktrace` - the stacktrace if the error was an unhandled exception.
+
   """
 
   @type t :: %__MODULE__{
+          context: map(),
+          last_event: struct(),
           pending_commands: [struct()],
           process_manager_state: struct(),
-          last_event: struct(),
-          context: map()
+          stacktrace: Exception.stacktrace() | nil
         }
 
   defstruct [
     :process_manager_state,
     :last_event,
+    :stacktrace,
     context: %{},
     pending_commands: []
   ]
diff --git a/lib/commanded/process_managers/process_manager_instance.ex b/lib/commanded/process_managers/process_manager_instance.ex
index dcee0db..7b52250 100644
--- a/lib/commanded/process_managers/process_manager_instance.ex
+++ b/lib/commanded/process_managers/process_manager_instance.ex
@@ -169,7 +169,7 @@ defmodule Commanded.ProcessManagers.ProcessManagerInstance do
     event_number <= last_seen_event
   end
 
-  # Already seen event, so just ack
+  # Already seen event, so just ack.
   defp process_seen_event(%RecordedEvent{} = event, %State{} = state) do
     %State{idle_timeout: idle_timeout} = state
 
@@ -185,13 +185,15 @@ defmodule Commanded.ProcessManagers.ProcessManagerInstance do
     %State{idle_timeout: idle_timeout} = state
 
     case handle_event(event, state) do
-      {:error, error} ->
-        Logger.error(fn ->
-          describe(state) <>
-            " failed to handle event #{inspect(event_number)} due to: #{inspect(error)}"
-        end)
+      {:error, _error} = error ->
+        failure_context = build_failure_context(event, context, nil, state)
+
+        handle_event_error(error, event, failure_context, state)
 
-        handle_event_error({:error, error}, event, state, context)
+      {:error, error, stacktrace} ->
+        failure_context = build_failure_context(event, context, stacktrace, state)
+
+        handle_event_error({:error, error}, event, failure_context, state)
 
       {:stop, _error, _state} = reply ->
         reply
@@ -233,20 +235,40 @@ defmodule Commanded.ProcessManagers.ProcessManagerInstance do
     try do
       process_manager_module.handle(process_state, data)
     rescue
-      e -> {:error, e}
+      error ->
+        stacktrace = __STACKTRACE__
+        Logger.error(fn -> Exception.format(:error, error, stacktrace) end)
+
+        {:error, error, stacktrace}
     end
   end
 
-  defp handle_event_error(error, %RecordedEvent{} = failed_event, %State{} = state, context) do
-    %RecordedEvent{data: data} = failed_event
-    %State{idle_timeout: idle_timeout, process_manager_module: process_manager_module} = state
-
-    failure_context = %FailureContext{
+  defp build_failure_context(failed_event, context, stacktrace, state) do
+    %FailureContext{
+      context: context,
+      last_event: failed_event,
       pending_commands: [],
       process_manager_state: state,
-      last_event: failed_event,
-      context: context
+      stacktrace: stacktrace
     }
+  end
+
+  defp handle_event_error(
+         {:error, reason} = error,
+         %RecordedEvent{} = failed_event,
+         %FailureContext{} = failure_context,
+         %State{} = state
+       ) do
+    %RecordedEvent{data: data} = failed_event
+    %State{idle_timeout: idle_timeout, process_manager_module: process_manager_module} = state
+
+    Logger.error(fn ->
+      describe(state) <>
+        " failed to handle event " <>
+        inspect(failed_event, pretty: true) <>
+        " due to: " <>
+        inspect(reason, pretty: true)
+    end)
 
     case process_manager_module.error(error, data, failure_context) do
       {:retry, context} when is_map(context) ->
@@ -308,17 +330,17 @@ defmodule Commanded.ProcessManagers.ProcessManagerInstance do
     %State{application: application} = state
 
     Logger.debug(fn ->
-      describe(state) <> " attempting to dispatch command: #{inspect(command)}"
+      describe(state) <> " attempting to dispatch command: " <> inspect(command)
     end)
 
     case Application.dispatch(application, command, opts) do
       :ok ->
         dispatch_commands(pending_commands, opts, state, last_event)
 
-      error ->
+      {:error, _error} = error ->
         Logger.warn(fn ->
           describe(state) <>
-            " failed to dispatch command #{inspect(command)} due to: #{inspect(error)}"
+            " failed to dispatch command " <> inspect(command) <> " due to: " <> inspect(error)
         end)
 
         failure_context = %FailureContext{
@@ -328,11 +350,11 @@ defmodule Commanded.ProcessManagers.ProcessManagerInstance do
           context: context
         }
 
-        dispatch_failure(error, command, opts, state, failure_context)
+        dispatch_failure(error, command, opts, failure_context, state)
     end
   end
 
-  defp dispatch_failure(error, failed_command, opts, state, failure_context) do
+  defp dispatch_failure(error, failed_command, opts, failure_context, state) do
     %State{process_manager_module: process_manager_module} = state
     %FailureContext{pending_commands: pending_commands, last_event: last_event} = failure_context
 


Test patch:
diff --git a/test/aggregates/aggregate_lifespan_test.exs b/test/aggregates/aggregate_lifespan_test.exs
index 85ea99d..66b100d 100644
--- a/test/aggregates/aggregate_lifespan_test.exs
+++ b/test/aggregates/aggregate_lifespan_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Aggregates.AggregateLifespanTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Aggregates.{DefaultLifespanRouter, LifespanAggregate, LifespanRouter}
   alias Commanded.Aggregates.LifespanAggregate.{Command, Event}
@@ -39,7 +39,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       assert :ok = DefaultLifespanRouter.dispatch(command, application: DefaultApp)
 
-      refute_receive {:DOWN, ^ref, :process, _, :normal}, 10
+      refute_receive {:DOWN, ^ref, :process, _pid, _reason}
     end
 
     test "should use default `after_command/1` lifespan when none specified'", %{
@@ -50,7 +50,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       assert :ok = DefaultLifespanRouter.dispatch(command, application: DefaultApp)
 
-      refute_receive {:DOWN, ^ref, :process, _, :normal}, 10
+      refute_receive {:DOWN, ^ref, :process, _pid, _reason}
     end
 
     test "should use default `after_error/1` lifespan when none specified'", %{
@@ -62,7 +62,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
       {:error, {:failed, nil, nil}} =
         DefaultLifespanRouter.dispatch(command, application: DefaultApp)
 
-      refute_receive {:DOWN, ^ref, :process, _, :normal}, 10
+      refute_receive {:DOWN, ^ref, :process, _pid, _reason}
     end
 
     test "should call `after_event/1` callback function", %{
@@ -97,7 +97,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       :ok = LifespanRouter.dispatch(command, application: DefaultApp)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should call `after_command/1` callback function when no domain events", %{
@@ -132,7 +132,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       :ok = LifespanRouter.dispatch(command, application: DefaultApp)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should call `after_error/1` callback function on error", %{
@@ -169,7 +169,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
       {:error, {:failed, _reply_to, :stop}} =
         LifespanRouter.dispatch(command, application: DefaultApp)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should not shutdown if another command executed", %{
@@ -190,8 +190,8 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
       assert_receive :after_command
       assert_receive :after_command
 
-      refute_receive {:DOWN, ^ref, :process, _, :normal}, 25
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      refute_receive {:DOWN, ^ref, :process, _pid, _reason}, 25
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should reopen the aggregate to execute command if it is stopped by the previous command",
@@ -210,7 +210,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       Task.yield_many(tasks)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should reopen the aggregate to execute command if it is stopped by an error",
@@ -230,7 +230,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       Task.yield_many(tasks)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should stop process when requested", %{
@@ -241,7 +241,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       :ok = LifespanRouter.dispatch(command, application: DefaultApp)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
     end
 
     test "should adhere to aggregate lifespan after taking snapshot",
@@ -265,7 +265,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
 
       :ok = LifespanRouter.dispatch(command, application: DefaultApp)
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
       assert {:ok, _snapshot} = EventStore.read_snapshot(DefaultApp, aggregate_uuid)
     end
 
@@ -298,7 +298,7 @@ defmodule Commanded.Aggregates.AggregateLifespanTest do
       # Publish events to aggregate after taking snapshot
       send(pid, {:events, events})
 
-      assert_receive {:DOWN, ^ref, :process, _, :normal}
+      assert_receive {:DOWN, ^ref, :process, _pid, :normal}
       assert {:ok, _snapshot} = EventStore.read_snapshot(DefaultApp, aggregate_uuid)
     end
 
diff --git a/test/aggregates/aggregate_subscription_test.exs b/test/aggregates/aggregate_subscription_test.exs
index 80aaf31..75d812d 100644
--- a/test/aggregates/aggregate_subscription_test.exs
+++ b/test/aggregates/aggregate_subscription_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Aggregates.AggregateSubscriptionTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Aggregates.{Aggregate, ExecutionContext}
   alias Commanded.Aggregates.Supervisor, as: AggregateSupervisor
diff --git a/test/aggregates/aggregate_test.exs b/test/aggregates/aggregate_test.exs
index dd9ad70..dcf14e7 100644
--- a/test/aggregates/aggregate_test.exs
+++ b/test/aggregates/aggregate_test.exs
@@ -1,11 +1,22 @@
 defmodule Commanded.Aggregates.AggregateTest do
-  use ExUnit.Case, async: true
+  use ExUnit.Case
+
+  import ExUnit.CaptureLog
 
   alias Commanded.Aggregates.Aggregate
+  alias Commanded.Aggregates.ExecutionContext
   alias Commanded.DefaultApp
 
+  defmodule RaiseException do
+    defstruct [:message]
+  end
+
   defmodule ExampleAggregate do
     defstruct [:uuid]
+
+    def execute(%ExampleAggregate{}, %RaiseException{message: message}) do
+      raise message
+    end
   end
 
   setup do
@@ -19,28 +30,47 @@ defmodule Commanded.Aggregates.AggregateTest do
 
       {:ok, pid} = start_aggregate(aggregate_uuid)
 
-      [aggregate_uuid: aggregate_uuid, pid: pid]
-    end
-
-    test "ignore unexpected messages", %{pid: pid} do
-      import ExUnit.CaptureLog
-
       ref = Process.monitor(pid)
+      Process.unlink(pid)
+
+      [aggregate_uuid: aggregate_uuid, pid: pid, ref: ref]
+    end
 
+    test "ignore unexpected messages", %{pid: pid, ref: ref} do
       send_unexpected_mesage = fn ->
         send(pid, :unexpected_message)
 
-        refute_receive {:DOWN, ^ref, :process, ^pid, _}
+        refute_receive {:DOWN, ^ref, :process, ^pid, _reason}
       end
 
-      assert capture_log(send_unexpected_mesage) =~
-               "received unexpected message in handle_info/2: :unexpected_message"
+      captured = capture_log(send_unexpected_mesage)
+
+      assert captured =~ "received unexpected message in handle_info/2: :unexpected_message"
+    end
+
+    test "log exception", %{pid: pid, ref: ref} do
+      raise_exception = fn ->
+        context = %ExecutionContext{
+          command: %RaiseException{message: "an exception"},
+          function: :execute,
+          handler: ExampleAggregate
+        }
+
+        GenServer.call(pid, {:execute_command, context})
+
+        assert_receive {:DOWN, ^ref, :process, ^pid, %RuntimeError{message: "an exception"}}
+      end
+
+      captured = capture_log(raise_exception)
+
+      assert captured =~ "(RuntimeError) an exception"
+      assert captured =~ "aggregate_test.exs"
+      assert captured =~ "Commanded.Aggregates.AggregateTest.ExampleAggregate.execute/2"
     end
   end
 
   def start_aggregate(aggregate_uuid) do
     Aggregate.start_link([application: DefaultApp],
-      application: DefaultApp,
       aggregate_module: ExampleAggregate,
       aggregate_uuid: aggregate_uuid
     )
diff --git a/test/aggregates/event_persistence_test.exs b/test/aggregates/event_persistence_test.exs
index 0ceac41..c6e473b 100644
--- a/test/aggregates/event_persistence_test.exs
+++ b/test/aggregates/event_persistence_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Aggregates.EventPersistenceTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Enumerable, only: [pluck: 2]
 
diff --git a/test/aggregates/multi_test.exs b/test/aggregates/multi_test.exs
index 0ab69c7..9305541 100644
--- a/test/aggregates/multi_test.exs
+++ b/test/aggregates/multi_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Aggregate.MultiTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Enumerable
 
diff --git a/test/aggregates/snapshotting_test.exs b/test/aggregates/snapshotting_test.exs
index 0f634f9..440ea93 100644
--- a/test/aggregates/snapshotting_test.exs
+++ b/test/aggregates/snapshotting_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Aggregates.SnapshottingTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
 
diff --git a/test/aggregates/supervisor_test.exs b/test/aggregates/supervisor_test.exs
index 16bce52..f80b7cb 100644
--- a/test/aggregates/supervisor_test.exs
+++ b/test/aggregates/supervisor_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Aggregates.SupervisorTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.ExampleDomain.BankAccount
diff --git a/test/commands/command_timeout_test.exs b/test/commands/command_timeout_test.exs
index 3823afa..8e11670 100644
--- a/test/commands/command_timeout_test.exs
+++ b/test/commands/command_timeout_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.CommandTimeoutTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.Commands.{TimeoutRouter, TimeoutCommand}
diff --git a/test/commands/composite_router_test.exs b/test/commands/composite_router_test.exs
index 32098c0..ec10f59 100644
--- a/test/commands/composite_router_test.exs
+++ b/test/commands/composite_router_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.CompositeRouterTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Commands.Composite.CompositeCompositeRouter
   alias Commanded.Commands.Composite.ExampleCompositeRouter
diff --git a/test/commands/correlation_causation_test.exs b/test/commands/correlation_causation_test.exs
index f560d8d..a867b11 100644
--- a/test/commands/correlation_causation_test.exs
+++ b/test/commands/correlation_causation_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.CorrelationCasuationTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Assertions.EventAssertions
 
diff --git a/test/commands/custom_identity_routing_test.exs b/test/commands/custom_identity_routing_test.exs
index 992c608..f39caaa 100644
--- a/test/commands/custom_identity_routing_test.exs
+++ b/test/commands/custom_identity_routing_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.CustomIdentityRoutingTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.EventStore
diff --git a/test/commands/dispatch_command_test.exs b/test/commands/dispatch_command_test.exs
index fb4c726..f233642 100644
--- a/test/commands/dispatch_command_test.exs
+++ b/test/commands/dispatch_command_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.DispatchCommandTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.ExampleDomain.BankApp
   alias Commanded.ExampleDomain.BankRouter
diff --git a/test/commands/dispatch_consistency_test.exs b/test/commands/dispatch_consistency_test.exs
index a04809a..6f81eba 100644
--- a/test/commands/dispatch_consistency_test.exs
+++ b/test/commands/dispatch_consistency_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.DispatchConsistencyTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Commands.{
     ConsistencyAggregateRoot,
diff --git a/test/commands/dispatch_return_test.exs b/test/commands/dispatch_return_test.exs
index d2f8ef7..18ebb26 100644
--- a/test/commands/dispatch_return_test.exs
+++ b/test/commands/dispatch_return_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.DispatchReturnTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Commands.ExecutionResult
   alias Commanded.ExampleDomain.BankApp
diff --git a/test/commands/routing_commands_test.exs b/test/commands/routing_commands_test.exs
index d672c06..3827a13 100644
--- a/test/commands/routing_commands_test.exs
+++ b/test/commands/routing_commands_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Commands.RoutingCommandsTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.Commands.UnregisteredCommand
diff --git a/test/event/default_event_handler_error_handling_test.exs b/test/event/default_event_handler_error_handling_test.exs
index 2c3d0e2..cf17c6a 100644
--- a/test/event/default_event_handler_error_handling_test.exs
+++ b/test/event/default_event_handler_error_handling_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Event.DefaultEventHandlerErrorHandlingTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.Event.DefaultErrorEventHandler
diff --git a/test/event/event_handler_error_handling_test.exs b/test/event/event_handler_error_handling_test.exs
index 4912d4d..7276ebd 100644
--- a/test/event/event_handler_error_handling_test.exs
+++ b/test/event/event_handler_error_handling_test.exs
@@ -1,9 +1,11 @@
 defmodule Commanded.Event.EventHandlerErrorHandlingTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
-  alias Commanded.Event.{ErrorEventHandler, ErrorRouter}
-  alias Commanded.Event.ErrorAggregate.Commands.{RaiseError, RaiseException}
+  alias Commanded.Event.FailureContext
+  alias Commanded.Event.ErrorAggregate.Events.{ErrorEvent, ExceptionEvent}
+  alias Commanded.Event.ErrorEventHandler
+  alias Commanded.Helpers.EventFactory
 
   setup do
     start_supervised!(DefaultApp)
@@ -14,102 +16,133 @@ defmodule Commanded.Event.EventHandlerErrorHandlingTest do
 
     [
       handler: handler,
-      ref: Process.monitor(handler),
-      uuid: UUID.uuid4()
+      ref: Process.monitor(handler)
     ]
   end
 
-  describe "event handler `error/3` callback function" do
-    test "should call on error", %{ref: ref, uuid: uuid} do
-      command = %RaiseError{uuid: uuid, strategy: "default", reply_to: reply_to()}
+  describe "event handling exception handling" do
+    test "should print the stack trace", %{handler: handler, ref: ref} do
+      import ExUnit.CaptureLog
 
-      :ok = ErrorRouter.dispatch(command, application: DefaultApp)
+      send_error_message = fn ->
+        send_exception_event(handler)
 
-      assert_receive {:error, :stopping}
-      assert_receive {:DOWN, ^ref, _, _, :failed}
+        assert_receive {:DOWN, ^ref, :process, ^handler, %RuntimeError{message: "exception"}}
+      end
+
+      captured = capture_log(send_error_message)
+
+      assert captured =~ "(RuntimeError) exception"
+      assert captured =~ "error_event_handler.ex"
+      assert captured =~ "Commanded.Event.ErrorEventHandler.handle/2"
     end
 
-    test "should call on exception", %{ref: ref, uuid: uuid} do
-      command = %RaiseException{uuid: uuid, strategy: "default", reply_to: reply_to()}
+    test "should include the stack trace in failure context", %{handler: handler, ref: ref} do
+      send_exception_event(handler)
 
-      :ok = ErrorRouter.dispatch(command, application: DefaultApp)
+      assert_receive {:exception, :stopping, error, %FailureContext{stacktrace: stacktrace}}
+      assert_receive {:DOWN, ^ref, :process, ^handler, %RuntimeError{message: "exception"}}
 
-      assert_receive {:exception, :stopping}
-      assert_receive {:DOWN, ^ref, _, _, %RuntimeError{message: "exception"}}
+      assert error == %RuntimeError{message: "exception"}
+      refute is_nil(stacktrace)
     end
   end
 
-  test "should stop event handler on error by default", %{handler: handler, ref: ref, uuid: uuid} do
-    command = %RaiseError{uuid: uuid, strategy: "default", reply_to: reply_to()}
+  describe "event handler `error/3` callback function" do
+    test "should call on error", %{handler: handler, ref: ref} do
+      send_error_event(handler)
+
+      assert_receive {:error, :stopping}
+      assert_receive {:DOWN, ^ref, :process, ^handler, :failed}
+    end
 
-    :ok = ErrorRouter.dispatch(command, application: DefaultApp)
+    test "should call on exception", %{handler: handler, ref: ref} do
+      send_exception_event(handler)
 
-    assert_receive {:error, :stopping}
+      assert_receive {:exception, :stopping, _error, _failure_context}
+      assert_receive {:DOWN, ^ref, :process, ^handler, %RuntimeError{message: "exception"}}
+    end
+  end
+
+  test "should stop event handler on error by default", %{handler: handler, ref: ref} do
+    send_error_event(handler)
 
-    assert_receive {:DOWN, ^ref, _, _, :failed}
+    assert_receive {:error, :stopping}
+    assert_receive {:DOWN, ^ref, :process, ^handler, :failed}
     refute Process.alive?(handler)
   end
 
   test "should stop event handler when invalid error response returned", %{
     handler: handler,
-    ref: ref,
-    uuid: uuid
+    ref: ref
   } do
-    command = %RaiseError{uuid: uuid, strategy: "invalid", reply_to: reply_to()}
-
-    :ok = ErrorRouter.dispatch(command, application: DefaultApp)
+    send_error_event(handler, strategy: "invalid")
 
     assert_receive {:error, :invalid}
 
-    assert_receive {:DOWN, ^ref, _, _, :failed}
+    assert_receive {:DOWN, ^ref, :process, ^handler, :failed}
     refute Process.alive?(handler)
   end
 
-  test "should retry event handler on error", %{handler: handler, ref: ref, uuid: uuid} do
-    command = %RaiseError{uuid: uuid, strategy: "retry", reply_to: reply_to()}
-
-    :ok = ErrorRouter.dispatch(command, application: DefaultApp)
+  test "should retry event handler on error", %{handler: handler, ref: ref} do
+    send_error_event(handler, strategy: "retry")
 
     assert_receive {:error, :failed, %{failures: 1}}
     assert_receive {:error, :failed, %{failures: 2}}
     assert_receive {:error, :too_many_failures, %{failures: 3}}
 
-    assert_receive {:DOWN, ^ref, _, _, :too_many_failures}
+    assert_receive {:DOWN, ^ref, :process, ^handler, :too_many_failures}
     refute Process.alive?(handler)
   end
 
   test "should retry event handler after delay on error", %{
     handler: handler,
-    ref: ref,
-    uuid: uuid
+    ref: ref
   } do
-    command = %RaiseError{uuid: uuid, strategy: "retry", delay: 10, reply_to: reply_to()}
-
-    :ok = ErrorRouter.dispatch(command, application: DefaultApp)
+    send_error_event(handler, strategy: "retry", delay: 10)
 
     assert_receive {:error, :failed, %{failures: 1, delay: 10}}
     assert_receive {:error, :failed, %{failures: 2, delay: 10}}
     assert_receive {:error, :too_many_failures, %{failures: 3, delay: 10}}
 
-    assert_receive {:DOWN, ^ref, _, _, :too_many_failures}
+    assert_receive {:DOWN, ^ref, :process, ^handler, :too_many_failures}
     refute Process.alive?(handler)
   end
 
-  test "should skip event on error", %{handler: handler, ref: ref, uuid: uuid} do
-    :ok =
-      ErrorRouter.dispatch(%RaiseError{uuid: uuid, strategy: "skip", reply_to: reply_to()},
-        application: DefaultApp
-      )
+  test "should skip event on error", %{handler: handler, ref: ref} do
+    send_error_event(handler, strategy: "skip")
 
     assert_receive {:error, :skipping}
 
-    # event handler should still be alive
-    refute_receive {:DOWN, ^ref, _, _, :too_many_failures}
+    # Event handler should still be alive
+    refute_receive {:DOWN, ^ref, :process, ^handler, :too_many_failures}
     assert Process.alive?(handler)
 
-    # should ack bad event
+    # Should ack errored event
     assert GenServer.call(handler, :last_seen_event) == 1
   end
 
+  defp send_error_event(handler, opts \\ []) do
+    send_events_to_handler(handler, [
+      %ErrorEvent{
+        reply_to: reply_to(),
+        strategy: Keyword.get(opts, :strategy, "default"),
+        delay: Keyword.get(opts, :delay)
+      }
+    ])
+  end
+
+  defp send_exception_event(handler) do
+    send_events_to_handler(handler, [
+      %ExceptionEvent{reply_to: reply_to()}
+    ])
+  end
+
+  defp send_events_to_handler(handler, events) do
+    recorded_events = EventFactory.map_to_recorded_events(events)
+
+    send(handler, {:events, recorded_events})
+  end
+
   defp reply_to, do: :erlang.pid_to_list(self())
 end
diff --git a/test/event/event_handler_macro_test.exs b/test/event/event_handler_macro_test.exs
index 2deebcf..b9856b9 100644
--- a/test/event/event_handler_macro_test.exs
+++ b/test/event/event_handler_macro_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Event.EventHandlerMacroTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Event.IgnoredEvent
   alias Commanded.ExampleDomain.BankApp
diff --git a/test/event/event_handler_subscribe_to_stream_test.exs b/test/event/event_handler_subscribe_to_stream_test.exs
index 7f9c994..0c09412 100644
--- a/test/event/event_handler_subscribe_to_stream_test.exs
+++ b/test/event/event_handler_subscribe_to_stream_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Event.EventHandlerSubscribeToStreamTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.EventStore
   alias Commanded.DefaultApp
diff --git a/test/event/handle_event_test.exs b/test/event/handle_event_test.exs
index 61e8e6d..f202298 100644
--- a/test/event/handle_event_test.exs
+++ b/test/event/handle_event_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Event.HandleEventTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Enumerable, only: [pluck: 2]
   import Commanded.Assertions.EventAssertions
@@ -10,7 +10,6 @@ defmodule Commanded.Event.HandleEventTest do
   alias Commanded.Event.UninterestingEvent
   alias Commanded.Helpers.EventFactory
   alias Commanded.Helpers.Wait
-  alias Commanded.Event.ErrorEvent
   alias Commanded.ExampleDomain.BankApp
   alias Commanded.ExampleDomain.BankAccount.AccountBalanceHandler
   alias Commanded.ExampleDomain.BankAccount.BankAccountHandler
@@ -78,29 +77,6 @@ defmodule Commanded.Event.HandleEventTest do
       assert capture_log(send_unexpected_mesage) =~
                "Commanded.ExampleDomain.BankAccount.AccountBalanceHandler received unexpected message: :unexpected_message"
     end
-
-    test "should print the stack trace on errors", %{handler: handler} do
-      import ExUnit.CaptureLog
-
-      events = [
-        %ErrorEvent{}
-      ]
-
-      ref = Process.monitor(handler)
-
-      recorded_events = EventFactory.map_to_recorded_events(events)
-
-      send_error_message = fn ->
-        send(handler, {:events, recorded_events})
-        assert_receive {:DOWN, ^ref, :process, ^handler, _}
-      end
-
-      captured = capture_log(send_error_message)
-
-      assert captured =~ "(RuntimeError) ErrorEvent occurred"
-      assert captured =~ "test/example_domain/bank_account/account_balance_handler.ex"
-      assert captured =~ "Commanded.ExampleDomain.BankAccount.AccountBalanceHandler.handle/2"
-    end
   end
 
   describe "reset event handler" do
diff --git a/test/event/handler_config_test.exs b/test/event/handler_config_test.exs
index 7e373a0..5ff8be1 100644
--- a/test/event/handler_config_test.exs
+++ b/test/event/handler_config_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Event.HandlerConfigTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
 
diff --git a/test/event/support/error/error_aggregate.ex b/test/event/support/error/error_aggregate.ex
index bd56558..39cde88 100644
--- a/test/event/support/error/error_aggregate.ex
+++ b/test/event/support/error/error_aggregate.ex
@@ -4,19 +4,24 @@ defmodule Commanded.Event.ErrorAggregate do
   defstruct [:uuid]
 
   defmodule Commands do
-    defmodule(RaiseError, do: defstruct([:uuid, :strategy, :delay, :reply_to]))
-    defmodule(RaiseException, do: defstruct([:uuid, :strategy, :delay, :reply_to]))
+    defmodule RaiseError do
+      defstruct [:uuid, :strategy, :delay, :reply_to]
+    end
+
+    defmodule RaiseException do
+      defstruct [:uuid, :strategy, :delay, :reply_to]
+    end
   end
 
   defmodule Events do
     defmodule ErrorEvent do
       @derive Jason.Encoder
-      defstruct([:uuid, :strategy, :delay, :reply_to])
+      defstruct [:uuid, :strategy, :delay, :reply_to]
     end
 
     defmodule ExceptionEvent do
       @derive Jason.Encoder
-      defstruct([:uuid, :strategy, :delay, :reply_to])
+      defstruct [:uuid, :strategy, :delay, :reply_to]
     end
   end
 
diff --git a/test/event/support/error/error_event_handler.ex b/test/event/support/error/error_event_handler.ex
index 0904220..7b960d7 100644
--- a/test/event/support/error/error_event_handler.ex
+++ b/test/event/support/error/error_event_handler.ex
@@ -66,10 +66,10 @@ defmodule Commanded.Event.ErrorEventHandler do
     :invalid
   end
 
-  def error({:error, error}, %ExceptionEvent{strategy: "default"} = event, _failure_context) do
+  def error({:error, error}, %ExceptionEvent{} = event, failure_context) do
     %ExceptionEvent{reply_to: reply_to} = event
 
-    send_reply(reply_to, {:exception, :stopping})
+    send_reply(reply_to, {:exception, :stopping, error, failure_context})
 
     {:stop, error}
   end
diff --git a/test/event/support/error_event.ex b/test/event/support/error_event.ex
deleted file mode 100644
index d0432a7..0000000
--- a/test/event/support/error_event.ex
+++ /dev/null
@@ -1,4 +0,0 @@
-defmodule Commanded.Event.ErrorEvent do
-  @moduledoc false
-  defstruct field: nil
-end
diff --git a/test/event/upcaster_test.exs b/test/event/upcaster_test.exs
index 75ef346..efe757f 100644
--- a/test/event/upcaster_test.exs
+++ b/test/event/upcaster_test.exs
@@ -1,5 +1,5 @@
 defmodule Event.UpcasterTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Aggregates.Aggregate
   alias Commanded.DefaultApp
diff --git a/test/event_assertions_test.exs b/test/event_assertions_test.exs
index 22fc0eb..3e9200f 100644
--- a/test/event_assertions_test.exs
+++ b/test/event_assertions_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.EventAssertionsTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Assertions.EventAssertions
 
diff --git a/test/example_domain/bank_account/account_balance_handler.ex b/test/example_domain/bank_account/account_balance_handler.ex
index a8ec2c1..58cbf97 100644
--- a/test/example_domain/bank_account/account_balance_handler.ex
+++ b/test/example_domain/bank_account/account_balance_handler.ex
@@ -8,7 +8,6 @@ defmodule Commanded.ExampleDomain.BankAccount.AccountBalanceHandler do
   alias Commanded.ExampleDomain.BankAccount.Events.BankAccountOpened
   alias Commanded.ExampleDomain.BankAccount.Events.MoneyDeposited
   alias Commanded.ExampleDomain.BankAccount.Events.MoneyWithdrawn
-  alias Commanded.Event.ErrorEvent
 
   def init do
     with {:ok, _pid} <- Agent.start_link(fn -> 0 end, name: __MODULE__) do
@@ -28,10 +27,6 @@ defmodule Commanded.ExampleDomain.BankAccount.AccountBalanceHandler do
     Agent.update(__MODULE__, fn _ -> balance end)
   end
 
-  def handle(%ErrorEvent{}, _metadata) do
-    raise "ErrorEvent occurred"
-  end
-
   def subscribed? do
     try do
       Agent.get(__MODULE__, fn _ -> true end)
diff --git a/test/helpers/storage_case.ex b/test/helpers/storage_case.ex
deleted file mode 100644
index 8e991b6..0000000
--- a/test/helpers/storage_case.ex
+++ /dev/null
@@ -1,15 +0,0 @@
-defmodule Commanded.StorageCase do
-  @moduledoc false
-
-  use ExUnit.CaseTemplate
-
-  require Logger
-
-  setup do
-    on_exit(fn ->
-      :ok = Application.stop(:commanded)
-
-      {:ok, _apps} = Application.ensure_all_started(:commanded)
-    end)
-  end
-end
diff --git a/test/middleware/application_middleware_test.exs b/test/middleware/application_middleware_test.exs
index 705fa34..f926af2 100644
--- a/test/middleware/application_middleware_test.exs
+++ b/test/middleware/application_middleware_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Middleware.ApplicationMiddlewareTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Middleware.Tenant.Commands.RegisterTenant
   alias Commanded.Middleware.TenantApp
diff --git a/test/middleware/halting_middleware_test.exs b/test/middleware/halting_middleware_test.exs
index 64b39c3..8b129af 100644
--- a/test/middleware/halting_middleware_test.exs
+++ b/test/middleware/halting_middleware_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Middleware.HaltingMiddlewareTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.Helpers.CommandAuditMiddleware
diff --git a/test/middleware/middleware_test.exs b/test/middleware/middleware_test.exs
index 1249c0d..5722fa3 100644
--- a/test/middleware/middleware_test.exs
+++ b/test/middleware/middleware_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.Middleware.MiddlewareTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Enumerable
 
@@ -118,7 +118,7 @@ defmodule Commanded.Middleware.MiddlewareTest do
     command = %RaiseError{aggregate_uuid: UUID.uuid4()}
 
     # Force command handling to error
-    assert {:error, :aggregate_execution_failed} =
+    assert {:error, %RuntimeError{message: "failed"}} =
              Router.dispatch(command, application: DefaultApp)
 
     {dispatched, succeeded, failed} = CommandAuditMiddleware.count_commands()
diff --git a/test/process_managers/multi_routing_test.exs b/test/process_managers/multi_routing_test.exs
index a6e7aea..a29fef4 100644
--- a/test/process_managers/multi_routing_test.exs
+++ b/test/process_managers/multi_routing_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManager.MultiRoutingTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Assertions.EventAssertions
 
diff --git a/test/process_managers/process_manager_error_handling_state_test.exs b/test/process_managers/process_manager_error_handling_state_test.exs
index 819fec9..c25b350 100644
--- a/test/process_managers/process_manager_error_handling_state_test.exs
+++ b/test/process_managers/process_manager_error_handling_state_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingStateTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.ProcessManagers.ErrorAggregate.Commands.StartProcess
   alias Commanded.ProcessManagers.ErrorRouter
diff --git a/test/process_managers/process_manager_error_handling_test.exs b/test/process_managers/process_manager_error_handling_test.exs
index 658d496..ba81808 100644
--- a/test/process_managers/process_manager_error_handling_test.exs
+++ b/test/process_managers/process_manager_error_handling_test.exs
@@ -1,53 +1,86 @@
 defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
+  alias Commanded.Helpers.EventFactory
   alias Commanded.ProcessManagers.DefaultErrorHandlingProcessManager
+  alias Commanded.ProcessManagers.ErrorAggregate.Commands.StartProcess
+
+  alias Commanded.ProcessManagers.ErrorAggregate.Events.{
+    ProcessError,
+    ProcessException,
+    ProcessDispatchException
+  }
+
   alias Commanded.ProcessManagers.ErrorApp
   alias Commanded.ProcessManagers.ErrorHandlingProcessManager
   alias Commanded.ProcessManagers.ErrorRouter
-  alias Commanded.ProcessManagers.ErrorAggregate.Commands.RaiseError
-  alias Commanded.ProcessManagers.ErrorAggregate.Commands.RaiseException
-  alias Commanded.ProcessManagers.ErrorAggregate.Commands.StartProcess
+
+  alias Commanded.ProcessManagers.FailureContext
 
   setup do
     start_supervised!(ErrorApp)
 
-    :ok
+    {:ok, process_router} = ErrorHandlingProcessManager.start_link()
+
+    Process.unlink(process_router)
+    ref = Process.monitor(process_router)
+
+    [process_router: process_router, ref: ref]
   end
 
-  describe "process manager event handling error" do
-    test "should call `error/3` callback on error" do
-      {:ok, process_router} = ErrorHandlingProcessManager.start_link()
+  describe "process manager event handling exception" do
+    test "should print the stack trace", %{process_router: process_router, ref: ref} do
+      import ExUnit.CaptureLog
 
-      command = %RaiseError{
-        process_uuid: UUID.uuid4(),
-        message: "an error",
-        reply_to: reply_to()
-      }
+      send_error_message = fn ->
+        send_exception_event(process_router)
 
-      ref = Process.monitor(process_router)
+        assert_receive {:DOWN, ^ref, :process, ^process_router,
+                        %RuntimeError{message: "an exception"}}
+      end
 
-      assert :ok = ErrorRouter.dispatch(command, application: ErrorApp)
+      captured = capture_log(send_error_message)
 
-      assert_receive {:error, "an error"}
-      refute_receive {:DOWN, ^ref, _, _, _}
+      assert captured =~ "(RuntimeError) an exception"
+      assert captured =~ "error_handling_process_manager.ex"
+      assert captured =~ "Commanded.ProcessManagers.ErrorHandlingProcessManager.handle/2"
     end
 
-    test "should call `error/3` callback on exception" do
-      {:ok, process_router} = ErrorHandlingProcessManager.start_link()
+    test "should include the stack trace in failure context", %{
+      process_router: process_router,
+      ref: ref
+    } do
+      send_exception_event(process_router)
 
-      command = %RaiseException{
-        process_uuid: UUID.uuid4(),
-        message: "an exception",
-        reply_to: reply_to()
-      }
+      expected_runtime_error = %RuntimeError{message: "an exception"}
 
-      ref = Process.monitor(process_router)
+      assert_receive {:error, ^expected_runtime_error, %FailureContext{stacktrace: stacktrace}}
+      assert_receive {:DOWN, ^ref, :process, ^process_router, ^expected_runtime_error}
 
-      assert :ok = ErrorRouter.dispatch(command, application: ErrorApp)
+      refute is_nil(stacktrace)
+    end
+  end
+
+  describe "process manager event handling error" do
+    test "should call `error/3` callback on error", %{process_router: process_router, ref: ref} do
+      send_error_event(process_router)
 
-      assert_receive {:error, %RuntimeError{message: "an exception"}}
-      refute_receive {:DOWN, ^ref, _, _, _}
+      assert_receive {:error, "an error", %FailureContext{stacktrace: stacktrace}}
+      assert_receive {:DOWN, ^ref, :process, ^process_router, "an error"}
+
+      assert is_nil(stacktrace)
+    end
+
+    test "should call `error/3` callback on exception", %{
+      process_router: process_router,
+      ref: ref
+    } do
+      send_exception_event(process_router)
+
+      expected_runtime_error = %RuntimeError{message: "an exception"}
+
+      assert_receive {:error, ^expected_runtime_error, _failure_context}
+      assert_receive {:DOWN, ^ref, :process, ^process_router, ^expected_runtime_error}
     end
   end
 
@@ -66,11 +99,11 @@ defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingTest do
 
       assert :ok = ErrorRouter.dispatch(command, application: ErrorApp)
 
-      assert_receive {:error, :failed, %{attempts: 1}}
-      assert_receive {:error, :failed, %{attempts: 2}}
-      assert_receive {:error, :too_many_attempts, %{attempts: 3}}
+      assert_receive {:error, :failed, %{attempts: 1}, _failure_context}
+      assert_receive {:error, :failed, %{attempts: 2}, _failure_context}
+      assert_receive {:error, :too_many_attempts, %{attempts: 3}, _failure_context}
 
-      # should shutdown process router
+      # Should shutdown process router
       assert_receive {:DOWN, ^ref, :process, ^process_router, :too_many_attempts}
     end
 
@@ -89,9 +122,9 @@ defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingTest do
 
       assert :ok = ErrorRouter.dispatch(command, application: ErrorApp)
 
-      assert_receive {:error, :failed, %{attempts: 1, delay: 10}}
-      assert_receive {:error, :failed, %{attempts: 2, delay: 10}}
-      assert_receive {:error, :too_many_attempts, %{attempts: 3}}
+      assert_receive {:error, :failed, %{attempts: 1, delay: 10}, _failure_context}
+      assert_receive {:error, :failed, %{attempts: 2, delay: 10}, _failure_context}
+      assert_receive {:error, :too_many_attempts, %{attempts: 3}, _failure_context}
 
       # Should shutdown process router
       assert_receive {:DOWN, ^ref, :process, ^process_router, :too_many_attempts}
@@ -108,8 +141,8 @@ defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingTest do
 
       assert :ok = ErrorRouter.dispatch(command, application: ErrorApp)
 
-      assert_receive {:error, :failed, %{attempts: 1}}
-      refute_receive {:error, :failed, %{attempts: 2}}
+      assert_receive {:error, :failed, %{attempts: 1}, _failure_context}
+      refute_receive {:error, :failed, %{attempts: 2}, _failure_context}
 
       # Should not shutdown process router
       assert Process.alive?(process_router)
@@ -126,7 +159,7 @@ defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingTest do
 
       assert :ok = ErrorRouter.dispatch(command, application: ErrorApp)
 
-      assert_receive {:error, :failed, %{attempts: 1}}
+      assert_receive {:error, :failed, %{attempts: 1}, _failure_context}
       assert_receive :process_continued
 
       # Should not shutdown process router
@@ -149,5 +182,50 @@ defmodule Commanded.ProcessManager.ProcessManagerErrorHandlingTest do
     end
   end
 
+  describe "process manager dispatch command exception" do
+    test "should stop process manager", %{process_router: process_router, ref: ref} do
+      send_dispatch_exception_event(process_router)
+
+      expected_runtime_error = %RuntimeError{message: "a dispatch exception"}
+
+      assert_receive {:error, ^expected_runtime_error, %FailureContext{}}
+      assert_receive {:DOWN, ^ref, :process, ^process_router, ^expected_runtime_error}
+    end
+  end
+
+  defp send_error_event(process_router) do
+    process_uuid = UUID.uuid4()
+
+    send_events_to_process_router(process_router, [
+      %ProcessError{process_uuid: process_uuid, reply_to: reply_to(), message: "an error"}
+    ])
+  end
+
+  defp send_exception_event(process_router) do
+    process_uuid = UUID.uuid4()
+
+    send_events_to_process_router(process_router, [
+      %ProcessException{process_uuid: process_uuid, reply_to: reply_to(), message: "an exception"}
+    ])
+  end
+
+  defp send_dispatch_exception_event(process_router) do
+    process_uuid = UUID.uuid4()
+
+    send_events_to_process_router(process_router, [
+      %ProcessDispatchException{
+        process_uuid: process_uuid,
+        reply_to: reply_to(),
+        message: "a dispatch exception"
+      }
+    ])
+  end
+
+  defp send_events_to_process_router(process_router, events) do
+    recorded_events = EventFactory.map_to_recorded_events(events)
+
+    send(process_router, {:events, recorded_events})
+  end
+
   defp reply_to, do: :erlang.pid_to_list(self())
 end
diff --git a/test/process_managers/process_manager_idle_timeout_test.exs b/test/process_managers/process_manager_idle_timeout_test.exs
index 7e9c568..431554a 100644
--- a/test/process_managers/process_manager_idle_timeout_test.exs
+++ b/test/process_managers/process_manager_idle_timeout_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManagers.ProcessManagerIdleTimeoutTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.ProcessManagers.ExampleApp
   alias Commanded.ProcessManagers.ExampleAggregate.Commands.Start
diff --git a/test/process_managers/process_manager_instance_exception_test.exs b/test/process_managers/process_manager_instance_exception_test.exs
index 4ea5152..21badb6 100644
--- a/test/process_managers/process_manager_instance_exception_test.exs
+++ b/test/process_managers/process_manager_instance_exception_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManagers.ProcessManagerInstanceExceptionTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.ProcessManagers.{ExampleApp, ExampleRouter, ExampleProcessManager}
   alias Commanded.ProcessManagers.ExampleAggregate.Commands.{Error, Raise, Start}
diff --git a/test/process_managers/process_manager_integration_test.exs b/test/process_managers/process_manager_integration_test.exs
index d79f540..e3dd6e0 100644
--- a/test/process_managers/process_manager_integration_test.exs
+++ b/test/process_managers/process_manager_integration_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManagers.ProcessManagerIntegrationTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Assertions.EventAssertions
 
diff --git a/test/process_managers/process_manager_timeout_test.exs b/test/process_managers/process_manager_timeout_test.exs
index 95fde5a..daf0e7c 100644
--- a/test/process_managers/process_manager_timeout_test.exs
+++ b/test/process_managers/process_manager_timeout_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManagers.ProcessManagerTimeoutTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Helpers.Wait
   alias Commanded.ProcessManagers.ProcessRouter
diff --git a/test/process_managers/process_router_process_pending_events_test.exs b/test/process_managers/process_router_process_pending_events_test.exs
index 95559fb..20d15ed 100644
--- a/test/process_managers/process_router_process_pending_events_test.exs
+++ b/test/process_managers/process_router_process_pending_events_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManagers.ProcessRouterProcessPendingEventsTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Assertions.EventAssertions
   import Commanded.Enumerable
diff --git a/test/process_managers/resume_process_manager_test.exs b/test/process_managers/resume_process_manager_test.exs
index db8a05b..b512ccb 100644
--- a/test/process_managers/resume_process_manager_test.exs
+++ b/test/process_managers/resume_process_manager_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.ProcessManagers.ResumeProcessManagerTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   import Commanded.Assertions.EventAssertions
 
diff --git a/test/process_managers/support/error/error_aggregate.ex b/test/process_managers/support/error/error_aggregate.ex
index 5c6635b..588f6d9 100644
--- a/test/process_managers/support/error/error_aggregate.ex
+++ b/test/process_managers/support/error/error_aggregate.ex
@@ -6,49 +6,54 @@ defmodule Commanded.ProcessManagers.ErrorAggregate do
   defmodule Commands do
     defmodule StartProcess do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :strategy, :delay, :reply_to])
+      defstruct [:process_uuid, :strategy, :delay, :reply_to]
     end
 
     defmodule RaiseError do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :message, :reply_to])
+      defstruct [:process_uuid, :message, :reply_to]
     end
 
     defmodule RaiseException do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :message, :reply_to])
+      defstruct [:process_uuid, :message, :reply_to]
     end
 
     defmodule AttemptProcess do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :strategy, :delay, :reply_to])
+      defstruct [:process_uuid, :strategy, :delay, :reply_to]
     end
 
     defmodule ContinueProcess do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :reply_to])
+      defstruct [:process_uuid, :reply_to]
     end
   end
 
   defmodule Events do
     defmodule ProcessStarted do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :strategy, :delay, :reply_to])
+      defstruct [:process_uuid, :strategy, :delay, :reply_to]
     end
 
     defmodule ProcessContinued do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :reply_to])
+      defstruct [:process_uuid, :reply_to]
     end
 
     defmodule ProcessError do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :message, :reply_to])
+      defstruct [:process_uuid, :message, :reply_to]
     end
 
     defmodule ProcessException do
       @derive Jason.Encoder
-      defstruct([:process_uuid, :message, :reply_to])
+      defstruct [:process_uuid, :message, :reply_to]
+    end
+
+    defmodule ProcessDispatchException do
+      @derive Jason.Encoder
+      defstruct [:process_uuid, :message, :reply_to]
     end
   end
 
@@ -73,15 +78,15 @@ defmodule Commanded.ProcessManagers.ErrorAggregate do
   end
 
   def execute(%ErrorAggregate{}, %RaiseError{} = command) do
-    %RaiseError{process_uuid: process_uuid, message: message, reply_to: reply_to} = command
+    %RaiseError{message: message} = command
 
-    %ProcessError{process_uuid: process_uuid, message: message, reply_to: reply_to}
+    {:error, message}
   end
 
   def execute(%ErrorAggregate{}, %RaiseException{} = command) do
-    %RaiseException{process_uuid: process_uuid, message: message, reply_to: reply_to} = command
+    %RaiseException{message: message} = command
 
-    %ProcessException{process_uuid: process_uuid, message: message, reply_to: reply_to}
+    raise message
   end
 
   def execute(%ErrorAggregate{}, %AttemptProcess{}),
diff --git a/test/process_managers/support/error/error_handling_process_manager.ex b/test/process_managers/support/error/error_handling_process_manager.ex
index aa701ad..bc12277 100644
--- a/test/process_managers/support/error/error_handling_process_manager.ex
+++ b/test/process_managers/support/error/error_handling_process_manager.ex
@@ -2,11 +2,18 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
   @moduledoc false
 
   alias Commanded.ProcessManagers.{ErrorHandlingProcessManager, FailureContext}
-  alias Commanded.ProcessManagers.ErrorAggregate.Commands.{AttemptProcess, ContinueProcess}
+
+  alias Commanded.ProcessManagers.ErrorAggregate.Commands.{
+    AttemptProcess,
+    ContinueProcess,
+    RaiseException
+  }
+
   alias Commanded.ProcessManagers.ErrorApp
 
   alias Commanded.ProcessManagers.ErrorAggregate.Events.{
     ProcessContinued,
+    ProcessDispatchException,
     ProcessError,
     ProcessException,
     ProcessStarted
@@ -24,6 +31,9 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
   def interested?(%ProcessException{process_uuid: process_uuid}), do: {:start, process_uuid}
   def interested?(%ProcessContinued{process_uuid: process_uuid}), do: {:continue, process_uuid}
 
+  def interested?(%ProcessDispatchException{process_uuid: process_uuid}),
+    do: {:start, process_uuid}
+
   def handle(%ErrorHandlingProcessManager{}, %ProcessStarted{} = event) do
     %ProcessStarted{
       process_uuid: process_uuid,
@@ -40,6 +50,13 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
     }
   end
 
+  def handle(%ErrorHandlingProcessManager{}, %ProcessDispatchException{} = event) do
+    %ProcessDispatchException{process_uuid: process_uuid, reply_to: reply_to, message: message} =
+      event
+
+    %RaiseException{process_uuid: process_uuid, reply_to: reply_to, message: message}
+  end
+
   # Simulate an error handling an event.
   def handle(%ErrorHandlingProcessManager{}, %ProcessError{} = event) do
     %ProcessError{message: message} = event
@@ -63,23 +80,33 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
   end
 
   # Skip events causing errors during event handling
-  def error({:error, _error} = error, %ProcessError{} = event, _failure_context) do
+  def error({:error, error}, %ProcessError{} = event, failure_context) do
     %ProcessError{reply_to: reply_to} = event
-    reply(reply_to, error)
 
-    :skip
+    reply(reply_to, {:error, error, failure_context})
+
+    {:stop, error}
   end
 
-  # Skip events causing exceptions during event handling
-  def error({:error, _error} = error, %ProcessException{} = event, _failure_context) do
+  # Stop on exceptions during event handling
+  def error({:error, error}, %ProcessException{} = event, failure_context) do
     %ProcessException{reply_to: reply_to} = event
 
-    reply(reply_to, error)
+    reply(reply_to, {:error, error, failure_context})
 
-    :skip
+    {:stop, error}
   end
 
-  # Stop after three attempts
+  # Stop on exceptions during command dispatch.
+  def error({:error, error}, %RaiseException{} = command, failure_context) do
+    %RaiseException{reply_to: reply_to} = command
+
+    reply(reply_to, {:error, error, failure_context})
+
+    {:stop, error}
+  end
+
+  # Stop after three attempts.
   def error(
         {:error, :failed},
         %AttemptProcess{strategy: "retry"} = command,
@@ -89,7 +116,7 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
     %AttemptProcess{reply_to: reply_to} = command
 
     context = record_attempt(failure_context)
-    reply(reply_to, {:error, :too_many_attempts, context})
+    reply(reply_to, {:error, :too_many_attempts, context, failure_context})
 
     {:stop, :too_many_attempts}
   end
@@ -103,8 +130,8 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
       when is_integer(delay) do
     %AttemptProcess{reply_to: reply_to} = command
 
-    context = record_attempt(failure_context)
-    reply_failure(reply_to, Map.put(context, :delay, delay))
+    context = failure_context |> record_attempt() |> Map.put(:delay, delay)
+    reply(reply_to, {:error, :failed, context, failure_context})
 
     {:retry, delay, context}
   end
@@ -114,7 +141,7 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
     %AttemptProcess{reply_to: reply_to} = command
 
     context = record_attempt(failure_context)
-    reply_failure(reply_to, context)
+    reply(reply_to, {:error, :failed, context, failure_context})
 
     {:retry, context}
   end
@@ -124,7 +151,7 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
     %AttemptProcess{reply_to: reply_to} = command
 
     context = record_attempt(failure_context)
-    reply(reply_to, {:error, :failed, context})
+    reply(reply_to, {:error, :failed, context, failure_context})
 
     {:skip, :continue_pending}
   end
@@ -134,7 +161,7 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
     %AttemptProcess{process_uuid: process_uuid, reply_to: reply_to} = command
 
     context = record_attempt(failure_context)
-    reply(reply_to, {:error, :failed, context})
+    reply(reply_to, {:error, :failed, context, failure_context})
 
     continue = %ContinueProcess{process_uuid: process_uuid, reply_to: reply_to}
 
@@ -145,12 +172,9 @@ defmodule Commanded.ProcessManagers.ErrorHandlingProcessManager do
     Map.update(context, :attempts, 1, fn attempts -> attempts + 1 end)
   end
 
-  defp reply_failure(reply_to, context) do
-    reply(reply_to, {:error, :failed, context})
-  end
-
   defp reply(reply_to, message) do
     pid = :erlang.list_to_pid(reply_to)
+
     send(pid, message)
   end
 end
diff --git a/test/subscriptions/distributed_subscriptions_test.exs b/test/subscriptions/distributed_subscriptions_test.exs
index ed6096b..105aeca 100644
--- a/test/subscriptions/distributed_subscriptions_test.exs
+++ b/test/subscriptions/distributed_subscriptions_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.DistributedSubscriptionsTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.Subscriptions.DistributedSubscribers
 
diff --git a/test/subscriptions/subscriptions_test.exs b/test/subscriptions/subscriptions_test.exs
index 6c0612f..2c22ac0 100644
--- a/test/subscriptions/subscriptions_test.exs
+++ b/test/subscriptions/subscriptions_test.exs
@@ -1,5 +1,5 @@
 defmodule Commanded.SubscriptionsTest do
-  use Commanded.StorageCase
+  use ExUnit.Case
 
   alias Commanded.DefaultApp
   alias Commanded.EventStore.RecordedEvent