diff --git a/pom.xml b/pom.xml index 86abf554db5dbbe339c6de49ccebfa67e275097b..1e8f3fd6fdbb1583cc30c11e234d0893b26b8498 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ <mockserver-client.version>5.15.0</mockserver-client.version> <openapi-generator.version>7.10.0</openapi-generator.version> <swagger-parser.version>2.1.23</swagger-parser.version> + <wiremock-spring-boot.version>3.6.0</wiremock-spring-boot.version> </properties> <dependencies> <!-- OZG-Cloud --> @@ -86,6 +87,13 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>org.wiremock.integrations</groupId> + <artifactId>wiremock-spring-boot</artifactId> + <version>${wiremock-spring-boot.version}</version> + </dependency> + + <!-- commons --> <dependency> <groupId>org.apache.commons</groupId> diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java index f75663dd923194920575f7de28395327bb6bd10f..d20919ea138d5e97cd18ce37d6b32738d96f50d1 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java @@ -1,25 +1,19 @@ package de.ozgcloud.nachrichten.postfach.osiv2; +import static com.github.tomakehurst.wiremock.client.WireMock.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.JwtFactory.*; import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockserver.mock.OpenAPIExpectation.*; -import static org.mockserver.model.HttpRequest.*; -import static org.mockserver.model.HttpResponse.*; import java.nio.file.Files; import java.nio.file.Path; import java.time.OffsetDateTime; import java.util.Arrays; -import java.util.Map; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.OpenAPIDefinition; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -27,6 +21,9 @@ import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; + import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.extension.Jwt; import de.ozgcloud.nachrichten.postfach.osiv2.extension.OsiMockServerExtension; @@ -35,10 +32,11 @@ import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFacto import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMessage; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMessagesResponse; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeSendMessageResponse; import lombok.SneakyThrows; @SpringBootTest(classes = TestApplication.class) -@ActiveProfiles({"stage", "itcase"}) +@ActiveProfiles({ "stage", "itcase" }) @TestPropertySource(properties = { "ozgcloud.osiv2-postfach.http-proxy.enabled=false", }) @@ -66,29 +64,40 @@ class OsiPostfachRemoteServiceITCase { registry.add("ozgcloud.osiv2-postfach.api.resource", () -> RESOURCE_URN); } - private MockServerClient postfachFacadeMockClient; + private WireMockServer postfachFacadeMockServer; + private WireMockServer serviceKontoMockServer; @BeforeEach @SneakyThrows public void setup() { - postfachFacadeMockClient = OSI_MOCK_SERVER_EXTENSION.getPostfachFacadeMockClient(); + postfachFacadeMockServer = OSI_MOCK_SERVER_EXTENSION.getPostfachFacadeMockServer(); + serviceKontoMockServer = OSI_MOCK_SERVER_EXTENSION.getServiceKontoMockServer(); } @DisplayName("should send request with jwt") @Test @SneakyThrows void shouldSendRequestWithJwt() { - mockOperationsAndResponses(Map.of( - "SendMessage", "200" - )); + // Stub message send response (MessageExchangeApi::sendMessage) + postfachFacadeMockServer.stubFor(post("/MessageExchange/v1/Send/{mailboxId}") + .willReturn(okJsonObj(new MessageExchangeSendMessageResponse().messageId(UUID.randomUUID()))) + ); osiPostfachRemoteService.sendMessage(postfachNachricht); - var requests = postfachFacadeMockClient.retrieveRecordedRequests(request()); - assertThat(requests).hasSize(1); - var jwt = Jwt.parseAuthHeaderValue( - requests[0].getHeader("Authorization").getFirst() + serviceKontoMockServer.verify( + exactly(1), + postRequestedFor(urlEqualTo(OSI_MOCK_SERVER_EXTENSION.getAccessTokenUrl())) + .withHeader("Content-Type", equalTo("application/x-www-form-urlencoded")) + .withQueryParam("grant_type", equalTo("client_credentials")) + .withQueryParam("client_id", equalTo(CLIENT_ID)) + .withQueryParam("scope", equalTo(String.join(" ", CLIENT_SCOPES))) + .withQueryParam("resource", equalTo(RESOURCE_URN)) ); + var requests = postfachFacadeMockServer.findAll( + postRequestedFor(urlPathEqualTo("/MessageExchange/v1/Send"))); + assertThat(requests).hasSize(1); + var jwt = Jwt.parseAuthHeaderValue(requests.getFirst().getHeader("Authorization")); assertThat(jwt.body().read("$.client_id", String.class)).isEqualTo(CLIENT_ID); assertThat(jwt.body().read("$.aud", String.class)).isEqualTo(RESOURCE_URN); } @@ -116,53 +125,54 @@ class OsiPostfachRemoteServiceITCase { } private void mockPostfachMessageAndResponse(final String... uuids) { - // Stub message listing response - mockJsonOperation("receiveMessages", new MessageExchangeReceiveMessagesResponse() - .messages(Arrays.stream(uuids) - .map(uuid -> new MessageExchangeReceiveMessage() - .guid(UUID.fromString(uuid))) - .toList())); + // Stub message listing response (MessageExchangeApi::receiveMessages) + postfachFacadeMockServer.stubFor(get("/MessageExchange/v1/Receive") + .willReturn( + okJsonObj( + new MessageExchangeReceiveMessagesResponse() + .messages(Arrays.stream(uuids) + .map(uuid -> new MessageExchangeReceiveMessage() + .guid(UUID.fromString(uuid))) + .toList()) + ) + ) + ); for (String uuid : uuids) { - // Stub individual response for message - mockJsonOperation("getMessage", V1ReplyMessageFactory.create() - .messageBox(UUID.fromString(uuid)) - .responseTime(OffsetDateTime.now())); + // Stub individual response for message (MessageExchangeApi::getMessage) + postfachFacadeMockServer.stubFor(get("/MessageExchange/v1/Receive/{messageId}") + .withPathParam("messageId", equalTo(uuid)) + .willReturn( + okJsonObj( + V1ReplyMessageFactory.create() + .messageBox(UUID.fromString(uuid)) + .responseTime(OffsetDateTime.now()) + ) + ) + ); } } - private void mockJsonOperation(final String operationId, final Object body) { - postfachFacadeMockClient - .when( - new OpenAPIDefinition() - .withSpecUrlOrPayload(getPostfachApiSpec()) - .withOperationId(operationId) - ) - .respond( - response() - .withHeader("Content-type", "application/json") - .withBody(JsonUtil.toJson(body)) - ); + private ResponseDefinitionBuilder okJsonObj(final Object body) { + return okJson(JsonUtil.toJson(body)); } @DisplayName("should delete message") @Test @SneakyThrows void shouldDeleteMessage() { - mockOperationsAndResponses(Map.of( - "deleteMessage", "200" - )); + var messageId = "00000000-0000-0000-0000-000000000000"; + // Stub delete message response (MessageExchangeApi::deleteMessage) + postfachFacadeMockServer.stubFor(delete("/MessageExchange/v1/Delete/{messageId}") + .willReturn(ok()) + ); - assertDoesNotThrow(() -> osiPostfachRemoteService.deleteMessage("00000000-0000-0000-0000-000000000000")); + osiPostfachRemoteService.deleteMessage(messageId); - // TODO verify delete message called - // var requests = postfachFacadeMockClient.retrieveRecordedRequests(request()); - } - private void mockOperationsAndResponses(Map<String, String> operationsAndResponses) { - postfachFacadeMockClient.upsert( - openAPIExpectation() - .withSpecUrlOrPayload(getPostfachApiSpec()) - .withOperationsAndResponses(operationsAndResponses) + postfachFacadeMockServer.verify( + exactly(1), + deleteRequestedFor(urlPathEqualTo("/MessageExchange/v1/Delete/{messageId}")) + .withPathParam("messageId", equalTo(messageId)) ); } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java index 9d59d950c805922702d0a18bf38cbdb30e6c3a5c..adf3e296fcb850f6937a207561909d6744e72fbf 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java @@ -1,22 +1,14 @@ package de.ozgcloud.nachrichten.postfach.osiv2.extension; -import static de.ozgcloud.nachrichten.postfach.osiv2.factory.JwtFactory.*; -import static org.mockserver.matchers.Times.*; -import static org.mockserver.model.Header.*; -import static org.mockserver.model.HttpRequest.*; -import static org.mockserver.model.HttpResponse.*; -import static org.mockserver.model.Parameter.*; -import static org.mockserver.model.ParameterBody.*; +import static com.github.tomakehurst.wiremock.client.WireMock.*; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; -import org.mockserver.client.MockServerClient; -import org.testcontainers.containers.MockServerContainer; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.utility.DockerImageName; + +import com.github.tomakehurst.wiremock.WireMockServer; import de.ozgcloud.nachrichten.postfach.osiv2.factory.JwtFactory; import lombok.Getter; @@ -28,25 +20,8 @@ import lombok.extern.log4j.Log4j2; @RequiredArgsConstructor public class OsiMockServerExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { - private MockServerClient serviceKontoMockClient; - private MockServerClient postfachFacadeMockClient; - - private static final DockerImageName MOCK_SERVER_IMAGE = DockerImageName.parse("mockserver/mockserver") - .withTag("mockserver-5.15.0"); - - private final MockServerContainer serviceKontoMockContainer = new MockServerContainer(MOCK_SERVER_IMAGE) - .withLogConsumer(this::logContainerOutput); - private final MockServerContainer postfachFacadeMockContainer = new MockServerContainer(MOCK_SERVER_IMAGE) - .withLogConsumer(this::logContainerOutput); - - private void logContainerOutput(OutputFrame outputFrame) { - var line = outputFrame.getUtf8String().stripTrailing(); - if (outputFrame.getType() == OutputFrame.OutputType.STDERR) { - System.err.println(line); - } else { - System.out.println(line); - } - } + private WireMockServer serviceKontoMockServer; + private WireMockServer postfachFacadeMockServer; @Override public void beforeAll(ExtensionContext context) { @@ -56,77 +31,39 @@ public class OsiMockServerExtension implements BeforeAllCallback, AfterAllCallba @Override public void afterEach(ExtensionContext context) { - postfachFacadeMockClient.reset(); - serviceKontoMockClient.reset(); + postfachFacadeMockServer.resetAll(); + serviceKontoMockServer.resetAll(); } @Override public void afterAll(ExtensionContext context) { - serviceKontoMockClient.stop(); - serviceKontoMockContainer.stop(); - - postfachFacadeMockClient.stop(); - postfachFacadeMockContainer.stop(); + serviceKontoMockServer.shutdown(); + postfachFacadeMockServer.shutdown(); } private void setupPostfachFacadeMock() { - postfachFacadeMockContainer.start(); - postfachFacadeMockClient = new MockServerClient( - postfachFacadeMockContainer.getHost(), - postfachFacadeMockContainer.getServerPort() - ); + postfachFacadeMockServer = new WireMockServer(0); + postfachFacadeMockServer.start(); } private void setupServiceKontoMock() { - serviceKontoMockContainer.start(); - serviceKontoMockClient = new MockServerClient( - serviceKontoMockContainer.getHost(), - serviceKontoMockContainer.getServerPort() - ); + serviceKontoMockServer = new WireMockServer(0); + serviceKontoMockServer.start(); } public String getAccessTokenUrl() { - return getMockServerUrl(serviceKontoMockClient) + "/access-token"; + return serviceKontoMockServer.baseUrl() + "/access-token"; } public String getPostfachFacadeUrl() { - return getMockServerUrl(postfachFacadeMockClient); - } - - private String getMockServerUrl(MockServerClient mockClient) { - return "http://" + mockClient.remoteAddress().getHostName() + ":" + mockClient.remoteAddress().getPort(); + return postfachFacadeMockServer.baseUrl(); } @Override public void beforeEach(ExtensionContext context) { - serviceKontoMockClient - .when( - request() - .withMethod("POST") - .withPath("/access-token") - .withHeaders( - header("Content-Type", "application/x-www-form-urlencoded") - ) - .withBody( - params( - param("grant_type", "client_credentials"), - param("client_id", CLIENT_ID), - param("client_secret", "changeme"), - param("scope", String.join(" ", CLIENT_SCOPES)), - param("resource", RESOURCE_URN) - ) - ), - exactly(1) - ) - .respond( - response() - .withStatusCode(200) - .withHeader("Content-Type", "application/json") - .withBody( - JwtFactory.createTokenResponse( - JwtFactory.createAccessTokenExampleWithExpireIn(900) - ) - ) - ); + serviceKontoMockServer.stubFor(post("/access-token") + .willReturn(okJson(JwtFactory.createTokenResponse( + JwtFactory.createAccessTokenExampleWithExpireIn(900) + )))); } }