Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an option to create a fixture for a state stored aggregate #1772

Merged
merged 9 commits into from
May 12, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.axonframework.commandhandling.GenericCommandMessage;
import org.axonframework.commandhandling.SimpleCommandBus;
import org.axonframework.common.Assert;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.common.ReflectionUtils;
import org.axonframework.common.Registration;
import org.axonframework.deadline.DeadlineMessage;
Expand Down Expand Up @@ -123,6 +124,7 @@ public class AggregateTestFixture<T> implements FixtureConfiguration<T>, TestExe
private final EventStore eventStore;
private final List<FieldFilter> fieldFilters = new ArrayList<>();
private final List<Object> resources = new ArrayList<>();
private boolean useStateStorage;
private RepositoryProvider repositoryProvider;
private IdentifierValidatingRepository<T> repository;
private final StubDeadlineManager deadlineManager;
Expand Down Expand Up @@ -166,6 +168,12 @@ public final FixtureConfiguration<T> withSubtypes(Class<? extends T>... subtypes
return this;
}

@Override
public FixtureConfiguration<T> useStateStorage() {
this.useStateStorage = true;
smcvb marked this conversation as resolved.
Show resolved Hide resolved
return this;
}

@Override
public FixtureConfiguration<T> registerRepository(Repository<T> repository) {
this.repository = new IdentifierValidatingRepository<>(repository);
Expand Down Expand Up @@ -306,21 +314,20 @@ public TestExecutor<T> andGiven(Object... domainEvents) {

@Override
public TestExecutor<T> givenNoPriorActivity() {
return given(Collections.emptyList());
ensureRepositoryConfiguration();
clearGivenWhenState();
return this;
}

@Override
public TestExecutor<T> givenState(Supplier<T> aggregate) {
if (this.repository == null) {
this.useStateStorage();
}

ensureRepositoryConfiguration();
clearGivenWhenState();
DefaultUnitOfWork.startAndGet(null).execute(() -> {
if (repository == null) {
registerRepository(new InMemoryRepository<>(aggregateType,
subtypes,
eventStore,
getParameterResolverFactory(),
getHandlerDefinition(),
getRepositoryProvider()));
}
try {
repository.newInstance(aggregate::get);
} catch (Exception e) {
Expand All @@ -341,6 +348,11 @@ public TestExecutor<T> given(List<?> domainEvents) {

@Override
public TestExecutor<T> andGiven(List<?> domainEvents) {
if (this.useStateStorage) {
throw new AxonConfigurationException(
"Given events not supported, because the fixture is configured to use state storage");
}

for (Object event : domainEvents) {
Object payload = event;
MetaData metaData = null;
Expand Down Expand Up @@ -506,16 +518,29 @@ private void registerAggregateCommandHandlers() {
}

private void ensureRepositoryConfiguration() {
if (repository == null) {
if (repository != null) {
return;
}

if (this.useStateStorage) {
this.registerRepository(new InMemoryRepository<>(
aggregateType,
subtypes,
eventStore,
getParameterResolverFactory(),
getHandlerDefinition(),
getRepositoryProvider()));
} else {
AggregateModel<T> aggregateModel = aggregateModel();
registerRepository(EventSourcingRepository.builder(aggregateType)
.aggregateModel(aggregateModel)
.aggregateFactory(new GenericAggregateFactory<>(aggregateModel))
.eventStore(eventStore)
.parameterResolverFactory(getParameterResolverFactory())
.handlerDefinition(getHandlerDefinition())
.repositoryProvider(getRepositoryProvider())
.build());
this.registerRepository(EventSourcingRepository.builder(aggregateType)
.aggregateModel(aggregateModel)
.aggregateFactory(new GenericAggregateFactory<>(
aggregateModel))
.eventStore(eventStore)
.parameterResolverFactory(getParameterResolverFactory())
.handlerDefinition(getHandlerDefinition())
.repositoryProvider(getRepositoryProvider())
.build());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ public interface FixtureConfiguration<T> {
*/
FixtureConfiguration<T> withSubtypes(Class<? extends T>... subtypes);

/**
* Configures the fixture for state stored aggregates.
sascha-eisenmann marked this conversation as resolved.
Show resolved Hide resolved
* This will register an {@link org.axonframework.test.aggregate.AggregateTestFixture.InMemoryRepository} with the fixture
* Should be used before calling {@link FixtureConfiguration#givenState(Supplier)} or {@link FixtureConfiguration#givenCommands(List)} (Supplier)}
sascha-eisenmann marked this conversation as resolved.
Show resolved Hide resolved
*
* @return the current FixtureConfiguration, for fluent interfacing
*/
FixtureConfiguration<T> useStateStorage();

/**
* Registers an arbitrary {@code repository} with the fixture. The repository must be wired
* with the Event Store of this test fixture.
Expand Down Expand Up @@ -323,8 +332,8 @@ FixtureConfiguration<T> registerDeadlineHandlerInterceptor(
TestExecutor<T> givenState(Supplier<T> aggregateState);

/**
* Indicates that no relevant activity has occurred in the past. The behavior of this method is identical to giving
* no events in the {@link #given(java.util.List)} method.
* Indicates that no relevant activities like commands or events have occurred in the past.
* This also means that no previous state is present in the repository.
*
* @return a TestExecutor instance that can execute the test with this configuration
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
package org.axonframework.test.aggregate;

import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.messaging.unitofwork.CurrentUnitOfWork;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
import org.junit.jupiter.api.*;

import java.util.Collections;
import java.util.function.Supplier;

import static org.axonframework.modelling.command.AggregateLifecycle.apply;
Expand Down Expand Up @@ -61,6 +63,27 @@ void testCreateStateStoredAggregate() {
.expectState(aggregate -> assertEquals("message2", aggregate.getMessage()));
}

@Test
void testGivenCommandsForStateStoredAggregate() {
fixture
smcvb marked this conversation as resolved.
Show resolved Hide resolved
.useStateStorage()
.givenCommands(new InitializeCommand(AGGREGATE_ID, "message"))
.when(new SetMessageCommand(AGGREGATE_ID, "message2"))
.expectEvents(new StubDomainEvent())
.expectState(aggregate -> assertEquals("message2", aggregate.getMessage()));
}


@Test
void testCreateStateStoredAggregateWithCommand() {
fixture
.useStateStorage()
.givenNoPriorActivity()
.when(new InitializeCommand(AGGREGATE_ID, "message"))
.expectEvents(new StubDomainEvent())
.expectState(aggregate -> assertEquals("message", aggregate.getMessage()));
}

@Test
void testEmittedEventsFromExpectStateAreNotStored() {
fixture.givenState(() -> new StateStoredAggregate(AGGREGATE_ID, "message"))
Expand Down Expand Up @@ -105,6 +128,28 @@ void testStateStoredAggregateCanAttachRegisteredResource() {
verify(testResource).difficultOperation(expectedMessage);
}

@Test
void testGivenWithStateStorageException() {
fixture
.useStateStorage();

assertThrows(
AxonConfigurationException.class,
() -> fixture.given(new StubDomainEvent())
);
}

@Test
void testGivenWithEventListAndStateStorageExpectException() {
fixture
.useStateStorage();

assertThrows(
AxonConfigurationException.class,
() -> fixture.given(Collections.singletonList(new StubDomainEvent()))
);
}

private static class InitializeCommand {

private final String id;
Expand Down Expand Up @@ -200,6 +245,7 @@ public static class StateStoredAggregate {
@CommandHandler
public StateStoredAggregate(InitializeCommand cmd) {
this.id = cmd.getId();
this.message = cmd.getMessage();
apply(new StubDomainEvent());
}

Expand Down