From ffb052a3539860082aab57851e4570944b5017a7 Mon Sep 17 00:00:00 2001 From: tmorin Date: Sun, 18 Feb 2024 17:50:42 +0100 Subject: [PATCH] feat: throw appropriate exception messages when before and after lambdas are not implemented as expected --- .../core/validation/ScenarioExecutor.java | 29 ++++++++++++------- .../faggregate/core/validation/Suite.java | 24 +++++++++++---- .../core/validation/ScenarioExecutorTest.java | 16 +++++----- .../faggregate/core/validation/SuiteTest.java | 4 +-- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/core-scenario/src/main/java/io/morin/faggregate/core/validation/ScenarioExecutor.java b/core-scenario/src/main/java/io/morin/faggregate/core/validation/ScenarioExecutor.java index b66e75b..eb71cc2 100644 --- a/core-scenario/src/main/java/io/morin/faggregate/core/validation/ScenarioExecutor.java +++ b/core-scenario/src/main/java/io/morin/faggregate/core/validation/ScenarioExecutor.java @@ -3,6 +3,7 @@ import io.morin.faggregate.api.AggregateManager; import io.morin.faggregate.api.Output; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -75,8 +76,7 @@ public CompletionStage execute() { return Optional // GIVEN STEP - initialize the state of the aggregate with a given state .ofNullable(scenario.getGiven().getState()) - .map(state -> before.initialize(scenario.getGiven().getIdentifier(), state, scenario.getGiven().getEvents()) - ) + .map(state -> before.store(scenario.getGiven().getIdentifier(), state, scenario.getGiven().getEvents())) .orElseGet(() -> CompletableFuture.completedStage(null)) // GIVEN STEP - mutate the aggregate with given commands .thenAccept(unused -> { @@ -94,7 +94,7 @@ public CompletionStage execute() { // WHEN - create the outcome based on the fetch aggregate state and the output of the command .thenCompose(output -> after - .fetch(scenario.getGiven().getIdentifier()) + .load(scenario.getGiven().getIdentifier()) .thenApply(currentState -> new Outcome(output, currentState)) ) // THEN - assert the outcome @@ -102,7 +102,16 @@ public CompletionStage execute() { // THEN - check actual state with the expected one Optional .ofNullable(scenario.getThen().getState()) - .ifPresent(expectedState -> stateAsserter.process(scenario, outcome.state, expectedState)); + .ifPresent(expectedState -> + stateAsserter.process( + scenario, + Objects.requireNonNull( + outcome.state, + "The after lambda of the suite execution is not implemented!" + ), + expectedState + ) + ); // THEN - check actual events with the expected ones Optional .ofNullable(scenario.getThen().getEvents()) @@ -123,14 +132,14 @@ public CompletionStage execute() { @FunctionalInterface public interface Before { /** - * Initialize the state of the aggregate. + * Store the state of the aggregate. * * @param identifier the identifier of the aggregate * @param state the initial state of the aggregate * @param events a set of initial domain events * @return a completion stage */ - CompletionStage initialize(@NonNull Object identifier, @NonNull Object state, @NonNull List events); + CompletionStage store(@NonNull Object identifier, @NonNull Object state, @NonNull List events); } /** @@ -139,12 +148,12 @@ public interface Before { @FunctionalInterface public interface After { /** - * Fetch the state of the aggregate. + * Load the state of the aggregate. * * @param identifier the identifier of the aggregate - * @return the embedded in a CompletionStage + * @return the state of the aggregate as a completion stage */ - CompletionStage fetch(@NonNull Object identifier); + CompletionStage load(@NonNull Object identifier); } /** @@ -159,7 +168,7 @@ static class Outcome { final Output output; /** - * The state fetched using {@link After#fetch(Object)}. + * The state fetched using {@link After#load(Object)}. */ final Object state; } diff --git a/core-scenario/src/main/java/io/morin/faggregate/core/validation/Suite.java b/core-scenario/src/main/java/io/morin/faggregate/core/validation/Suite.java index e4677a0..2489fb7 100644 --- a/core-scenario/src/main/java/io/morin/faggregate/core/validation/Suite.java +++ b/core-scenario/src/main/java/io/morin/faggregate/core/validation/Suite.java @@ -23,9 +23,9 @@ public class Suite { /** * Execute the scenarios sequentially. * - * @param am the Aggregate Manager - * @return a completion stage + * @param am the Aggregate Manager * @param The type of the identifier + * @return a completion stage */ @SneakyThrows public CompletableFuture execute(@NonNull AggregateManager am) { @@ -34,12 +34,20 @@ public CompletableFuture execute(@NonNull AggregateManager am) { /** * Execute the scenarios sequentially. + *

+ * The before lambda is executed before the _Given_ phase. + * Its purpose is to store the state of the aggregate. + * As long as the state of the artifact is not provided during the _Given_ phase, the before lambda is not mandatory. + *

+ * The after lambda is executed after the _Then_ phase. + * Its purpose is to load the state of the aggregate. + * As long as the state of the artifact is not validated during the _Then_ phase, the after lambda is not mandatory. * * @param am the Aggregate Manager - * @param before the before lambda - * @param after the after lambda + * @param before the optional before lambda + * @param after the optional after lambda + * @param The type of the identifier * @return a completion stage - * @param The type of the identifier */ @SneakyThrows @SuppressWarnings("unchecked") @@ -58,7 +66,11 @@ public CompletableFuture execute( .before( Optional .ofNullable(before) - .orElse((identifier, state, events) -> CompletableFuture.completedStage(null)) + .orElse((identifier, state, events) -> + CompletableFuture.failedFuture( + new UnsupportedOperationException("No state storing is implemented.") + ) + ) ) .after(Optional.ofNullable(after).orElse(identifier -> CompletableFuture.completedStage(null))) .build() diff --git a/core-scenario/src/test/java/io/morin/faggregate/core/validation/ScenarioExecutorTest.java b/core-scenario/src/test/java/io/morin/faggregate/core/validation/ScenarioExecutorTest.java index b7430f9..fa8d7be 100644 --- a/core-scenario/src/test/java/io/morin/faggregate/core/validation/ScenarioExecutorTest.java +++ b/core-scenario/src/test/java/io/morin/faggregate/core/validation/ScenarioExecutorTest.java @@ -83,9 +83,9 @@ void shouldSuccess() { .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).add(expectedEvent).build())); when(am.execute(eq("id"), any(String.class))) .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).build())); - when(after.fetch(any())).thenReturn(CompletableFuture.completedStage(expectedState)); + when(after.load(any())).thenReturn(CompletableFuture.completedStage(expectedState)); Assertions.assertDoesNotThrow(() -> executor.execute().toCompletableFuture().get()); - verify(before, new Only()).initialize(eq("id"), eq(initialState), anyList()); + verify(before, new Only()).store(eq("id"), eq(initialState), anyList()); verify(am, new Times(2)).execute(eq("id"), anyString()); verify(asserter, new Only()).accept(eq(expectedState), anyList()); } @@ -94,9 +94,9 @@ void shouldSuccess() { void shouldSuccessWhenInitialState() { when(am.execute(eq("id"), any(ApplyUppercase.class))) .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).add(expectedEvent).build())); - when(after.fetch(any())).thenReturn(CompletableFuture.completedStage(expectedState)); + when(after.load(any())).thenReturn(CompletableFuture.completedStage(expectedState)); Assertions.assertDoesNotThrow(() -> executorAlt.execute().toCompletableFuture().get()); - verify(before, new NoInteractions()).initialize(any(), any(), any()); + verify(before, new NoInteractions()).store(any(), any(), any()); verify(am, new Only()).execute(eq("id"), any(ApplyUppercase.class)); } @@ -104,7 +104,7 @@ void shouldSuccessWhenInitialState() { void shouldFailedWhenWrongExpectedState() { when(am.execute(any(), any())) .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).add(expectedEvent).build())); - when(after.fetch(any())).thenReturn(CompletableFuture.completedStage(initialState)); + when(after.load(any())).thenReturn(CompletableFuture.completedStage(initialState)); Assertions.assertThrows(ExecutionException.class, () -> executor.execute().toCompletableFuture().get()); } @@ -113,7 +113,7 @@ void shouldFailedWhenWrongExpectedState() { void shouldFailedWhenWrongExpectedEvent() { when(am.execute(any(), any())) .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).add(wrongExpectedEvent).build())); - when(after.fetch(any())).thenReturn(CompletableFuture.completedStage(expectedState)); + when(after.load(any())).thenReturn(CompletableFuture.completedStage(expectedState)); Assertions.assertThrows(ExecutionException.class, () -> executor.execute().toCompletableFuture().get()); } @@ -126,7 +126,7 @@ void shouldFailedWhenTooMuchExpectedEvents() { OutputBuilder.get(null).add(expectedEvent).add(wrongExpectedEvent).build() ) ); - when(after.fetch(any())).thenReturn(CompletableFuture.completedStage(expectedState)); + when(after.load(any())).thenReturn(CompletableFuture.completedStage(expectedState)); Assertions.assertThrows(ExecutionException.class, () -> executor.execute().toCompletableFuture().get()); } @@ -143,7 +143,7 @@ void shouldFailedWhenAsserterFails() { .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).add(expectedEvent).build())); when(am.execute(eq("id"), any(String.class))) .thenReturn(CompletableFuture.completedFuture(OutputBuilder.get(null).build())); - when(after.fetch(any())).thenReturn(CompletableFuture.completedStage(expectedState)); + when(after.load(any())).thenReturn(CompletableFuture.completedStage(expectedState)); doThrow(new IllegalStateException()).when(asserter).accept(any(), any()); Assertions.assertThrows(ExecutionException.class, () -> executor.execute().toCompletableFuture().get()); diff --git a/core-scenario/src/test/java/io/morin/faggregate/core/validation/SuiteTest.java b/core-scenario/src/test/java/io/morin/faggregate/core/validation/SuiteTest.java index ed9c325..7b99358 100644 --- a/core-scenario/src/test/java/io/morin/faggregate/core/validation/SuiteTest.java +++ b/core-scenario/src/test/java/io/morin/faggregate/core/validation/SuiteTest.java @@ -62,7 +62,7 @@ void shouldExecute() { when(scenarioC.getThen()).thenReturn(then); when(am.execute(any(), any())).thenReturn(CompletableFuture.completedFuture(output)); - when(after.fetch(any())).thenReturn(CompletableFuture.completedFuture(null)); + when(after.load(any())).thenReturn(CompletableFuture.completedFuture(null)); assertDoesNotThrow(() -> Suite @@ -77,7 +77,7 @@ void shouldExecute() { ); verify(am, Mockito.times(3)).execute(any(), any()); - verify(after, Mockito.times(3)).fetch(any()); + verify(after, Mockito.times(3)).load(any()); verify(scenarioA, Mockito.times(4)).getGiven(); verify(scenarioA, Mockito.times(1)).getWhen();