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
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