From 72f4cf3783e137e165db4f83ea0124f1c6ace372 Mon Sep 17 00:00:00 2001 From: Pavel Vetokhin Date: Mon, 4 Mar 2024 21:11:27 +0300 Subject: [PATCH] Add view endpoint and test --- .../bezmen/messaging/SepulkaClientIT.java | 21 +++++++++ .../bezmen/core/SepulkaServiceImpl.java | 10 ++-- .../bezmen/messaging/SepulkaClientImpl.java | 10 ++++ .../springmvc/SepulkaController.java | 18 ++++++-- .../bezmen/messaging/SepulkaClientIT.kt | 21 +++++++++ .../bezmen/core/SepulkaServiceImpl.kt | 8 ++-- .../bezmen/messaging/SepulkaClientImpl.kt | 9 ++++ .../messaging/springmvc/SepulkaController.kt | 20 ++++++-- .../bezmen/messaging/SepulkaClient.java | 4 ++ .../bezmen/messaging/SepulkaMessageEm.java | 15 ++++++ .../bezmen/messaging/SepulkaMessageEmEg.java | 26 +++++++++++ .../bezmen/core/SepulkaMessageDm.java | 4 +- .../bezmen/core/SepulkaMessageDmEg.java | 9 ++++ .../bezmen/core/SepulkaService.java | 7 ++- lib/essentials/pom.xml | 7 ++- lib/messaging-client/pom.xml | 8 ++-- .../messaging/BezmenClientJavaHttp.java | 27 +++++++++-- .../messaging/SepulkaMessageMapper.java | 4 ++ .../bezmen/construction/StandBeans.java | 23 ++++++++++ .../{ToySuite.java => ResilienceSuite.java} | 4 +- .../bezmen/registration/SepulkaTest.java | 13 ------ .../resilience/AbstractResilienceTest.java | 37 +++++++++++++++ .../bezmen/resilience/SepulkaTest.java | 43 +++++++++++++++++ .../src/test/resources/docker-java.properties | 0 tool/pom.xml | 17 +++++++ tool/testing/pom.xml | 13 +++++- .../messaging/SepulkaClientSpringWebTest.java | 13 ++++++ .../smecalculus/bezmen/testing/Demiurge.java | 46 +++++++++++++++++++ 28 files changed, 392 insertions(+), 45 deletions(-) rename test/e2e/src/test/java/smecalculus/bezmen/{ToySuite.java => ResilienceSuite.java} (61%) create mode 100644 test/e2e/src/test/java/smecalculus/bezmen/resilience/AbstractResilienceTest.java create mode 100644 test/e2e/src/test/java/smecalculus/bezmen/resilience/SepulkaTest.java create mode 100644 test/e2e/src/test/resources/docker-java.properties create mode 100644 tool/testing/src/main/java/smecalculus/bezmen/testing/Demiurge.java diff --git a/app/sepuling-java/src/it/java/smecalculus/bezmen/messaging/SepulkaClientIT.java b/app/sepuling-java/src/it/java/smecalculus/bezmen/messaging/SepulkaClientIT.java index 246f2658..287c47ff 100644 --- a/app/sepuling-java/src/it/java/smecalculus/bezmen/messaging/SepulkaClientIT.java +++ b/app/sepuling-java/src/it/java/smecalculus/bezmen/messaging/SepulkaClientIT.java @@ -11,6 +11,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import smecalculus.bezmen.construction.SepulkaClientBeans; +import smecalculus.bezmen.core.SepulkaMessageDm; import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationRequest; import smecalculus.bezmen.core.SepulkaMessageDmEg; import smecalculus.bezmen.core.SepulkaService; @@ -44,4 +45,24 @@ void shouldRegisterSepulka() { .ignoringExpectedNullFields() .isEqualTo(expectedResponse); } + + @Test + void shouldViewSepulka() { + // given + var externalId = UUID.randomUUID().toString(); + // and + var request = SepulkaMessageEmEg.viewRequest(externalId); + // and + when(serviceMock.view(any(SepulkaMessageDm.ViewRequest.class))) + .thenReturn(SepulkaMessageDmEg.viewResponse(externalId).build()); + // and + var expectedResponse = SepulkaMessageEmEg.viewResponse(externalId); + // when + var actualResponse = externalClient.view(request); + // then + assertThat(actualResponse) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expectedResponse); + } } diff --git a/app/sepuling-java/src/main/java/smecalculus/bezmen/core/SepulkaServiceImpl.java b/app/sepuling-java/src/main/java/smecalculus/bezmen/core/SepulkaServiceImpl.java index e1a8a837..1e931d6a 100644 --- a/app/sepuling-java/src/main/java/smecalculus/bezmen/core/SepulkaServiceImpl.java +++ b/app/sepuling-java/src/main/java/smecalculus/bezmen/core/SepulkaServiceImpl.java @@ -3,14 +3,12 @@ import static java.util.UUID.randomUUID; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import smecalculus.bezmen.core.SepulkaMessageDm.PreviewRequest; -import smecalculus.bezmen.core.SepulkaMessageDm.PreviewResponse; import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationRequest; import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationResponse; +import smecalculus.bezmen.core.SepulkaMessageDm.ViewRequest; +import smecalculus.bezmen.core.SepulkaMessageDm.ViewResponse; import smecalculus.bezmen.storage.SepulkaDao; @RequiredArgsConstructor @@ -37,7 +35,7 @@ public RegistrationResponse register(RegistrationRequest request) { } @Override - public List view(PreviewRequest request) { - return Collections.emptyList(); + public ViewResponse view(ViewRequest request) { + return new ViewResponse(request.externalId()); } } diff --git a/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/SepulkaClientImpl.java b/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/SepulkaClientImpl.java index be2502c7..2e2cef20 100644 --- a/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/SepulkaClientImpl.java +++ b/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/SepulkaClientImpl.java @@ -5,6 +5,8 @@ import smecalculus.bezmen.core.SepulkaService; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse; import smecalculus.bezmen.validation.EdgeValidator; @RequiredArgsConstructor @@ -26,4 +28,12 @@ public RegistrationResponse register(RegistrationRequest requestEdge) { var response = service.register(request); return mapper.toEdge(response); } + + @Override + public ViewResponse view(ViewRequest requestEdge) { + validator.validate(requestEdge); + var request = mapper.toDomain(requestEdge); + var response = service.view(request); + return mapper.toEdge(response); + } } diff --git a/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/springmvc/SepulkaController.java b/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/springmvc/SepulkaController.java index d206cbdf..3c71e81c 100644 --- a/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/springmvc/SepulkaController.java +++ b/app/sepuling-java/src/main/java/smecalculus/bezmen/messaging/springmvc/SepulkaController.java @@ -4,13 +4,17 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import smecalculus.bezmen.messaging.SepulkaClient; +import smecalculus.bezmen.messaging.SepulkaMessageEm; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse; @RestController @RequestMapping("sepulkas") @@ -21,8 +25,16 @@ public class SepulkaController { private SepulkaClient client; @PostMapping - ResponseEntity register(@RequestBody RegistrationRequest requestEdge) { - var responseEdge = client.register(requestEdge); - return ResponseEntity.status(HttpStatus.CREATED).body(responseEdge); + ResponseEntity register(@RequestBody RegistrationRequest request) { + var response = client.register(request); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @GetMapping("/{externalId}") + ResponseEntity view(@PathVariable("externalId") String externalId) { + var request = new SepulkaMessageEm.ViewRequest(); + request.setExternalId(externalId); + var response = client.view(request); + return ResponseEntity.status(HttpStatus.OK).body(response); } } diff --git a/app/sepuling-kotlin/src/it/kotlin/smecalculus/bezmen/messaging/SepulkaClientIT.kt b/app/sepuling-kotlin/src/it/kotlin/smecalculus/bezmen/messaging/SepulkaClientIT.kt index 53636164..8a1d6143 100644 --- a/app/sepuling-kotlin/src/it/kotlin/smecalculus/bezmen/messaging/SepulkaClientIT.kt +++ b/app/sepuling-kotlin/src/it/kotlin/smecalculus/bezmen/messaging/SepulkaClientIT.kt @@ -17,6 +17,7 @@ import java.util.UUID @ExtendWith(SpringExtension::class) @ContextConfiguration(classes = [SepulkaClientBeans::class]) abstract class SepulkaClientIT { + @Autowired private lateinit var externalClient: SepulkaClient @@ -42,4 +43,24 @@ abstract class SepulkaClientIT { .ignoringExpectedNullFields() .isEqualTo(expectedResponse) } + + @Test + fun shouldViewSepulka() { + // given + val externalId = UUID.randomUUID().toString() + // and + val request = SepulkaMessageEmEg.viewRequest(externalId) + // and + whenever(serviceMock.view(any(SepulkaMessageDm.ViewRequest::class.java))) + .thenReturn(SepulkaMessageDmEg.viewResponse(externalId).build()) + // and + val expectedResponse = SepulkaMessageEmEg.viewResponse(externalId) + // when + val actualResponse = externalClient.view(request) + // then + assertThat(actualResponse) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expectedResponse) + } } diff --git a/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/core/SepulkaServiceImpl.kt b/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/core/SepulkaServiceImpl.kt index 8c8bb3bb..da53937d 100644 --- a/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/core/SepulkaServiceImpl.kt +++ b/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/core/SepulkaServiceImpl.kt @@ -1,9 +1,9 @@ package smecalculus.bezmen.core -import smecalculus.bezmen.core.SepulkaMessageDm.PreviewRequest -import smecalculus.bezmen.core.SepulkaMessageDm.PreviewResponse import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationRequest import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationResponse +import smecalculus.bezmen.core.SepulkaMessageDm.ViewRequest +import smecalculus.bezmen.core.SepulkaMessageDm.ViewResponse import smecalculus.bezmen.storage.SepulkaDao import java.time.LocalDateTime import java.util.UUID @@ -25,7 +25,7 @@ class SepulkaServiceImpl( return converter.toMessage(sepulkaSaved).build() } - override fun view(request: PreviewRequest): List { - return listOf() + override fun view(request: ViewRequest): ViewResponse { + return ViewResponse(request.externalId) } } diff --git a/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/SepulkaClientImpl.kt b/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/SepulkaClientImpl.kt index 4e1ed257..458c0937 100644 --- a/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/SepulkaClientImpl.kt +++ b/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/SepulkaClientImpl.kt @@ -3,6 +3,8 @@ package smecalculus.bezmen.messaging import smecalculus.bezmen.core.SepulkaService import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse import smecalculus.bezmen.validation.EdgeValidator class SepulkaClientImpl( @@ -16,4 +18,11 @@ class SepulkaClientImpl( val response = service.register(request) return mapper.toEdge(response) } + + override fun view(requestEdge: ViewRequest): ViewResponse { + validator.validate(requestEdge) + val request = mapper.toDomain(requestEdge) + val response = service.view(request) + return mapper.toEdge(response) + } } diff --git a/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/springmvc/SepulkaController.kt b/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/springmvc/SepulkaController.kt index 598b6e17..18b458df 100644 --- a/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/springmvc/SepulkaController.kt +++ b/app/sepuling-kotlin/src/main/kotlin/smecalculus/bezmen/messaging/springmvc/SepulkaController.kt @@ -2,6 +2,8 @@ package smecalculus.bezmen.messaging.springmvc import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -9,6 +11,8 @@ import org.springframework.web.bind.annotation.RestController import smecalculus.bezmen.messaging.SepulkaClient import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse @RestController @RequestMapping("sepulkas") @@ -17,9 +21,19 @@ class SepulkaController( ) { @PostMapping fun register( - @RequestBody requestEdge: RegistrationRequest, + @RequestBody request: RegistrationRequest, ): ResponseEntity { - val responseEdge = client.register(requestEdge) - return ResponseEntity.status(HttpStatus.CREATED).body(responseEdge) + val response = client.register(request) + return ResponseEntity.status(HttpStatus.CREATED).body(response) + } + + @GetMapping("/{externalId}") + fun register( + @PathVariable("externalId") externalId: String, + ): ResponseEntity { + val request = ViewRequest() + request.externalId = externalId + val response = client.view(request) + return ResponseEntity.status(HttpStatus.OK).body(response) } } diff --git a/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaClient.java b/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaClient.java index 853767e5..4b5ff446 100644 --- a/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaClient.java +++ b/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaClient.java @@ -2,10 +2,14 @@ import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse; /** * Port: client side */ public interface SepulkaClient { RegistrationResponse register(RegistrationRequest request); + + ViewResponse view(ViewRequest request); } diff --git a/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEm.java b/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEm.java index c1c750c5..56bd1669 100644 --- a/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEm.java +++ b/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEm.java @@ -14,8 +14,23 @@ public static class RegistrationRequest { @Data public static class RegistrationResponse { + String externalId; + } + + @Data + public static class SearchResponse { + String externalId; + } + + @Data + public static class ViewRequest { @NotNull @Size(min = 1, max = 64) String externalId; } + + @Data + public static class ViewResponse { + String externalId; + } } diff --git a/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEmEg.java b/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEmEg.java index a0b6f020..9d0babd8 100644 --- a/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEmEg.java +++ b/lib/abstraction-client/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageEmEg.java @@ -3,6 +3,8 @@ import java.util.UUID; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse; public abstract class SepulkaMessageEmEg { public static RegistrationRequest registrationRequest() { @@ -28,4 +30,28 @@ public static RegistrationResponse registrationResponse(String externalId) { responseEdge.setExternalId(externalId); return responseEdge; } + + public static ViewRequest viewRequest() { + var requestEdge = new ViewRequest(); + requestEdge.setExternalId(UUID.randomUUID().toString()); + return requestEdge; + } + + public static ViewRequest viewRequest(String id) { + var requestEdge = viewRequest(); + requestEdge.setExternalId(id); + return requestEdge; + } + + public static ViewResponse viewResponse() { + var responseEdge = new ViewResponse(); + responseEdge.setExternalId(UUID.randomUUID().toString()); + return responseEdge; + } + + public static ViewResponse viewResponse(String externalId) { + var responseEdge = viewResponse(); + responseEdge.setExternalId(externalId); + return responseEdge; + } } diff --git a/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDm.java b/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDm.java index e0777a97..5e127cd7 100644 --- a/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDm.java +++ b/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDm.java @@ -11,8 +11,8 @@ public record RegistrationRequest(@NonNull String externalId) {} public record RegistrationResponse(@NonNull String externalId) {} @Builder - public record PreviewRequest(@NonNull String externalId) {} + public record ViewRequest(@NonNull String externalId) {} @Builder - public record PreviewResponse(@NonNull String externalId) {} + public record ViewResponse(@NonNull String externalId) {} } diff --git a/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDmEg.java b/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDmEg.java index febb5495..8f277c73 100644 --- a/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDmEg.java +++ b/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaMessageDmEg.java @@ -2,6 +2,7 @@ import java.util.UUID; import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationResponse; +import smecalculus.bezmen.core.SepulkaMessageDm.ViewResponse; public class SepulkaMessageDmEg { public static RegistrationResponse.Builder registrationResponse() { @@ -11,4 +12,12 @@ public static RegistrationResponse.Builder registrationResponse() { public static RegistrationResponse.Builder registrationResponse(String externalId) { return registrationResponse().externalId(externalId); } + + public static ViewResponse.Builder viewResponse() { + return ViewResponse.builder().externalId(UUID.randomUUID().toString()); + } + + public static ViewResponse.Builder viewResponse(String externalId) { + return viewResponse().externalId(externalId); + } } diff --git a/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaService.java b/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaService.java index c60e69a6..21766fad 100644 --- a/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaService.java +++ b/lib/abstraction/src/main/java/smecalculus/bezmen/core/SepulkaService.java @@ -1,13 +1,12 @@ package smecalculus.bezmen.core; -import java.util.List; -import smecalculus.bezmen.core.SepulkaMessageDm.PreviewRequest; -import smecalculus.bezmen.core.SepulkaMessageDm.PreviewResponse; import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationRequest; import smecalculus.bezmen.core.SepulkaMessageDm.RegistrationResponse; +import smecalculus.bezmen.core.SepulkaMessageDm.ViewRequest; +import smecalculus.bezmen.core.SepulkaMessageDm.ViewResponse; public interface SepulkaService { RegistrationResponse register(RegistrationRequest request); - List view(PreviewRequest request); + ViewResponse view(ViewRequest request); } diff --git a/lib/essentials/pom.xml b/lib/essentials/pom.xml index cc19bd93..4b4e9ca9 100644 --- a/lib/essentials/pom.xml +++ b/lib/essentials/pom.xml @@ -14,12 +14,17 @@ jar - + ${project.groupId} abstraction compile + + ${project.groupId} + testing + test + com.fasterxml.jackson.core diff --git a/lib/messaging-client/pom.xml b/lib/messaging-client/pom.xml index 8d124ccf..d8ca932c 100644 --- a/lib/messaging-client/pom.xml +++ b/lib/messaging-client/pom.xml @@ -14,19 +14,21 @@ jar - ${project.groupId} abstraction-client compile - com.fasterxml.jackson.core jackson-databind compile - + + org.slf4j + slf4j-api + compile + ${project.groupId} testing diff --git a/lib/messaging-client/src/main/java/smecalculus/bezmen/messaging/BezmenClientJavaHttp.java b/lib/messaging-client/src/main/java/smecalculus/bezmen/messaging/BezmenClientJavaHttp.java index 7d593192..552ee5dc 100644 --- a/lib/messaging-client/src/main/java/smecalculus/bezmen/messaging/BezmenClientJavaHttp.java +++ b/lib/messaging-client/src/main/java/smecalculus/bezmen/messaging/BezmenClientJavaHttp.java @@ -1,6 +1,5 @@ package smecalculus.bezmen.messaging; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.net.URI; @@ -13,6 +12,8 @@ import smecalculus.bezmen.configuration.ClientProps; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse; @RequiredArgsConstructor public class BezmenClientJavaHttp implements BezmenClient { @@ -38,9 +39,29 @@ public RegistrationResponse register(RegistrationRequest request) { .header("Accept", "application/json") .build(); var httpResponse = client.send(httpRequest, BodyHandlers.ofString()); - return mapper.readValue(httpResponse.body(), RegistrationResponse.class); - } catch (JsonProcessingException e) { + if (httpResponse.statusCode() < 300) { + return mapper.readValue(httpResponse.body(), RegistrationResponse.class); + } + throw new RuntimeException(httpResponse.body()); + } catch (IOException | InterruptedException e) { throw new RuntimeException(e); + } + } + + @Override + public ViewResponse view(ViewRequest request) { + URI uri = URI.create("http://" + props.host() + ":" + props.port() + "/sepulkas/" + request.getExternalId()); + try { + var httpRequest = HttpRequest.newBuilder() + .uri(uri) + .GET() + .header("Accept", "application/json") + .build(); + var httpResponse = client.send(httpRequest, BodyHandlers.ofString()); + if (httpResponse.statusCode() < 300) { + return mapper.readValue(httpResponse.body(), ViewResponse.class); + } + throw new RuntimeException(httpResponse.body()); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } diff --git a/lib/messaging/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageMapper.java b/lib/messaging/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageMapper.java index 09dbb786..2b73eede 100644 --- a/lib/messaging/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageMapper.java +++ b/lib/messaging/src/main/java/smecalculus/bezmen/messaging/SepulkaMessageMapper.java @@ -8,4 +8,8 @@ public interface SepulkaMessageMapper { SepulkaMessageDm.RegistrationRequest toDomain(SepulkaMessageEm.RegistrationRequest request); SepulkaMessageEm.RegistrationResponse toEdge(SepulkaMessageDm.RegistrationResponse response); + + SepulkaMessageDm.ViewRequest toDomain(SepulkaMessageEm.ViewRequest request); + + SepulkaMessageEm.ViewResponse toEdge(SepulkaMessageDm.ViewResponse response); } diff --git a/test/e2e/src/main/java/smecalculus/bezmen/construction/StandBeans.java b/test/e2e/src/main/java/smecalculus/bezmen/construction/StandBeans.java index 1cab6e2d..73e80191 100644 --- a/test/e2e/src/main/java/smecalculus/bezmen/construction/StandBeans.java +++ b/test/e2e/src/main/java/smecalculus/bezmen/construction/StandBeans.java @@ -1,6 +1,12 @@ package smecalculus.bezmen.construction; +import static com.fasterxml.jackson.core.JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION; + import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import java.net.http.HttpClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -9,6 +15,7 @@ import smecalculus.bezmen.configuration.TestingProps; import smecalculus.bezmen.messaging.BezmenClient; import smecalculus.bezmen.messaging.BezmenClientJavaHttp; +import smecalculus.bezmen.testing.Demiurge; @Configuration(proxyBeanMethods = false) public class StandBeans { @@ -23,7 +30,23 @@ ClientProps clientProps(Environment environment) { @Bean BezmenClient bezmenClient(ClientProps clientProps) { var jsonMapper = new ObjectMapper(); + jsonMapper.enable(INCLUDE_SOURCE_IN_LOCATION); var httpClient = HttpClient.newHttpClient(); return new BezmenClientJavaHttp(clientProps, jsonMapper, httpClient); } + + @Bean + DockerClient dockerClient() { + var clientConfig = + DefaultDockerClientConfig.createDefaultConfigBuilder().build(); + var httpClient = new ApacheDockerHttpClient.Builder() + .dockerHost(clientConfig.getDockerHost()) + .build(); + return DockerClientImpl.getInstance(clientConfig, httpClient); + } + + @Bean + Demiurge demiurge(DockerClient dockerClient) { + return new Demiurge(dockerClient); + } } diff --git a/test/e2e/src/test/java/smecalculus/bezmen/ToySuite.java b/test/e2e/src/test/java/smecalculus/bezmen/ResilienceSuite.java similarity index 61% rename from test/e2e/src/test/java/smecalculus/bezmen/ToySuite.java rename to test/e2e/src/test/java/smecalculus/bezmen/ResilienceSuite.java index 7c061d0f..34a0c736 100644 --- a/test/e2e/src/test/java/smecalculus/bezmen/ToySuite.java +++ b/test/e2e/src/test/java/smecalculus/bezmen/ResilienceSuite.java @@ -4,5 +4,5 @@ import org.junit.platform.suite.api.Suite; @Suite -@SelectPackages("smecalculus.bezmen.registration") -public class ToySuite {} +@SelectPackages("smecalculus.bezmen.resilience") +public class ResilienceSuite {} diff --git a/test/e2e/src/test/java/smecalculus/bezmen/registration/SepulkaTest.java b/test/e2e/src/test/java/smecalculus/bezmen/registration/SepulkaTest.java index bbdb09e1..c0be9bb6 100644 --- a/test/e2e/src/test/java/smecalculus/bezmen/registration/SepulkaTest.java +++ b/test/e2e/src/test/java/smecalculus/bezmen/registration/SepulkaTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; @@ -40,16 +39,4 @@ void shouldRegisterSepulka() { // then assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); } - - @Test - @EnabledIfSystemProperty(named = "storage.protocol.mode", matches = "postgres") - void postgresSpecificTest() { - // empty - } - - @Test - @EnabledIfSystemProperty(named = "storage.protocol.mode", matches = "sqlite") - void sqliteSpecificTest() { - // empty - } } diff --git a/test/e2e/src/test/java/smecalculus/bezmen/resilience/AbstractResilienceTest.java b/test/e2e/src/test/java/smecalculus/bezmen/resilience/AbstractResilienceTest.java new file mode 100644 index 00000000..d19e3124 --- /dev/null +++ b/test/e2e/src/test/java/smecalculus/bezmen/resilience/AbstractResilienceTest.java @@ -0,0 +1,37 @@ +package smecalculus.bezmen.resilience; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Container; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import smecalculus.bezmen.construction.StandBeans; +import smecalculus.bezmen.testing.Demiurge; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = StandBeans.class) +public abstract class AbstractResilienceTest { + + @Autowired + protected DockerClient dockerClient; + + @Autowired + protected Demiurge demiurge; + + protected Container postgresPrimary; + protected Container postgresSecondary; + + @BeforeAll + void abstractBeforeAll() { + var containers = dockerClient.listContainersCmd().withShowAll(true).exec().stream() + .collect(toMap(container -> container.getNames()[0], identity())); + containers.values().forEach(demiurge::starts); + postgresPrimary = containers.get("/bezmen-postgres-primary"); + postgresSecondary = containers.get("/bezmen-postgres-secondary"); + } +} diff --git a/test/e2e/src/test/java/smecalculus/bezmen/resilience/SepulkaTest.java b/test/e2e/src/test/java/smecalculus/bezmen/resilience/SepulkaTest.java new file mode 100644 index 00000000..ddf8b225 --- /dev/null +++ b/test/e2e/src/test/java/smecalculus/bezmen/resilience/SepulkaTest.java @@ -0,0 +1,43 @@ +package smecalculus.bezmen.resilience; + +import static java.time.Duration.ofSeconds; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static smecalculus.bezmen.messaging.SepulkaMessageEmEg.registrationRequest; +import static smecalculus.bezmen.messaging.SepulkaMessageEmEg.viewRequest; + +import java.util.UUID; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import smecalculus.bezmen.messaging.BezmenClient; +import smecalculus.bezmen.messaging.SepulkaMessageEmEg; + +public class SepulkaTest extends AbstractResilienceTest { + + @Autowired + private BezmenClient bezmenClient; + + @BeforeAll + void beforeAll() { + await("isReady").atMost(ofSeconds(5)).until(bezmenClient::isReady); + } + + @Test + void shouldViewSepulka() { + // given + String externalId = UUID.randomUUID().toString(); + // and + var registrationRequest = registrationRequest(externalId); + // and + bezmenClient.register(registrationRequest(externalId)); + // and + demiurge.kills(postgresPrimary); + // and + var expectedResponse = SepulkaMessageEmEg.viewResponse(registrationRequest.getExternalId()); + // when + var actualResponse = bezmenClient.view(viewRequest(externalId)); + // then + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + } +} diff --git a/test/e2e/src/test/resources/docker-java.properties b/test/e2e/src/test/resources/docker-java.properties new file mode 100644 index 00000000..e69de29b diff --git a/tool/pom.xml b/tool/pom.xml index b673420e..fd6ece70 100644 --- a/tool/pom.xml +++ b/tool/pom.xml @@ -37,6 +37,13 @@ lombok ${lombok.version} + + com.github.docker-java + docker-java-bom + 3.3.6 + import + pom + org.junit junit-bom @@ -63,6 +70,16 @@ import pom + + org.slf4j + slf4j-api + 2.0.12 + + + com.fasterxml.jackson.core + jackson-annotations + 2.16.1 + jakarta.servlet jakarta.servlet-api diff --git a/tool/testing/pom.xml b/tool/testing/pom.xml index 93818358..45e9e1c6 100644 --- a/tool/testing/pom.xml +++ b/tool/testing/pom.xml @@ -47,6 +47,17 @@ compile + + com.github.docker-java + docker-java-core + compile + + + com.github.docker-java + docker-java-transport-httpclient5 + compile + + org.junit.jupiter junit-jupiter-api @@ -81,7 +92,7 @@ org.awaitility awaitility - runtime + compile diff --git a/tool/testing/src/main/java/smecalculus/bezmen/messaging/SepulkaClientSpringWebTest.java b/tool/testing/src/main/java/smecalculus/bezmen/messaging/SepulkaClientSpringWebTest.java index e56297ea..fd09a39e 100644 --- a/tool/testing/src/main/java/smecalculus/bezmen/messaging/SepulkaClientSpringWebTest.java +++ b/tool/testing/src/main/java/smecalculus/bezmen/messaging/SepulkaClientSpringWebTest.java @@ -6,6 +6,8 @@ import org.springframework.test.web.reactive.server.WebTestClient; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationRequest; import smecalculus.bezmen.messaging.SepulkaMessageEm.RegistrationResponse; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewRequest; +import smecalculus.bezmen.messaging.SepulkaMessageEm.ViewResponse; @RequiredArgsConstructor public class SepulkaClientSpringWebTest implements SepulkaClient { @@ -25,4 +27,15 @@ public RegistrationResponse register(RegistrationRequest request) { .returnResult() .getResponseBody(); } + + @Override + public ViewResponse view(ViewRequest request) { + return client.get() + .uri("/sepulkas/" + request.getExternalId()) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectBody(ViewResponse.class) + .returnResult() + .getResponseBody(); + } } diff --git a/tool/testing/src/main/java/smecalculus/bezmen/testing/Demiurge.java b/tool/testing/src/main/java/smecalculus/bezmen/testing/Demiurge.java new file mode 100644 index 00000000..087ae4c3 --- /dev/null +++ b/tool/testing/src/main/java/smecalculus/bezmen/testing/Demiurge.java @@ -0,0 +1,46 @@ +package smecalculus.bezmen.testing; + +import static org.awaitility.Awaitility.await; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.exception.NotModifiedException; +import com.github.dockerjava.api.model.Container; +import java.time.Duration; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@RequiredArgsConstructor +public class Demiurge { + + private static final Logger LOG = LoggerFactory.getLogger(Demiurge.class); + + @NonNull + private DockerClient dockerClient; + + public void starts(@NonNull Container container) { + try { + dockerClient.startContainerCmd(container.getId()).exec(); + await().atMost(Duration.ofSeconds(20)).until(() -> isStarted(container)); + } catch (NotModifiedException e) { + LOG.debug("Already started"); + } + } + + public void kills(@NonNull Container container) { + dockerClient.killContainerCmd(container.getId()).exec(); + } + + private boolean isStarted(@NonNull Container container) { + var response = dockerClient.inspectContainerCmd(container.getId()).exec(); + if (response.getConfig().getHealthcheck() == null) { + return Boolean.TRUE.equals(response.getState().getRunning()); + } + var health = response.getState().getHealth(); + if (health == null) { + return false; + } + return health.getStatus().equalsIgnoreCase("healthy"); + } +}