From 69dc18f424df6bc38987f1b0c33dd7aef4398170 Mon Sep 17 00:00:00 2001 From: Michael Schnell Date: Sat, 28 Dec 2019 16:14:29 +0100 Subject: [PATCH] Added integration test --- .../spring/command/app/CmdApplication.java | 2 +- .../app/{Config.java => CmdConfig.java} | 6 +- spring-boot/pom.xml | 2 +- spring-boot/query/pom.xml | 153 +++++++++++++++++- .../example/spring/query/app/Config.java | 128 --------------- .../spring/query/app/QryApplication.java | 19 ++- .../example/spring/query/app/QryConfig.java | 134 +++++++++++++++ .../spring/query/handler/PersonProjector.java | 7 + ...s4jSpringQueryExampleApplicationTests.java | 13 -- .../spring/query/api/PersonControllerIT.java | 109 +++++++++++++ 10 files changed, 411 insertions(+), 162 deletions(-) rename spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/{Config.java => CmdConfig.java} (94%) delete mode 100644 spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/Config.java create mode 100644 spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryConfig.java delete mode 100644 spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/Cqrs4jSpringQueryExampleApplicationTests.java create mode 100644 spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/api/PersonControllerIT.java diff --git a/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdApplication.java b/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdApplication.java index 282eb0c..3c75f17 100644 --- a/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdApplication.java +++ b/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdApplication.java @@ -46,7 +46,7 @@ public class CmdApplication { * @return New event store instance. */ @Bean(destroyMethod = "shutdown") - public com.github.msemys.esjc.EventStore getESHttpEventStore(final Config config) { + public com.github.msemys.esjc.EventStore getESHttpEventStore(final CmdConfig config) { return EventStoreBuilder.newBuilder().singleNodeAddress(config.getEventStoreHost(), config.getEventStoreTcpPort()) .executor(Executors.newFixedThreadPool(10)).userCredentials(config.getEventStoreUser(), config.getEventStorePassword()) .build(); diff --git a/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/Config.java b/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdConfig.java similarity index 94% rename from spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/Config.java rename to spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdConfig.java index 87832a4..ce8e41a 100644 --- a/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/Config.java +++ b/spring-boot/command/src/main/java/org/fuin/cqrs4j/example/spring/command/app/CmdConfig.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component -public class Config { +public class CmdConfig { private static final String EVENT_STORE_PROTOCOL = "http"; @@ -39,7 +39,7 @@ public class Config { /** * Constructor using default values internally. */ - public Config() { + public CmdConfig() { super(); this.eventStoreProtocol = EVENT_STORE_PROTOCOL; this.eventStoreHost = EVENT_STORE_HOST; @@ -59,7 +59,7 @@ public class Config { * @param eventStoreUser User. * @param eventStorePassword Password. */ - public Config(final String eventStoreProtocol, final String eventStoreHost, final int eventStoreHttpPort, + public CmdConfig(final String eventStoreProtocol, final String eventStoreHost, final int eventStoreHttpPort, final int eventStoreTcpPort, final String eventStoreUser, final String eventStorePassword) { super(); this.eventStoreProtocol = eventStoreProtocol; diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 265c219..f2125a7 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -11,7 +11,7 @@ shared query - + command diff --git a/spring-boot/query/pom.xml b/spring-boot/query/pom.xml index 7503fe8..1cd243b 100644 --- a/spring-boot/query/pom.xml +++ b/spring-boot/query/pom.xml @@ -22,6 +22,8 @@ 1.8 1.8 0.3.1-SNAPSHOT + 2.22.1 + 2.22.2 @@ -87,12 +89,6 @@ true - - com.h2database - h2 - runtime - - @@ -100,6 +96,10 @@ spring-boot-starter-test test + + junit + junit + org.junit.vintage junit-vintage-engine @@ -107,15 +107,156 @@ + + org.junit.jupiter + junit-jupiter-api + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + io.rest-assured + rest-assured + test + + + + io.rest-assured + spring-mock-mvc + test + + + + org.awaitility + awaitility + test + + + + org.springframework.boot spring-boot-maven-plugin + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + + integration-test + verify + + + + + + + io.fabric8 + docker-maven-plugin + 0.31.0 + + + + + eventstore/eventstore:release-4.1.3 + + + bridge + + + 1113:1113 + 2113:2113 + + + false + + + + http://localhost:2113/web/index.html#/ + GET + + + + + + + + mariadb:10.4 + + + bridge + + + 3306:3306 + + + false + + + 1 + xyz + querydb + mary + abc + + + + + + + + + + + + + start-images + pre-integration-test + + start + + + + stop-images + post-integration-test + + stop + + + + + + + + + + + sonatype.oss.snapshots + Sonatype OSS Snapshot Repository + https://oss.sonatype.org/content/repositories/snapshots + + false + + + always + true + + + + + diff --git a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/Config.java b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/Config.java deleted file mode 100644 index 9730873..0000000 --- a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/Config.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.fuin.cqrs4j.example.spring.query.app; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -public class Config { - - private static final String EVENT_STORE_PROTOCOL = "http"; - - private static final String EVENT_STORE_HOST = "localhost"; - - private static final int EVENT_STORE_HTTP_PORT = 2113; - - private static final int EVENT_STORE_TCP_PORT = 1113; - - private static final String EVENT_STORE_USER = "admin"; - - private static final String EVENT_STORE_PASSWORD = "changeit"; - - @Value("${EVENT_STORE_PROTOCOL:http}") - private String eventStoreProtocol; - - @Value("${EVENT_STORE_HOST:localhost}") - private String eventStoreHost; - - @Value("${EVENT_STORE_HTTP_PORT:2113}") - private int eventStoreHttpPort; - - @Value("${EVENT_STORE_TCP_PORT:1113}") - private int eventStoreTcpPort; - - @Value("${EVENT_STORE_USER:admin}") - private String eventStoreUser; - - @Value("${EVENT_STORE_PASSWORD:changeit}") - private String eventStorePassword; - - /** - * Constructor using default values internally. - */ - public Config() { - super(); - this.eventStoreProtocol = EVENT_STORE_PROTOCOL; - this.eventStoreHost = EVENT_STORE_HOST; - this.eventStoreHttpPort = EVENT_STORE_HTTP_PORT; - this.eventStoreTcpPort = EVENT_STORE_TCP_PORT; - this.eventStoreUser = EVENT_STORE_USER; - this.eventStorePassword = EVENT_STORE_PASSWORD; - } - - /** - * Constructor with all data. - * - * @param eventStoreProtocol Protocol. - * @param eventStoreHost Host. - * @param eventStoreHttpPort HTTP port - * @param eventStoreTcpPort TCP port. - * @param eventStoreUser User. - * @param eventStorePassword Password. - */ - public Config(final String eventStoreProtocol, final String eventStoreHost, final int eventStoreHttpPort, final int eventStoreTcpPort, - final String eventStoreUser, final String eventStorePassword) { - super(); - this.eventStoreProtocol = eventStoreProtocol; - this.eventStoreHost = eventStoreHost; - this.eventStoreHttpPort = eventStoreHttpPort; - this.eventStoreTcpPort = eventStoreTcpPort; - this.eventStoreUser = eventStoreUser; - this.eventStorePassword = eventStorePassword; - } - - /** - * Returns the protocol of the event store. - * - * @return Either http or https. - */ - public String getEventStoreProtocol() { - return eventStoreProtocol; - } - - /** - * Returns the host name of the event store. - * - * @return Name. - */ - public String getEventStoreHost() { - return eventStoreHost; - } - - /** - * Returns the HTTP port of the event store. - * - * @return Port. - */ - public int getEventStoreHttpPort() { - return eventStoreHttpPort; - } - - /** - * Returns the TCP port of the event store. - * - * @return Port. - */ - public int getEventStoreTcpPort() { - return eventStoreTcpPort; - } - - /** - * Returns the username of the event store. - * - * @return Username. - */ - public String getEventStoreUser() { - return eventStoreUser; - } - - /** - * Returns the password of the event store. - * - * @return Password. - */ - public String getEventStorePassword() { - return eventStorePassword; - } - -} - diff --git a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryApplication.java b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryApplication.java index 82c937b..7a30ff6 100644 --- a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryApplication.java +++ b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryApplication.java @@ -32,7 +32,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; "org.fuin.cqrs4j.example.spring.query.controller", "org.fuin.cqrs4j.example.spring.query.domain", "org.fuin.cqrs4j.example.spring.query.handler" }) @EnableJpaRepositories("org.fuin.cqrs4j.example.spring.query.domain") -@EntityScan({"org.fuin.cqrs4j.example.spring.query.domain", "org.fuin.cqrs4j.example.spring.query.handler"}) +@EntityScan({ "org.fuin.cqrs4j.example.spring.query.domain", "org.fuin.cqrs4j.example.spring.query.handler" }) @EnableScheduling @EnableAsync public class QryApplication { @@ -43,14 +43,13 @@ public class QryApplication { * @return Fully configured instance. */ @Bean - public Jsonb createJsonb() { - final JsonbConfig config = new JsonbConfig() - .withAdapters(SharedUtils.JSONB_ADAPTERS) - .withPropertyVisibilityStrategy(new FieldAccessStrategy()); - final Jsonb jsonb = JsonbBuilder.create(config); - return jsonb; - } - + public Jsonb createJsonb() { + final JsonbConfig config = new JsonbConfig().withAdapters(SharedUtils.JSONB_ADAPTERS) + .withPropertyVisibilityStrategy(new FieldAccessStrategy()); + final Jsonb jsonb = JsonbBuilder.create(config); + return jsonb; + } + /** * Creates a HTTP based event store connection. * @@ -59,7 +58,7 @@ public class QryApplication { * @return New event store instance. */ @Bean(destroyMethod = "close") - public IESHttpEventStore getESHttpEventStore(final Config config) { + public IESHttpEventStore getESHttpEventStore(final QryConfig config) { final String url = config.getEventStoreProtocol() + "://" + config.getEventStoreHost() + ":" + config.getEventStoreHttpPort(); try { diff --git a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryConfig.java b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryConfig.java new file mode 100644 index 0000000..bc48bea --- /dev/null +++ b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/app/QryConfig.java @@ -0,0 +1,134 @@ +package org.fuin.cqrs4j.example.spring.query.app; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class QryConfig { + + private static final String EVENT_STORE_PROTOCOL = "http"; + + private static final String EVENT_STORE_HOST = "localhost"; + + private static final int EVENT_STORE_HTTP_PORT = 2113; + + private static final int EVENT_STORE_TCP_PORT = 1113; + + private static final String EVENT_STORE_USER = "admin"; + + private static final String EVENT_STORE_PASSWORD = "changeit"; + + @Value("${EVENT_STORE_PROTOCOL:http}") + private String eventStoreProtocol; + + @Value("${EVENT_STORE_HOST:localhost}") + private String eventStoreHost; + + @Value("${EVENT_STORE_HTTP_PORT:2113}") + private int eventStoreHttpPort; + + @Value("${EVENT_STORE_TCP_PORT:1113}") + private int eventStoreTcpPort; + + @Value("${EVENT_STORE_USER:admin}") + private String eventStoreUser; + + @Value("${EVENT_STORE_PASSWORD:changeit}") + private String eventStorePassword; + + /** + * Constructor using default values internally. + */ + public QryConfig() { + super(); + this.eventStoreProtocol = EVENT_STORE_PROTOCOL; + this.eventStoreHost = EVENT_STORE_HOST; + this.eventStoreHttpPort = EVENT_STORE_HTTP_PORT; + this.eventStoreTcpPort = EVENT_STORE_TCP_PORT; + this.eventStoreUser = EVENT_STORE_USER; + this.eventStorePassword = EVENT_STORE_PASSWORD; + } + + /** + * Constructor with all data. + * + * @param eventStoreProtocol Protocol. + * @param eventStoreHost Host. + * @param eventStoreHttpPort HTTP port + * @param eventStoreTcpPort TCP port. + * @param eventStoreUser User. + * @param eventStorePassword Password. + */ + public QryConfig(final String eventStoreProtocol, final String eventStoreHost, final int eventStoreHttpPort, + final int eventStoreTcpPort, final String eventStoreUser, final String eventStorePassword) { + super(); + this.eventStoreProtocol = eventStoreProtocol; + this.eventStoreHost = eventStoreHost; + this.eventStoreHttpPort = eventStoreHttpPort; + this.eventStoreTcpPort = eventStoreTcpPort; + this.eventStoreUser = eventStoreUser; + this.eventStorePassword = eventStorePassword; + } + + /** + * Returns the protocol of the event store. + * + * @return Either http or https. + */ + public String getEventStoreProtocol() { + return eventStoreProtocol; + } + + /** + * Returns the host name of the event store. + * + * @return Name. + */ + public String getEventStoreHost() { + return eventStoreHost; + } + + /** + * Returns the HTTP port of the event store. + * + * @return Port. + */ + public int getEventStoreHttpPort() { + return eventStoreHttpPort; + } + + /** + * Returns the TCP port of the event store. + * + * @return Port. + */ + public int getEventStoreTcpPort() { + return eventStoreTcpPort; + } + + /** + * Returns the username of the event store. + * + * @return Username. + */ + public String getEventStoreUser() { + return eventStoreUser; + } + + /** + * Returns the password of the event store. + * + * @return Password. + */ + public String getEventStorePassword() { + return eventStorePassword; + } + + @Override + public String toString() { + return "QryConfig [eventStoreProtocol=" + eventStoreProtocol + ", eventStoreHost=" + eventStoreHost + + ", eventStoreHttpPort=" + eventStoreHttpPort + ", eventStoreTcpPort=" + eventStoreTcpPort + + ", eventStoreUser=" + eventStoreUser + "]"; + } + +} diff --git a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/handler/PersonProjector.java b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/handler/PersonProjector.java index c2446b1..9524642 100644 --- a/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/handler/PersonProjector.java +++ b/spring-boot/query/src/main/java/org/fuin/cqrs4j/example/spring/query/handler/PersonProjector.java @@ -9,6 +9,7 @@ import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PreDestroy; import javax.annotation.concurrent.ThreadSafe; import org.fuin.ddd4j.ddd.EventType; @@ -82,6 +83,12 @@ public class PersonProjector { APP_STARTED.set(true); } + @PreDestroy + public void destroy() { + APP_STARTED.set(false); + LOG.info("Application stopped"); + } + private void readStreamEvents() { // TODO Make sure a projection with the correct events exists diff --git a/spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/Cqrs4jSpringQueryExampleApplicationTests.java b/spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/Cqrs4jSpringQueryExampleApplicationTests.java deleted file mode 100644 index 3bc0d68..0000000 --- a/spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/Cqrs4jSpringQueryExampleApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.fuin.cqrs4j.example.spring.query; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class Cqrs4jSpringQueryExampleApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/api/PersonControllerIT.java b/spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/api/PersonControllerIT.java new file mode 100644 index 0000000..903569c --- /dev/null +++ b/spring-boot/query/src/test/java/org/fuin/cqrs4j/example/spring/query/api/PersonControllerIT.java @@ -0,0 +1,109 @@ +package org.fuin.cqrs4j.example.spring.query.api; + +import static io.restassured.RestAssured.given; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +import java.util.Arrays; +import java.util.UUID; + +import javax.persistence.EntityManager; + +import org.fuin.cqrs4j.example.spring.query.app.QryApplication; +import org.fuin.cqrs4j.example.spring.query.app.QryConfig; +import org.fuin.cqrs4j.example.spring.query.controller.PersonController; +import org.fuin.cqrs4j.example.spring.query.domain.QryPerson; +import org.fuin.cqrs4j.example.spring.shared.PersonCreatedEvent; +import org.fuin.cqrs4j.example.spring.shared.PersonId; +import org.fuin.cqrs4j.example.spring.shared.PersonName; +import org.fuin.esc.api.CommonEvent; +import org.fuin.esc.api.EventId; +import org.fuin.esc.api.EventStore; +import org.fuin.esc.api.SimpleCommonEvent; +import org.fuin.esc.api.SimpleStreamId; +import org.fuin.esc.api.TypeName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.context.WebApplicationContext; + +import io.restassured.RestAssured; +import io.restassured.module.mockmvc.RestAssuredMockMvc; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = QryApplication.class) +public class PersonControllerIT { + + @LocalServerPort + int port; + + @Autowired + WebApplicationContext wac; + + @Autowired + EventStore eventStore; + + @Autowired + EntityManager em; + + @Autowired + PersonController testee; + + @Autowired + QryConfig config; + + @BeforeEach + public void initRestAssuredMockMvcStandalone() { + RestAssured.port = port; + RestAssuredMockMvc.webAppContextSetup(wac); + } + + @Test + public void testGetByIdNotFound() { + given().pathParam("id", UUID.randomUUID()).when().get("/persons/{id}").then().statusCode(404); + } + + public boolean findPerson(final PersonId personId) { + return em.find(QryPerson.class, personId.asString()) != null; + } + + @Test + public void testGetByIdOK() { + + // PREPARE + final PersonId personId = new PersonId(UUID.randomUUID()); + final PersonName personName = new PersonName("Peter Parker " + personId); + final SimpleStreamId personStreamId = new SimpleStreamId(PersonId.TYPE + "-" + personId); + final PersonCreatedEvent event = new PersonCreatedEvent(personId, personName); + final CommonEvent ce = new SimpleCommonEvent(new EventId(event.getEventId().asBaseType()), + new TypeName(event.getEventType().asBaseType()), event); + eventStore.appendToStream(personStreamId, ce); + + await().atMost(5, SECONDS).until(() -> findPerson(personId)); + + // TEST & VERIFY + + final QryPerson person = given().pathParam("id", personId.asString()).when().get("/persons/{id}").then() + .statusCode(200).extract().as(QryPerson.class); + assertThat(person.getId(), is(equalTo(personId))); + assertThat(person.getName(), is(equalTo(personName))); + + final QryPerson[] persons = given().when().get("/persons").then().statusCode(200).extract() + .as(QryPerson[].class); + + assertThat(Arrays.asList(persons), is(not(empty()))); + final QryPerson person0 = persons[0]; + assertThat(person0.getId(), is(equalTo(personId))); + assertThat(person0.getName(), is(equalTo(personName))); + + } + +}