Skip to content

Commit

Permalink
Merge pull request #32 from JohT/feature/projectiontest
Browse files Browse the repository at this point in the history
Query side projection integration tests based on messages
  • Loading branch information
JohT authored Dec 28, 2021
2 parents bc3048f + 31c5e28 commit cb4e0c1
Show file tree
Hide file tree
Showing 9 changed files with 3,581 additions and 3,773 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
"queryAllDeclaredMethods":true}
,
{
"name":"org.axonframework.config.DefaultConfigurer$$Lambda$611/0x000000084058cc40",
"name":"org.axonframework.config.DefaultConfigurer$$Lambda$611/0x000000084058c840",
"queryAllDeclaredMethods":true}
,
{
Expand All @@ -285,7 +285,7 @@
]}
,
{
"name":"org.axonframework.config.MessageMonitorFactoryBuilder$$Lambda$602/0x000000084058e840",
"name":"org.axonframework.config.MessageMonitorFactoryBuilder$$Lambda$602/0x000000084058f440",
"queryAllDeclaredMethods":true}
,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
{
"pattern":"\\Qdb/command/common/V1.0.0__AxonOnMicroprofileTryoutEventsourcing.sql\\E"
},
{
"pattern":"\\Qdb/command/common/V1.1.7__DomainEventEntryTypeNullable.sql\\E"
},
{
"pattern":"\\Qdb/command/h2/V1.0.1__H2BlobTypeForBinaryData.sql\\E"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@
import io.github.joht.showcase.quarkuseventsourcing.messaging.infrastructure.axon.serializer.jsonb.axon.JsonbSerializer;
import io.github.joht.showcase.quarkuseventsourcing.messaging.infrastructure.axon.transaction.jta.JtaTransactionManager;
import io.github.joht.showcase.quarkuseventsourcing.messaging.infrastructure.axon.upcaster.AnnotationEventRevisionResolver;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.axon.EventPublishingAdapter;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.axon.QueryReplayAdapter;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.axon.QuerySubmitterAdapter;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.axon.QueryUpdateEmitterAdapter;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.EventPublishingService;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.QueryModelProjection;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.QueryProcessor;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.QueryProjectionManagementService;
Expand Down Expand Up @@ -177,6 +179,12 @@ public QueryProjectionManagementService getQueryReplayService() {
return new QueryReplayAdapter(configuration.eventProcessingConfiguration());
}

@Produces
@ApplicationScoped
public EventPublishingService getEventPublishingService() {
return new EventPublishingAdapter(configuration.eventGateway());
}

private Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.github.joht.showcase.quarkuseventsourcing.messaging.query.axon;

import static javax.transaction.Transactional.TxType.REQUIRED;

import javax.transaction.Transactional;

import org.axonframework.eventhandling.gateway.EventGateway;

import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.EventPublishingService;

public class EventPublishingAdapter implements EventPublishingService {

private final EventGateway eventGateway;

public EventPublishingAdapter(EventGateway eventGateway) {
this.eventGateway = eventGateway;
}

/**
* {@inheritDoc}
*/
@Override
@Transactional(REQUIRED)
public void publish(Object... events) {
eventGateway.publish(events);
}

@Override
public String toString() {
return "EventPublishingAdapter [eventGateway=" + eventGateway + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary;

/**
* Provides methods to publish events.
* Based on the "EventGateway" in "AxonFramework".
*/
public interface EventPublishingService {

/**
* Publishes events that will be dispatched to all subscribed listeners.
*
* @param events The collection of events to publish
*/
void publish(Object... events);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "axon_on_microprofile_tryout"."domainevententry" ALTER COLUMN TYPE VARCHAR(255) NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.joht.showcase.quarkuseventsourcing.messaging.query.axon;

import static org.mockito.Mockito.verify;

import org.axonframework.eventhandling.gateway.EventGateway;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class EventPublishingAdapterTest {

@Mock
EventGateway eventGateway;

@InjectMocks
EventPublishingAdapter adapterUnderTest;

@Test
void eventPublishDelegatedToEventGateway() {
TestEvent testEvent = new TestEvent();
adapterUnderTest.publish(testEvent);
verify(eventGateway).publish(testEvent);
}

private static class TestEvent {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.github.joht.showcase.quarkuseventsourcing.query.model.account;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.UUID;
import java.util.concurrent.ExecutionException;

import javax.inject.Inject;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import io.github.joht.showcase.quarkuseventsourcing.message.common.Nickname;
import io.github.joht.showcase.quarkuseventsourcing.message.event.account.AccountCreatedEvent;
import io.github.joht.showcase.quarkuseventsourcing.message.event.account.NicknameChangedEvent;
import io.github.joht.showcase.quarkuseventsourcing.message.event.account.NicknamePresetEvent;
import io.github.joht.showcase.quarkuseventsourcing.message.query.account.AccountNicknameQuery;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.EventPublishingService;
import io.github.joht.showcase.quarkuseventsourcing.messaging.query.boundary.QuerySubmitterService;
import io.quarkus.test.junit.QuarkusTest;

/**
* Read-Side (Integration-)Test that operates on message level.
* <p>
* This is a black-box test. Published events represent the input. Query messages return the output,
* that is compared to the expected values.
* Given that there is no pre-existing state, the test is perfectly reconstructible and repeatable.
* <p>
* This test also shows the strength of a CQRS-Read-Side (Projection) that ideally only depends
* on the events it consumes and its current state.
* Test cases can be completely described as a sequence of events.
*/
@QuarkusTest
class AccountProjectionTest {

@Inject
EventPublishingService eventGateway;

@Inject
QuerySubmitterService queryGateway;

@Test
@DisplayName("After a account had been created and the nickname had been changed the new nickname is returned by the query")
void nicknameChangeQueryable() throws InterruptedException, ExecutionException {
String accountId = createTestAccountId();
Nickname expectedNickname = Nickname.of("Eddie");

eventGateway.publish(new AccountCreatedEvent(accountId));
eventGateway.publish(new NicknamePresetEvent(accountId, Nickname.none()));
eventGateway.publish(new NicknameChangedEvent(accountId, expectedNickname, Nickname.none()));

Nickname nickname = queryGateway.query(new AccountNicknameQuery(accountId), Nickname.class).get();
assertEquals(expectedNickname, nickname);
}

private String createTestAccountId() {
return getClass().getSimpleName() + "-accountId-" + UUID.randomUUID();
}
}

0 comments on commit cb4e0c1

Please sign in to comment.