From 02c696d1f8629197d0224355c32ebf0e9ca7c17e Mon Sep 17 00:00:00 2001 From: Jonas Wagner Date: Mon, 16 Dec 2024 15:49:27 +0100 Subject: [PATCH] Use WireMock instead of Mockserver --- .github/workflows/analyze.yml | 2 +- .github/workflows/build.yml | 4 +- .../fts/cda/impl/DeidentifhirStepTest.java | 70 ++-- .../fts/cda/impl/RDABundleSenderTest.java | 179 +++++----- .../care/smith/fts/cda/rest/it/BaseIT.java | 39 +-- .../smith/fts/cda/rest/it/BundleSenderIT.java | 4 +- .../fts/cda/rest/it/CohortSelectorIT.java | 90 +++-- .../smith/fts/cda/rest/it/DataSelectorIT.java | 9 +- .../smith/fts/cda/rest/it/DeidentifhirIT.java | 2 +- .../fts/cda/rest/it/FhirResolveServiceIT.java | 10 +- .../care/smith/fts/cda/rest/it/GeneralIT.java | 10 +- .../rest/it/TransferProcessControllerIT.java | 35 +- .../cda/rest/it/mock/MockBundleSender.java | 103 +++--- .../cda/rest/it/mock/MockCohortSelector.java | 310 ++++++++++-------- .../cda/rest/it/mock/MockDataSelector.java | 63 ++-- .../fts/cda/rest/it/mock/MockFetchData.java | 77 +++-- .../rest/it/mock/MockFhirResolveService.java | 94 +++--- .../cda/rest/it/mock/MockTransportIds.java | 78 ++--- .../cda/services/FhirResolveServiceTest.java | 55 ++-- .../src/test/resources/application.yaml | 1 - .../fts/rda/impl/DeidentifhirStepTest.java | 72 ++-- .../rda/impl/FhirStoreBundleSenderTest.java | 47 ++- .../care/smith/fts/rda/rest/it/BaseIT.java | 28 +- .../smith/fts/rda/rest/it/BundleSenderIT.java | 6 +- .../smith/fts/rda/rest/it/DeidentifierIT.java | 2 +- .../rest/it/TransferProcessControllerIT.java | 28 +- .../rda/rest/it/mock/MockBundleSender.java | 60 ++-- .../rda/rest/it/mock/MockDeidentifier.java | 86 +++-- .../src/test/resources/application.yaml | 1 - test-util/pom.xml | 11 +- .../care/smith/fts/test/MockServerUtil.java | 164 +++++++-- .../test/java/care/smith/fts/tca/BaseIT.java | 21 +- ...ConsentedPatientsProviderFetchAllTest.java | 213 ++++++------ ...hirConsentedPatientsProviderFetchTest.java | 153 ++++----- .../FhirMappingProviderTest.java | 106 +++--- .../rest/DeIdentificationControllerIT.java | 123 +++---- .../tca/rest/FetchAllConsentControllerIT.java | 68 ++-- .../tca/rest/FetchConsentControllerIT.java | 66 ++-- .../src/test/resources/application.yaml | 1 - util/pom.xml | 5 +- .../smith/fts/util/WebClientDefaultsTest.java | 43 +-- .../fts/util/WebClientFhirCodecTest.java | 40 +-- .../util/auth/HttpServerAuthConfigTest.java | 3 +- 43 files changed, 1322 insertions(+), 1260 deletions(-) diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index f0012913..d983be35 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -11,7 +11,7 @@ on: jobs: codeql: env: - MAVEN_ARGS: -B -T1C -Dmockserver.logLevel=WARN -Dfts.retryTimeout=false + MAVEN_ARGS: -B -T1C -Dfts.retryTimeout=false GITHUB_TOKEN: ${{ github.token }} permissions: security-events: write diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d745247a..74061a38 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: jobs: test: env: - MAVEN_ARGS: -B -T1C -Dmockserver.logLevel=WARN -Dfts.retryTimeout=false + MAVEN_ARGS: -B -T1C -Dfts.retryTimeout=false GITHUB_TOKEN: ${{ github.token }} runs-on: ubuntu-24.04 strategy: @@ -50,7 +50,7 @@ jobs: build-jar: needs: [ test ] env: - MAVEN_ARGS: -B -T1C -Dmockserver.logLevel=WARN -Dfts.retryTimeout=false + MAVEN_ARGS: -B -T1C -Dfts.retryTimeout=false GITHUB_TOKEN: ${{ github.token }} COMMIT_TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }} runs-on: ubuntu-24.04 diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/DeidentifhirStepTest.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/DeidentifhirStepTest.java index 04b48dae..28a71283 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/DeidentifhirStepTest.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/DeidentifhirStepTest.java @@ -2,12 +2,12 @@ import static care.smith.fts.test.MockServerUtil.clientConfig; import static care.smith.fts.test.TestPatientGenerator.generateOnePatient; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.typesafe.config.ConfigFactory.parseResources; import static java.time.Duration.ofDays; -import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; import static reactor.test.StepVerifier.create; import care.smith.fts.api.ConsentedPatient; @@ -16,46 +16,45 @@ import care.smith.fts.cda.services.deidentifhir.DeidentifhirUtils; import care.smith.fts.util.WebClientFactory; import care.smith.fts.util.tca.TCADomains; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import java.io.IOException; import org.hl7.fhir.r4.model.Bundle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest(classes = ClinicalDomainAgent.class) -@ExtendWith(MockServerExtension.class) +@WireMockTest class DeidentifhirStepTest { @Autowired MeterRegistry meterRegistry; private DeidentifhirStep step; + private WireMock wireMock; @BeforeEach - void setUp(MockServerClient mockServer, @Autowired WebClientFactory clientFactory) { + void setUp(WireMockRuntimeInfo wireMockRuntime, @Autowired WebClientFactory clientFactory) { var scraperConfig = parseResources(DeidentifhirUtils.class, "IDScraper.profile"); var deidentifhirConfig = parseResources(DeidentifhirUtils.class, "CDtoTransport.profile"); var domains = new TCADomains("domain", "domain", "domain"); - var client = clientFactory.create(clientConfig(mockServer)); + var client = clientFactory.create(clientConfig(wireMockRuntime)); + wireMock = wireMockRuntime.getWireMock(); step = new DeidentifhirStep( client, domains, ofDays(14), deidentifhirConfig, scraperConfig, meterRegistry); } @Test - void correctRequestSent(MockServerClient mockServer) throws IOException { - mockServer - .when( - request() - .withMethod("POST") - .withPath("/api/v2/cd/transport-mapping") - .withBody( - json( - """ + void correctRequestSent() throws IOException { + wireMock.register( + post("/api/v2/cd/transport-mapping") + .withRequestBody( + equalToJson( + """ { "patientId" : "id1", "resourceIds" : [ "id1.identifier.identifierSystem:id1", "id1.Patient:id1" ], @@ -67,8 +66,9 @@ void correctRequestSent(MockServerClient mockServer) throws IOException { "maxDateShift" : 1209600.0 } """, - ONLY_MATCHING_FIELDS))) - .respond(response().withStatusCode(200)); + true, + true)) + .willReturn(ok())); var consentedPatient = new ConsentedPatient("id1"); var bundle = generateOnePatient("id1", "2024", "identifierSystem"); @@ -78,10 +78,8 @@ void correctRequestSent(MockServerClient mockServer) throws IOException { } @Test - void emptyTCAResponseYieldsEmptyResult(MockServerClient mockServer) throws IOException { - mockServer - .when(request().withMethod("POST").withPath("/api/v2/cd/transport-mapping")) - .respond(response().withStatusCode(200)); + void emptyTCAResponseYieldsEmptyResult() throws IOException { + wireMock.register(post("/api/v2/cd/transport-mapping").willReturn(ok())); var consentedPatient = new ConsentedPatient("id1"); var bundle = generateOnePatient("id1", "2024", "identifierSystem"); @@ -91,22 +89,20 @@ void emptyTCAResponseYieldsEmptyResult(MockServerClient mockServer) throws IOExc } @Test - void deidentifySucceeds(MockServerClient mockServer) throws IOException { - mockServer - .when(request().withMethod("POST").withPath("/api/v2/cd/transport-mapping")) - .respond( - response() - .withBody( - json( - """ + void deidentifySucceeds() throws IOException { + wireMock.register( + post("/api/v2/cd/transport-mapping") + .willReturn( + jsonResponse( + """ { "transferId": "transferId", "transportMapping": { "id1.identifier.identifierSystem:id1": "tident1", "id1.Patient:id1": "tid1" }, "dateShiftValue": 1209600.000000000 } - """)) - .withStatusCode(200)); + """, + 200))); var consentedPatient = new ConsentedPatient("id1"); var bundle = generateOnePatient("id1", "2024", "identifierSystem"); @@ -123,7 +119,7 @@ void emptyIdsYieldEmptyMono() { } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/RDABundleSenderTest.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/RDABundleSenderTest.java index a7b11a8d..082c094e 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/RDABundleSenderTest.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/impl/RDABundleSenderTest.java @@ -1,25 +1,29 @@ package care.smith.fts.cda.impl; +import static care.smith.fts.test.MockServerUtil.FIRST; +import static care.smith.fts.test.MockServerUtil.REST; +import static care.smith.fts.test.MockServerUtil.accepted; +import static care.smith.fts.test.MockServerUtil.clientConfig; import static care.smith.fts.util.FhirUtils.toBundle; +import static com.github.tomakehurst.wiremock.client.WireMock.created; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.matching.UrlPattern.ANY; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockserver.matchers.Times.once; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; import static org.springframework.http.HttpHeaders.CONTENT_LOCATION; import static org.springframework.http.HttpHeaders.RETRY_AFTER; -import static org.springframework.http.HttpStatus.ACCEPTED; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.OK; import static reactor.test.StepVerifier.create; import care.smith.fts.api.ConsentedPatient; import care.smith.fts.api.TransportBundle; import care.smith.fts.api.cda.BundleSender; -import care.smith.fts.test.MockServerUtil; import care.smith.fts.util.HttpClientConfig; import care.smith.fts.util.WebClientFactory; import care.smith.fts.util.error.TransferProcessException; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import java.util.stream.Stream; import org.hl7.fhir.r4.model.Bundle; @@ -29,15 +33,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.web.reactive.function.client.*; @SpringBootTest @ExtendWith(MockitoExtension.class) -@ExtendWith(MockServerExtension.class) +@WireMockTest class RDABundleSenderTest { @Autowired MeterRegistry meterRegistry; @@ -49,11 +51,12 @@ class RDABundleSenderTest { private final RDABundleSenderConfig config = new RDABundleSenderConfig(server, "example"); private WebClient client; + private WireMock wireMock; @BeforeEach - void setUp(MockServerClient mockServer, @Autowired WebClientFactory clientFactory) { - var server = MockServerUtil.clientConfig(mockServer); - client = clientFactory.create(server); + void setUp(WireMockRuntimeInfo wireMockRuntime, @Autowired WebClientFactory clientFactory) { + client = clientFactory.create(clientConfig(wireMockRuntime)); + wireMock = wireMockRuntime.getWireMock(); } @Test @@ -65,10 +68,8 @@ void nullBundleErrors() { } @Test - void badRequest(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond(response().withStatusCode(BAD_REQUEST.value())); + void badRequest() { + wireMock.register(post(ANY).willReturn(WireMock.badRequest())); var bundleSender = new RDABundleSender(config, client, meterRegistry); @@ -78,11 +79,9 @@ void badRequest(MockServerClient mockServer) { } @Test - void contentLocationIsNull(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond(response().withStatusCode(ACCEPTED.value())); - mockServer.when(request().withMethod("GET")).respond(response().withStatusCode(OK.value())); + void contentLocationIsNull() { + wireMock.register(post(ANY).willReturn(accepted())); + wireMock.register(get(ANY).willReturn(ok())); var bundleSender = new RDABundleSender(config, client, meterRegistry); @@ -93,11 +92,8 @@ void contentLocationIsNull(MockServerClient mockServer) { } @Test - void contentLocationIsEmpty(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond(response().withStatusCode(ACCEPTED.value()).withHeader(CONTENT_LOCATION, "")); - mockServer.when(request().withMethod("GET")).respond(response().withStatusCode(OK.value())); + void contentLocationIsEmpty() { + wireMock.register(post(ANY).willReturn(accepted().withHeader(CONTENT_LOCATION, ""))); var bundleSender = new RDABundleSender(config, client, meterRegistry); @@ -108,16 +104,13 @@ void contentLocationIsEmpty(MockServerClient mockServer) { } @Test - void bundleSent(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond( - response() - .withStatusCode(ACCEPTED.value()) - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); - mockServer - .when(request().withMethod("GET").withPath("/api/v2/process/status/processId")) - .respond(response().withStatusCode(OK.value())); + void bundleSent() { + wireMock.register( + post(ANY) + .willReturn( + accepted().withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId"))); + + wireMock.register(get("/api/v2/process/status/processId").willReturn(ok())); var bundleSender = new RDABundleSender(config, client, meterRegistry); @@ -128,13 +121,11 @@ void bundleSent(MockServerClient mockServer) { } @Test - void withStatusUnequalAcceptedInWaitForRDACompleted(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond( - response() - .withStatusCode(CREATED.value()) - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); + void withStatusUnequalAcceptedInWaitForRDACompleted() { + wireMock.register( + post(ANY) + .willReturn( + created().withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId"))); var bundleSender = new RDABundleSender(config, client, meterRegistry); var bundle = Stream.of(new Patient().setId(PATIENT_ID)).collect(toBundle()); @@ -144,23 +135,27 @@ void withStatusUnequalAcceptedInWaitForRDACompleted(MockServerClient mockServer) } @Test - void withNumberFormatExceptionInGetRetryAfter(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond( - response() - .withStatusCode(ACCEPTED.value()) - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); - mockServer - .when(request().withMethod("GET").withPath("/api/v2/process/status/processId"), once()) - .respond( - response() - .withStatusCode(ACCEPTED.value()) - .withHeader(RETRY_AFTER, "1") - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); - mockServer - .when(request().withMethod("GET").withPath("/api/v2/process/status/processId")) - .respond(response().withStatusCode(OK.value())); + void withRetryAfterOnFirstAttempt() { + wireMock.register( + post(ANY) + .willReturn( + accepted().withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId"))); + + wireMock.register( + get("/api/v2/process/status/processId") + .inScenario("BadRetryAfterAtFirst") + .whenScenarioStateIs(FIRST) + .willReturn( + accepted() + .withHeader(RETRY_AFTER, "1") + .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")) + .willSetStateTo(REST)); + + wireMock.register( + get("/api/v2/process/status/processId") + .inScenario("BadRetryAfterAtFirst") + .whenScenarioStateIs(REST) + .willReturn(ok())); var bundleSender = new RDABundleSender(config, client, meterRegistry); var bundle = Stream.of(new Patient().setId(PATIENT_ID)).collect(toBundle()); @@ -170,23 +165,27 @@ void withNumberFormatExceptionInGetRetryAfter(MockServerClient mockServer) { } @Test - void withNumberFormatExceptionInGetRetryAfterWithParsingException(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond( - response() - .withStatusCode(ACCEPTED.value()) - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); - mockServer - .when(request().withMethod("GET").withPath("/api/v2/process/status/processId"), once()) - .respond( - response() - .withStatusCode(ACCEPTED.value()) - .withHeader(RETRY_AFTER, "try to parse this!") - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); - mockServer - .when(request().withMethod("GET").withPath("/api/v2/process/status/processId")) - .respond(response().withStatusCode(OK.value())); + void withNumberFormatExceptionInGetRetryAfterWithParsingException() { + wireMock.register( + post(ANY) + .willReturn( + accepted().withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId"))); + + wireMock.register( + get("/api/v2/process/status/processId") + .inScenario("BadRetryAfterAtFirst") + .whenScenarioStateIs(FIRST) + .willReturn( + accepted() + .withHeader(RETRY_AFTER, "try to parse this!") + .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")) + .willSetStateTo(REST)); + + wireMock.register( + get("/api/v2/process/status/processId") + .inScenario("BadRetryAfterAtFirst") + .whenScenarioStateIs(REST) + .willReturn(ok())); var bundleSender = new RDABundleSender(config, client, meterRegistry); var bundle = Stream.of(new Patient().setId(PATIENT_ID)).collect(toBundle()); @@ -196,16 +195,14 @@ void withNumberFormatExceptionInGetRetryAfterWithParsingException(MockServerClie } @Test - void withNumberFormatExceptionInGetRetryAfterWithRetriesExhausted(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond( - response() - .withStatusCode(ACCEPTED.value()) - .withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId")); - mockServer - .when(request().withMethod("GET").withPath("/api/v2/process/status/processId")) - .respond(response().withStatusCode(ACCEPTED.value()).withHeader(RETRY_AFTER, "1")); + void withNumberFormatExceptionInGetRetryAfterWithRetriesExhausted() { + wireMock.register( + post(ANY) + .willReturn( + accepted().withHeader(CONTENT_LOCATION, "/api/v2/process/status/processId"))); + wireMock.register( + get("/api/v2/process/status/processId") + .willReturn(accepted().withHeader(RETRY_AFTER, "1"))); var bundleSender = new RDABundleSender(config, client, meterRegistry); var bundle = Stream.of(new Patient().setId(PATIENT_ID)).collect(toBundle()); @@ -215,7 +212,7 @@ void withNumberFormatExceptionInGetRetryAfterWithRetriesExhausted(MockServerClie } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BaseIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BaseIT.java index 30f6c3cb..9a2d9cc0 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BaseIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BaseIT.java @@ -4,20 +4,21 @@ import static org.springframework.util.FileSystemUtils.deleteRecursively; import care.smith.fts.test.MockServerUtil; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.junit.jupiter.api.AfterAll; -import org.mockserver.client.MockServerClient; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; public abstract class BaseIT { private static final Path tempDir; - protected static final MockServerClient hds; - protected static final MockServerClient tca; - protected static final MockServerClient rda; + protected static final WireMockServer hds; + protected static final WireMockServer tca; + protected static final WireMockServer rda; @AfterAll static void afterAll() throws IOException { @@ -30,17 +31,17 @@ static void registerTempDir(DynamicPropertyRegistry registry) { } protected static void resetAll() { - tca.reset(); - hds.reset(); - rda.reset(); + tca.resetAll(); + hds.resetAll(); + rda.resetAll(); } static { try { tempDir = Files.createTempDirectory("ftsit"); - hds = hdsMockServer(); - tca = tcaMockServer(); - rda = rdaMockServer(); + hds = MockServerUtil.onRandomPort(); + tca = MockServerUtil.onRandomPort(); + rda = MockServerUtil.onRandomPort(); createProject(); } catch (IOException e) { throw new IllegalStateException("Unable to create project config file", e); @@ -54,22 +55,10 @@ private static void createProject() throws IOException { var outStream = Files.newOutputStream(projectFile)) { var config = new String(inStream.readAllBytes(), UTF_8) - .replace("", "http://localhost:%d".formatted(tca.getPort())) - .replace("", "http://localhost:%d".formatted(hds.getPort())) - .replace("", "http://localhost:%d".formatted(rda.getPort())); + .replace("", tca.baseUrl()) + .replace("", hds.baseUrl()) + .replace("", rda.baseUrl()); outStream.write(config.getBytes(UTF_8)); } } - - private static MockServerClient tcaMockServer() throws IOException { - return MockServerUtil.onRandomPort(); - } - - private static MockServerClient rdaMockServer() throws IOException { - return MockServerUtil.onRandomPort(); - } - - private static MockServerClient hdsMockServer() throws IOException { - return MockServerUtil.onRandomPort(); - } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BundleSenderIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BundleSenderIT.java index 254a53f9..7dba0482 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BundleSenderIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/BundleSenderIT.java @@ -19,7 +19,7 @@ public BundleSenderIT() throws IOException { @BeforeEach void setUp() throws IOException { - mockCohortSelector.consentForOnePatient(patientId); + allCohortSelector.consentForOnePatient(patientId); mockDataSelector.whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM).resolveId(patientId); mockDataSelector.whenFetchData(patientId).respondWith(patient); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); @@ -50,7 +50,7 @@ void firstTryToSendBundleFails() throws IOException { mockDataSelector .whenFetchData(patientId) .respondWith(new Bundle().addEntry(patient.getEntryFirstRep())); - mockBundleSender.successWithStatusCode(List.of(500)); + mockBundleSender.successWithStatusCode(List.of(500, 200)); startProcess(Duration.ofSeconds(8)) .assertNext(r -> completedWithBundles(1, r)) diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/CohortSelectorIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/CohortSelectorIT.java index 24d00cf8..ca3129eb 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/CohortSelectorIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/CohortSelectorIT.java @@ -2,6 +2,7 @@ import static care.smith.fts.test.TestPatientGenerator.generateNPatients; +import com.fasterxml.jackson.core.JsonProcessingException; import java.io.IOException; import java.time.Duration; import java.util.List; @@ -13,7 +14,8 @@ public class CohortSelectorIT extends TransferProcessControllerIT { @Test void tcaDown() { - mockCohortSelector.isDown(); + allCohortSelector.isDown(); + listCohortSelector.isDown(); startProcess(Duration.ofSeconds(3)).expectError(InternalServerError.class).verify(); startProcessForIds(Duration.ofSeconds(3), List.of()) @@ -23,7 +25,8 @@ void tcaDown() { @Test void tcaTimeoutConsentedPatientsRequest() { - mockCohortSelector.timeout(); + allCohortSelector.timeout(); + listCohortSelector.timeout(); startProcess(Duration.ofMinutes(1)).expectError(InternalServerError.class).verify(); startProcessForIds(Duration.ofMinutes(1), List.of()) @@ -32,8 +35,9 @@ void tcaTimeoutConsentedPatientsRequest() { } @Test - void tcaSendsWrongContentType() throws IOException { - mockCohortSelector.wrongContentType(); + void tcaSendsWrongContentType() { + allCohortSelector.wrongContentType(); + listCohortSelector.wrongContentType(); startProcess(Duration.ofSeconds(3)).expectError(InternalServerError.class).verify(); startProcessForIds(Duration.ofSeconds(3), List.of()) @@ -42,8 +46,9 @@ void tcaSendsWrongContentType() throws IOException { } @Test - void unknownDomain() { - mockCohortSelector.unknownDomain(om); + void unknownDomain() throws JsonProcessingException { + allCohortSelector.unknownDomain(om); + listCohortSelector.unknownDomain(om); startProcess(Duration.ofSeconds(3)).expectError(InternalServerError.class).verify(); startProcessForIds(Duration.ofSeconds(3), List.of()) @@ -52,46 +57,66 @@ void unknownDomain() { } @Test - void firstRequestToCohortFails() throws IOException { - var idPrefix = "patientId"; + void firstRequestToCohortFailsInAllSelector() throws IOException { + var idPrefix = "patient-917653"; int total = 3; - var patientsAndIds = generateNPatients(idPrefix, "2025", DEFAULT_IDENTIFIER_SYSTEM, total); - var patients = patientsAndIds.bundle(); - var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatients(idPrefix, total, List.of(500)); - for (var i = 0; i < patients.getTotal(); i++) { - var patientId = ids.get(i); - mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); - mockDataSelector - .whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM) - .resolveId(patientId); - mockDataSelector - .whenFetchData(patientId) - .respondWith(new Bundle().addEntry(patients.getEntry().get(i))); - } - - mockBundleSender.success(); + allCohortSelector.consentForNPatients(idPrefix, total, List.of(500, 200)); + prepareCohortWithPaging(idPrefix, total); startProcess(Duration.ofSeconds(12)) .assertNext(r -> completedWithBundles(total, r)) .verifyComplete(); + } + + @Test + void firstRequestToCohortFailsInListSelector() throws IOException { + var idPrefix = "patient-241352"; + int total = 3; + + listCohortSelector.consentForNPatients(idPrefix, total, List.of(500, 200)); + var ids = prepareCohortWithPaging(idPrefix, total); + startProcessForIds(Duration.ofSeconds(12), ids) .assertNext(r -> completedWithBundles(total, r)) .verifyComplete(); } @Test - void someRequestsToCohortFailDuringPaging() throws IOException { - var idPrefix = "patientId"; + void someRequestsToCohortFailDuringPagingInAllSelector() throws IOException { + var idPrefix = "patient-819305"; int total = 7; int maxPageSize = 2; + + allCohortSelector.consentForNPatientsWithPaging( + idPrefix, total, maxPageSize, List.of(200, 500, 500, 200, 200, 500, 200)); + prepareCohortWithPaging(idPrefix, total); + + startProcess(Duration.ofSeconds(12)) + .assertNext(r -> completedWithBundles(total, r)) + .verifyComplete(); + } + + @Test + void someRequestsToCohortFailDuringPagingInListSelector() throws IOException { + var idPrefix = "patient-101183"; + int total = 7; + int maxPageSize = 2; + + listCohortSelector.consentForNPatientsWithPaging( + idPrefix, total, maxPageSize, List.of(200, 500, 500, 200, 200, 500, 200)); + var ids = prepareCohortWithPaging(idPrefix, total); + + startProcessForIds(Duration.ofSeconds(12), ids) + .assertNext(r -> completedWithBundles(total, r)) + .verifyComplete(); + } + + private List prepareCohortWithPaging(String idPrefix, int total) throws IOException { var patientsAndIds = generateNPatients(idPrefix, "2025", DEFAULT_IDENTIFIER_SYSTEM, total); var patients = patientsAndIds.bundle(); var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatientsWithPaging( - idPrefix, total, maxPageSize, List.of(200, 500, 500, 200, 200, 500, 200)); for (var i = 0; i < patients.getTotal(); i++) { var patientId = ids.get(i); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); @@ -102,14 +127,7 @@ void someRequestsToCohortFailDuringPaging() throws IOException { .whenFetchData(patientId) .respondWith(new Bundle().addEntry(patients.getEntry().get(i))); } - mockBundleSender.success(); - - startProcess(Duration.ofSeconds(12)) - .assertNext(r -> completedWithBundles(total, r)) - .verifyComplete(); - startProcessForIds(Duration.ofSeconds(12), ids) - .assertNext(r -> completedWithBundles(total, r)) - .verifyComplete(); + return ids; } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DataSelectorIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DataSelectorIT.java index ae710365..7fdc84a5 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DataSelectorIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DataSelectorIT.java @@ -6,6 +6,7 @@ import java.time.Duration; import java.util.List; import org.hl7.fhir.r4.model.Bundle; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,7 +15,7 @@ public class DataSelectorIT extends TransferProcessControllerIT { @BeforeEach void setUp() throws IOException { - mockCohortSelector.consentForOnePatient(patientId); + allCohortSelector.consentForOnePatient(patientId); mockDataSelector.whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM).resolveId(patientId); } @@ -22,7 +23,7 @@ void setUp() throws IOException { void hdsDown() { mockDataSelector.whenFetchData(patientId).dropConnection(); - startProcess(Duration.ofSeconds(3)) + startProcess(Duration.ofMinutes(1)) .assertNext(TransferProcessControllerIT::errored) .verifyComplete(); } @@ -60,10 +61,10 @@ void hdsFirstRequestFails() throws IOException { mockDataSelector.whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM).resolveId(patientId); mockDataSelector .whenFetchData(patientId) - .respondWith(new Bundle().addEntry(patient.getEntryFirstRep()), List.of(500)); + .respondWith(new Bundle().addEntry(patient.getEntryFirstRep()), List.of(500, 200)); mockBundleSender.success(); - startProcess(Duration.ofSeconds(5)) + startProcess(Duration.ofSeconds(10)) .assertNext(r -> completedWithBundles(1, r)) .verifyComplete(); } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DeidentifhirIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DeidentifhirIT.java index 228d5faa..4322418e 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DeidentifhirIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/DeidentifhirIT.java @@ -23,7 +23,7 @@ public DeidentifhirIT() throws IOException { @BeforeEach void setUp() throws IOException { - mockCohortSelector.consentForOnePatient(patientId); + allCohortSelector.consentForOnePatient(patientId); mockDataSelector.whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM).resolveId(patientId); mockDataSelector.whenFetchData(patientId).respondWith(patient); } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/FhirResolveServiceIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/FhirResolveServiceIT.java index 0337164d..71476d2c 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/FhirResolveServiceIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/FhirResolveServiceIT.java @@ -14,15 +14,15 @@ public class FhirResolveServiceIT extends TransferProcessControllerIT { private static final String patientId = "patientId"; @BeforeEach - void setUp() throws IOException { - mockCohortSelector.consentForOnePatient(patientId); + void setUp() { + allCohortSelector.consentForOnePatient(patientId); } @Test void hdsDown() { mockDataSelector.whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM).isDown(); - startProcess(Duration.ofSeconds(3)) + startProcess(Duration.ofMinutes(1)) .assertNext(TransferProcessControllerIT::errored) .verifyComplete(); } @@ -69,13 +69,13 @@ void hdsFirstRequestFails() throws IOException { mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); mockDataSelector .whenResolvePatient(patientId, DEFAULT_IDENTIFIER_SYSTEM) - .resolveId(patientId, List.of(500)); + .resolveId(patientId, List.of(500, 200)); mockDataSelector .whenFetchData(patientId) .respondWith(new Bundle().addEntry(patient.getEntryFirstRep())); mockBundleSender.success(); - startProcess(Duration.ofSeconds(3)) + startProcess(Duration.ofSeconds(10)) .assertNext(r -> completedWithBundles(1, r)) .verifyComplete(); } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/GeneralIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/GeneralIT.java index 4708bb13..9ca6f011 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/GeneralIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/GeneralIT.java @@ -33,7 +33,7 @@ void successfulRequest() throws IOException { var patients = patientsAndIds.bundle(); var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatients(idPrefix, totalPatients); + allCohortSelector.consentForNPatients(idPrefix, totalPatients); for (var i = 0; i < patients.getTotal(); i++) { var patientId = ids.get(i); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); @@ -63,7 +63,7 @@ void successfulRequestWithPaging() throws IOException { var patients = patientsAndIds.bundle(); var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatientsWithPaging(idPrefix, totalPatients, pageSize); + allCohortSelector.consentForNPatientsWithPaging(idPrefix, totalPatients, pageSize); for (var i = 0; i < patients.getTotal(); i++) { var patientId = ids.get(i); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); @@ -92,7 +92,7 @@ void successfulRequestWithRetryAfter() throws IOException { var patients = patientsAndIds.bundle(); var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatients(idPrefix, totalPatients); + allCohortSelector.consentForNPatients(idPrefix, totalPatients); for (var i = 0; i < patients.getTotal(); i++) { var patientId = ids.get(i); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); @@ -137,7 +137,7 @@ void callingStatusWithWrongProcessIdReturns404() throws IOException { var patients = patientsAndIds.bundle(); var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatients(idPrefix, 3); + allCohortSelector.consentForNPatients(idPrefix, 3); for (var i = 0; i < patients.getTotal(); i++) { var patientId = ids.get(i); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); @@ -179,7 +179,7 @@ void statuses() throws IOException { var patients = patientsAndIds.bundle(); var ids = patientsAndIds.ids(); - mockCohortSelector.consentForNPatients(idPrefix, 3); + allCohortSelector.consentForNPatients(idPrefix, 3); for (var i = 0; i < patients.getTotal(); i++) { var patientId = ids.get(i); mockDataSelector.whenTransportMapping(patientId, DEFAULT_IDENTIFIER_SYSTEM).success(); diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/TransferProcessControllerIT.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/TransferProcessControllerIT.java index 7231ed4e..5d8ee7e1 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/TransferProcessControllerIT.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/TransferProcessControllerIT.java @@ -81,30 +81,35 @@ public class TransferProcessControllerIT extends BaseIT { protected final ObjectMapper om = new ObjectMapper().registerModule(new JavaTimeModule()); - protected final MockCohortSelector mockCohortSelector = new MockCohortSelector(tca); - protected final MockDataSelector mockDataSelector = new MockDataSelector(om, tca, hds); - protected final MockBundleSender mockBundleSender = new MockBundleSender(rda); + protected MockCohortSelector allCohortSelector; + protected MockCohortSelector listCohortSelector; + protected MockDataSelector mockDataSelector; + protected MockBundleSender mockBundleSender; protected static final String DEFAULT_IDENTIFIER_SYSTEM = "http://fts.smith.care"; @BeforeEach - void setUp(@LocalServerPort int port, @Autowired TestWebClientFactory factory) { + final void setUpTransferProcessControllerIT( + @LocalServerPort int port, @Autowired TestWebClientFactory factory) { this.port = port; client = factory.webClient("https://localhost:" + port); + allCohortSelector = MockCohortSelector.fetchAll(tca); + listCohortSelector = MockCohortSelector.fetch(tca); + mockDataSelector = new MockDataSelector(om, tca, hds); + mockBundleSender = new MockBundleSender(rda); } @AfterEach - void tearDown() { + final void tearDownTransferProcessControllerIT() { resetAll(); } - - protected FirstStep startProcess(Duration timeout) { return startProcess(timeout, s -> s.phase() != RUNNING && s.phase() != QUEUED); } - protected FirstStep startProcess(Duration timeout, Predicate until) { + protected FirstStep startProcess( + Duration timeout, Predicate until) { return client .post() .uri("/api/v2/process/test/start") @@ -123,7 +128,8 @@ protected FirstStep startProcess(Duration timeout, Predic .as(StepVerifier::create); } - protected FirstStep startProcessForIds(Duration timeout, List ids) { + protected FirstStep startProcessForIds( + Duration timeout, List ids) { return startProcessForIds(timeout, s -> s.phase() != RUNNING && s.phase() != QUEUED, ids); } @@ -153,13 +159,20 @@ private Mono retrieveStatus(List r) { } protected static void completedWithBundles(int expectedBundlesSent, TransferProcessStatus r) { + log.debug("Status of completed process: {}", r); expectPhase(r, Phase.COMPLETED); - assertThat(r.sentBundles()).isEqualTo(expectedBundlesSent); + assertThat(r.sentBundles()) + .withFailMessage( + "Wrong number of sent bundles %d, expected %d", r.sentBundles(), expectedBundlesSent) + .isEqualTo(expectedBundlesSent); } protected static void errored(TransferProcessStatus r) { + log.debug("Status of errored process: {}", r); expectPhase(r, Phase.COMPLETED_WITH_ERROR); - assertThat(r.skippedBundles()).isEqualTo(1); + assertThat(r.skippedBundles()) + .withFailMessage("Wrong number of skipped bundles %d, expected %d", r.skippedBundles(), 1) + .isEqualTo(1); } protected static void expectPhase(TransferProcessStatus r, Phase phase) { diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockBundleSender.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockBundleSender.java index d20360f3..de86e2d7 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockBundleSender.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockBundleSender.java @@ -1,92 +1,79 @@ package care.smith.fts.cda.rest.it.mock; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; +import static care.smith.fts.test.MockServerUtil.accepted; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.springframework.http.HttpHeaders.CONTENT_LOCATION; import static org.springframework.http.HttpHeaders.RETRY_AFTER; -import java.util.LinkedList; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; import java.util.List; -import java.util.Optional; import lombok.extern.slf4j.Slf4j; -import org.mockserver.client.MockServerClient; -import org.mockserver.matchers.MatchType; -import org.mockserver.matchers.Times; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; @Slf4j public class MockBundleSender { - private final MockServerClient rda; - private final HttpRequest request; - private final String baseUrl; + private static final String BASE_PATH = "/api/v2/process"; + + private final WireMock rda; + private final MappingBuilder request; + + public MockBundleSender(WireMockServer rda) { + this.rda = new WireMock(rda); - public MockBundleSender(MockServerClient rda) { - this.rda = rda; - baseUrl = "/api/v2/process"; request = - request() - .withMethod("POST") - .withPath(baseUrl + "/test/patient") - .withBody( - json("{\"resourceType\":\"Bundle\",\"total\":2}", MatchType.ONLY_MATCHING_FIELDS)); + post(urlPathEqualTo(BASE_PATH + "/test/patient")) + .withRequestBody(equalToJson("{\"resourceType\":\"Bundle\",\"total\":2}", true, true)); } public void success() { - rda.when(request).respond(initialSuccessResponse()); + rda.register(request.willReturn(initialSuccessResponse())); - rda.when(request().withMethod("GET").withPath(baseUrl + "/status/processId456")) - .respond(response().withStatusCode(200)); + rda.register(get(urlPathEqualTo(BASE_PATH + "/status/processId456")).willReturn(ok())); } public void successWithStatusCode(List statusCodes) { - var rs = new LinkedList<>(statusCodes); - - rda.when(request) - .respond( - request -> - Optional.ofNullable(rs.poll()) - .map( - statusCode -> { - log.trace("statusCode: {}", statusCode); - return statusCode < 400 - ? initialSuccessResponse() - : response().withStatusCode(statusCode); - }) - .orElseGet(this::initialSuccessResponse)); - - rda.when(request().withMethod("GET").withPath(baseUrl + "/status/processId456")) - .respond(response().withStatusCode(200)); + var seq = sequentialMock(rda); + var butLast = statusCodes.subList(0, statusCodes.size() - 1); + for (var statusCode : butLast) { + var response = statusCode < 400 ? initialSuccessResponse() : status(statusCode); + seq = seq.then(request, response); + } + seq.thereafter(request, initialSuccessResponse()); + + rda.register(get(urlPathEqualTo(BASE_PATH + "/status/processId456")).willReturn(ok())); } - private HttpResponse initialSuccessResponse() { + private ResponseDefinitionBuilder initialSuccessResponse() { log.trace("initialSuccessResponse with statusCode 200"); - return response() - .withHeader( - CONTENT_LOCATION, - ("http://localhost:%d" + baseUrl + "/status/processId456").formatted(rda.getPort())); + return ok().withHeader(CONTENT_LOCATION, BASE_PATH + "/status/processId456"); } public void isDown() { - rda.when(request).error(HttpError.error().withDropConnection(true)); + rda.register(request.willReturn(connectionReset())); } public void timeout() { - rda.when(request).respond(request -> null, Delay.minutes(10)); + rda.register(request.willReturn(delayedResponse())); } public void successWithRetryAfter() { - rda.when(request).respond(initialSuccessResponse()); - - var statusRequest = request().withMethod("GET").withPath(baseUrl + "/status/processId456"); - rda.when(statusRequest, Times.exactly(1)) - .respond(response().withStatusCode(202).withHeader(RETRY_AFTER, "2")); - rda.when(statusRequest, Times.exactly(1)) - .respond(response().withStatusCode(202).withHeader(RETRY_AFTER, "1")); - rda.when(statusRequest).respond(response().withStatusCode(200)); + rda.register(request.willReturn(initialSuccessResponse())); + + var statusRequest = get(urlPathEqualTo(BASE_PATH + "/status/processId456")); + sequentialMock(rda) + .then(statusRequest, accepted().withHeader(RETRY_AFTER, "2")) + .then(statusRequest, accepted().withHeader(RETRY_AFTER, "1")) + .thereafter(statusRequest, ok()); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockCohortSelector.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockCohortSelector.java index 0810b6ba..2e4baffe 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockCohortSelector.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockCohortSelector.java @@ -1,47 +1,67 @@ package care.smith.fts.cda.rest.it.mock; import static care.smith.fts.test.FhirGenerators.withPrefix; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; import static care.smith.fts.util.FhirUtils.fhirResourceToString; import static care.smith.fts.util.FhirUtils.toBundle; -import static java.lang.Math.min; +import static care.smith.fts.util.FhirUtils.typedResourceStream; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.matching.UrlPattern.ANY; +import static com.google.common.collect.Lists.partition; +import static java.lang.Math.ceilDiv; +import static java.util.stream.IntStream.range; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; +import static org.springframework.web.util.UriComponentsBuilder.fromUriString; import care.smith.fts.test.FhirGenerator; import care.smith.fts.test.FhirGenerators; -import care.smith.fts.util.MediaTypes; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.google.common.collect.Streams; import java.io.IOException; import java.util.LinkedList; import java.util.List; -import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.UriType; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.mockserver.model.MediaType; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; -import org.springframework.web.util.UriComponentsBuilder; @Slf4j public class MockCohortSelector { - private final MockServerClient tca; - private List urls = - List.of("/api/v2/cd/consented-patients/fetch-all", "/api/v2/cd/consented-patients/fetch"); + private final WireMock tca; + private final String basePath; - public MockCohortSelector(MockServerClient tca) { - this.tca = tca; + public static MockCohortSelector fetchAll(WireMockServer tca) { + return new MockCohortSelector(tca, "/api/v2/cd/consented-patients/fetch-all"); + } + + public static MockCohortSelector fetch(WireMockServer tca) { + return new MockCohortSelector(tca, "/api/v2/cd/consented-patients/fetch"); + } + + public MockCohortSelector(WireMockServer tca, String basePath) { + this.tca = new WireMock(tca); + this.basePath = basePath; } private FhirGenerator validConsent(Supplier patientId) { @@ -52,151 +72,177 @@ private FhirGenerator validConsent(Supplier patientId) { } } - public void consentForOnePatient(String patientId) throws IOException { - log.info("consentForOnePatient {}", patientId); + public void consentForOnePatient(String patientId) { consentForNPatients(patientId, 1); } - public void consentForNPatients(String idPrefix, int n) throws IOException { - consentForNPatientsWithPaging(idPrefix, n, n); + public void consentForNPatients(String idPrefix, int total) { + consentForNPatientsWithPaging(idPrefix, total, total); } - public void consentForNPatients(String idPrefix, int n, List statusCodes) - throws IOException { - consentForNPatientsWithPaging(idPrefix, n, n, statusCodes); + public void consentForNPatients(String idPrefix, int total, List statusCodes) { + consentForNPatientsWithPaging(idPrefix, total, total, statusCodes); } - public void consentForNPatientsWithPaging(String idPrefix, int total, int maxPageSize) - throws IOException { - consentForNPatientsWithPaging(idPrefix, total, maxPageSize, List.of()); + public void consentForNPatientsWithPaging(String idPrefix, int total, int maxPageSize) { + + var statusCodes = Stream.generate(() -> 200).limit(ceilDiv(total, maxPageSize)).toList(); + consentForNPatientsWithPaging(idPrefix, total, maxPageSize, statusCodes); } public void consentForNPatientsWithPaging( - String idPrefix, int total, int maxPageSize, List statusCodes) throws IOException { + String idPrefix, int total, int maxPageSize, List statusCodes) { assertThat(maxPageSize).isGreaterThan(0); assertThat(total).isGreaterThanOrEqualTo(maxPageSize); - var rs = new LinkedList<>(statusCodes); - - log.trace("total: {}, maxPageSize: {}", total, maxPageSize); - urls.forEach( - url -> { - FhirGenerator bundleFhirGenerator = - total > 1 ? validConsent(withPrefix(idPrefix)) : validConsent(() -> idPrefix); - - tca.when(request().withMethod("POST").withPath(url)) - .respond( - request -> { - log.trace("path: {}", request.getPath()); - return Optional.ofNullable(rs.poll()) - .map( - statusCode -> { - log.trace("statusCode: {}", statusCode); - return statusCode < 400 - ? successResponse( - url, - total, - maxPageSize, - request, - bundleFhirGenerator, - statusCode) - : response().withStatusCode(statusCode); - }) - .orElseGet( - () -> - successResponse( - url, total, maxPageSize, request, bundleFhirGenerator, 200)); - }); - }); - } - - private HttpResponse successResponse( - String url, - int total, - int maxPageSize, - HttpRequest request, - FhirGenerator bundleFhirGenerator, - int statusCode) { - log.trace("Generate response with status code: {}", statusCode); - var from = extractParameter(request, "from", 0); - var count = extractParameter(request, "count", maxPageSize); - log.trace("request params: from {}, count {}", from, count); - - var consent = - bundleFhirGenerator.generateResources().limit(count).collect(toBundle()).setTotal(total); - - consent = addNextLink(url, total, maxPageSize, from, count, consent); - return response() - .withStatusCode(statusCode) - .withContentType(MediaType.parse(MediaTypes.APPLICATION_FHIR_JSON_VALUE)) - .withBody(fhirResourceToString(consent)); - } - - private static int extractParameter(HttpRequest request, String name, int x) { - String fromRequest = request.getFirstQueryStringParameter(name); - return !fromRequest.isEmpty() ? Integer.parseInt(fromRequest) : x; - } - - private Bundle addNextLink( - String url, int total, int maxPageSize, int from, int count, Bundle consent) { - var nextFrom = from + count; - var nextCount = min(total - nextFrom, maxPageSize); - if (nextFrom < total) { - log.trace("Add nextLink count consent bundle"); - consent = consent.addLink(nextLink(url, nextFrom, nextCount)); + var consents = generateConsents(idPrefix, total); + var pages = new LinkedList<>(generatePages(consents, maxPageSize)); + var seq = sequentialMock(tca); + + var request = post(urlPathEqualTo(basePath)); + var butLast = statusCodes.subList(0, statusCodes.size() - 1); + for (var statusCode : butLast) { + var response = + statusCode < 400 + // May be out of bounds, meaning there is not enough consent for the + // number of statuses + ? successResponse(pages.poll(), statusCode) + : status(statusCode); + log.debug("Generate {} response", statusCode); + seq = seq.then(request, response); } - return consent; + log.debug("Thereafter respond with {}", statusCodes.getLast()); + seq.thereafter(request, successResponse(pages.poll(), statusCodes.getLast())); + } + + /** + * Generates a list of paginated Bundle objects from the given list of consent Bundles based on + * the specified maximum page size. + * + *

Each page is created by partitioning the input list into sublists of the given maximum size, + * wrapping these sublists into a Bundle, setting the total number of consents, and then linking + * the pages together with appropriate navigation links. + * + * @param consents the list of input consent Bundles to be partitioned into pages + * @param maxPageSize the maximum number of Bundles allowed per page + * @return a list of Bundle objects representing the paginated content + */ + private List generatePages(List consents, int maxPageSize) { + var pages = + new LinkedList<>(partition(consents, maxPageSize)) + .stream() + .map(c -> c.stream().collect(toBundle())) + .map(b -> b.setTotal(consents.size())) + .toList(); + var butLast = pages.subList(0, pages.size() - 1); + var last = pages.getLast(); + return Streams.concat( + range(0, butLast.size()) + .mapToObj( + page -> butLast.get(page).addLink(nextLink(++page * maxPageSize, maxPageSize))), + Stream.of(last)) + .toList(); } - private Bundle.BundleLinkComponent nextLink(String url, int from, int count) { + /** + * Generates a list of FHIR Bundle resources based on a given prefix and a specified total count. + * The generated resources will either use the provided prefix directly or a generated prefix if + * multiple items are required. + */ + private List generateConsents(String idPrefix, int total) { + return validConsent(total > 1 ? withPrefix(idPrefix) : () -> idPrefix) + .generateResources() + .limit(total) + .toList(); + } + + /** + * Generates a success response for the given FHIR Bundle with the specified HTTP status code. + * This method creates a response containing the bundle converted to a JSON string, and sets + * appropriate headers and status code for the response. + * + * @param bundle the FHIR Bundle to include in the response body + * @param statusCode the HTTP status code for the response + * @return a ResponseDefinitionBuilder configured with the provided bundle and status code + */ + private ResponseDefinitionBuilder successResponse(Bundle bundle, int statusCode) { + log.trace( + "Returning consent for {}", + typedResourceStream(bundle, Bundle.class) + .flatMap(b -> typedResourceStream(b, Patient.class)) + .map(Resource::getId) + .toList()); + return fhirResponse(bundle, statusCode); + } + + /** + * Creates a "next" link component for a FHIR Bundle. + * + * @param from the starting index for the next set of results + * @param count the number of results to be retrieved in the next set + * @return a Bundle.BundleLinkComponent representing the "next" link + */ + private Bundle.BundleLinkComponent nextLink(int from, int count) { var uri = - UriComponentsBuilder.fromUriString(url.formatted(tca.getPort())) + fromUriString(basePath) .replaceQueryParam("from", from) .replaceQueryParam("count", count) .toUriString(); - log.trace("Next link uri: {}", uri); - return new Bundle.BundleLinkComponent(new StringType("next"), new UriType(uri)); } + /** + * Simulates a scenario where the system/network connection is unavailable by responding with a + * connection reset fault. + * + *

This is used to mimic connectivity issues such as the remote server abruptly terminating the + * connection. + */ public void isDown() { - tca.when(request()).error(HttpError.error().withDropConnection(true)); + tca.register(any(ANY).willReturn(connectionReset())); } + /** + * Simulates a timeout scenario for HTTP requests. + * + *

This method configures a mock server to simulate a timeout by introducing a fixed delay of + * 10 minutes (600,000 milliseconds) before returning an HTTP response with a status code of 204. + * Useful for testing timeout handling mechanisms in applications. + */ public void timeout() { - tca.when(request().withMethod("POST")).respond(request -> null, Delay.minutes(10)); + tca.register(post(ANY).willReturn(delayedResponse())); } - public void wrongContentType() throws IOException { + /** + * Simulates a scenario where a response contains the wrong content type. This method registers a + * mocked HTTP POST request with a content type of "text/plain" instead of the expected content + * type, which is typically used for FHIR resources. + * + *

The method constructs a mock FHIR response bundled into a String and then configures the + * response with a 200 status code and the incorrect content type header. This setup is primarily + * used for testing the handling of unexpected or invalid content types in HTTP responses. + */ + public void wrongContentType() { var consent = Stream.of(validConsent(() -> "id1").generateResource()).collect(toBundle()); - urls.forEach( - url -> - tca.when(request().withMethod("POST").withPath(url)) - .respond( - response() - .withStatusCode(200) - .withContentType(MediaType.PLAIN_TEXT_UTF_8) - .withBody(fhirResourceToString(consent)))); - } - - public void unknownDomain(ObjectMapper om) { - urls.forEach( - url -> { - try { - tca.when(request().withMethod("POST").withPath(url)) - .respond( - response() - .withStatusCode(400) - .withContentType(MediaType.APPLICATION_JSON) - .withBody( - om.writeValueAsString( - ProblemDetail.forStatusAndDetail( - HttpStatus.BAD_REQUEST, - "No consents found for domain 'MII1234'")))); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); + var response = + ok().withHeader(CONTENT_TYPE, TEXT_PLAIN_VALUE).withBody(fhirResourceToString(consent)); + tca.register(post(urlPathEqualTo(basePath)).willReturn(response)); + } + + /** + * Registers a mock POST request to simulate an HTTP 400 Bad Request error with a specific + * ProblemDetail message indicating no consents were found for the specified domain. + * + * @param om the {@link ObjectMapper} used to serialize the ProblemDetail object into a JSON + * string for the mock response body + * @throws JsonProcessingException if an error occurs during JSON serialization + */ + public void unknownDomain(ObjectMapper om) throws JsonProcessingException { + String body = + om.writeValueAsString( + ProblemDetail.forStatusAndDetail( + HttpStatus.BAD_REQUEST, "No consents found for domain 'MII1234'")); + tca.register(post(urlPathEqualTo(basePath)).willReturn(jsonResponse(body, 400))); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockDataSelector.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockDataSelector.java index 700c60af..f5338c6c 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockDataSelector.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockDataSelector.java @@ -1,40 +1,46 @@ package care.smith.fts.cda.rest.it.mock; import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON_VALUE; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.JsonBody.json; -import static org.mockserver.model.MediaType.APPLICATION_JSON; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.time.Duration.ofDays; +import static java.util.Map.entry; +import static java.util.Map.ofEntries; +import static org.springframework.http.HttpHeaders.ACCEPT; -import care.smith.fts.util.tca.TransportMappingRequest; import care.smith.fts.util.tca.TCADomains; +import care.smith.fts.util.tca.TransportMappingRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.time.Duration; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; import java.util.Set; -import org.mockserver.client.MockServerClient; public class MockDataSelector { private final ObjectMapper om; - private final MockServerClient hds; - private final MockServerClient tca; + private final WireMock hds; + private final WireMock tca; - public MockDataSelector(ObjectMapper om, MockServerClient tca, MockServerClient hds) { + public MockDataSelector(ObjectMapper om, WireMockServer tca, WireMockServer hds) { this.om = om; - this.tca = tca; - this.hds = hds; + this.tca = new WireMock(tca); + this.hds = new WireMock(hds); } public MockFetchData whenFetchData(String patientId) { return MockFetchData.builder() .hds(hds) .mockRequestSpec( - request() - .withMethod("GET") - .withHeader("accept", APPLICATION_FHIR_JSON_VALUE) - .withPath("/Patient/%s/$everything".formatted(patientId)) - .withQueryStringParameter("start", "2023-07-29") - .withQueryStringParameter("end", "2028-07-29")) + get(urlPathEqualTo("/Patient/%s/$everything".formatted(patientId))) + .withHeader(ACCEPT, equalTo(APPLICATION_FHIR_JSON_VALUE)) + .withQueryParams( + ofEntries( + entry("start", equalTo("2023-07-29")), + entry("end", equalTo("2028-07-29"))))) .build(); } @@ -42,11 +48,9 @@ public MockFhirResolveService whenResolvePatient(String patientId, String identi return MockFhirResolveService.builder() .hds(hds) .mockRequestSpec( - request() - .withMethod("GET") - .withHeader("accept", APPLICATION_FHIR_JSON_VALUE) - .withPath("/Patient") - .withQueryStringParameter("identifier", identifierSystem + "|" + patientId)) + get(urlPathEqualTo("/Patient")) + .withHeader(ACCEPT, equalTo(APPLICATION_FHIR_JSON_VALUE)) + .withQueryParam("identifier", equalTo(identifierSystem + "|" + patientId))) .build(); } @@ -55,20 +59,17 @@ public MockTransportIds whenTransportMapping(String patientId, String identifier var id1 = patientId + ".identifier." + identifierSystem + ":" + patientId; var id2 = patientId + ".Patient:" + patientId; - Set ids = Set.of(id1, id2); - var pseudonymizeRequest = - new TransportMappingRequest( - patientId, ids, new TCADomains("MII", "MII", "MII"), Duration.ofDays(14)); + var ids = Set.of(id1, id2); + var tcaDomains = new TCADomains("MII", "MII", "MII"); + var pseudonymizeRequest = new TransportMappingRequest(patientId, ids, tcaDomains, ofDays(14)); return MockTransportIds.builder() .tca(tca) .transportIds(ids) .om(om) .mockRequestSpec( - request() - .withMethod("POST") - .withContentType(APPLICATION_JSON) - .withPath("/api/v2/cd/transport-mapping") - .withBody(json(om.writeValueAsString(pseudonymizeRequest)))) + post(urlPathEqualTo("/api/v2/cd/transport-mapping")) + .withRequestBody( + equalToJson(om.writeValueAsString(pseudonymizeRequest), true, true))) .build(); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFetchData.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFetchData.java index af144d33..6f75873f 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFetchData.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFetchData.java @@ -1,75 +1,72 @@ package care.smith.fts.cda.rest.it.mock; -import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON_VALUE; -import static org.mockserver.model.HttpResponse.response; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; +import static care.smith.fts.util.FhirUtils.fhirResourceToString; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.notFound; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.matching.UrlPattern.ANY; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; -import care.smith.fts.util.FhirUtils; -import java.util.LinkedList; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; import java.util.List; -import java.util.Optional; import lombok.Builder; +import lombok.extern.slf4j.Slf4j; import org.hl7.fhir.r4.model.Bundle; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.mockserver.model.MediaType; +@Slf4j @Builder public class MockFetchData { - private final MockServerClient hds; - private final HttpRequest mockRequestSpec; + private final WireMock hds; + private final MappingBuilder mockRequestSpec; - public MockFetchData(MockServerClient hds, HttpRequest mockRequestSpec) { + public MockFetchData(WireMock hds, MappingBuilder mockRequestSpec) { this.hds = hds; this.mockRequestSpec = mockRequestSpec; } public void respondWith(Bundle patient) { - respondWith(patient, List.of()); + respondWith(patient, List.of(200)); } public void respondWith(Bundle patient, List statusCodes) { - var rs = new LinkedList<>(statusCodes); - hds.when(mockRequestSpec) - .respond( - request -> - Optional.ofNullable(rs.poll()) - .map( - statusCode -> - statusCode < 400 - ? successResponse(statusCode, patient) - : response().withStatusCode(statusCode)) - .orElseGet(() -> successResponse(200, patient))); + var seq = sequentialMock(hds); + var butLast = statusCodes.subList(0, statusCodes.size() - 1); + for (var statusCode : butLast) { + var response = statusCode < 400 ? successResponse(statusCode, patient) : status(statusCode); + seq = seq.then(mockRequestSpec, response); + } + seq.thereafter(mockRequestSpec, successResponse(statusCodes.getLast(), patient)); } - private static HttpResponse successResponse(int statusCode, Bundle patient) { - return response() - .withStatusCode(statusCode) - .withContentType(MediaType.parse(APPLICATION_FHIR_JSON_VALUE)) - .withBody(FhirUtils.fhirResourceToString(patient)); + private static ResponseDefinitionBuilder successResponse(int statusCode, Bundle patient) { + return fhirResponse(patient, statusCode); } public void dropConnection() { - hds.when(mockRequestSpec).error(HttpError.error().withDropConnection(true)); + hds.register(any(ANY).willReturn(connectionReset())); } public void timeout() { - hds.when(mockRequestSpec).respond(request -> null, Delay.minutes(10)); + hds.register(any(ANY).willReturn(delayedResponse())); } public void respondWithWrongContentType() { - hds.when(mockRequestSpec) - .respond( - response() - .withStatusCode(200) - .withContentType(MediaType.PLAIN_TEXT_UTF_8) - .withBody(FhirUtils.fhirResourceToString(new Bundle()))); + hds.register( + mockRequestSpec.willReturn( + ok().withHeader(CONTENT_TYPE, TEXT_PLAIN_VALUE) + .withBody(fhirResourceToString(new Bundle())))); } public void respondWithEmptyBundle() { - hds.when(mockRequestSpec).respond(response().withStatusCode(404)); + hds.register(mockRequestSpec.willReturn(notFound())); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFhirResolveService.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFhirResolveService.java index fafaba14..d1cdbe95 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFhirResolveService.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockFhirResolveService.java @@ -1,100 +1,82 @@ package care.smith.fts.cda.rest.it.mock; import static care.smith.fts.test.FhirGenerators.randomUuid; +import static care.smith.fts.test.FhirGenerators.resolveSearchResponse; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; import static care.smith.fts.util.FhirUtils.fhirResourceToString; import static care.smith.fts.util.FhirUtils.toBundle; -import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON_VALUE; -import static org.mockserver.model.HttpResponse.response; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; import care.smith.fts.test.FhirGenerator; -import care.smith.fts.test.FhirGenerators; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; import java.io.IOException; -import java.util.LinkedList; import java.util.List; -import java.util.Optional; import lombok.Builder; import org.hl7.fhir.r4.model.Bundle; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.mockserver.model.MediaType; @Builder public class MockFhirResolveService { - private final MockServerClient hds; - private final HttpRequest mockRequestSpec; + private final WireMock hds; + private final MappingBuilder mockRequestSpec; - public MockFhirResolveService(MockServerClient hds, HttpRequest mockRequestSpec) { + public MockFhirResolveService(WireMock hds, MappingBuilder mockRequestSpec) { this.hds = hds; this.mockRequestSpec = mockRequestSpec; } public void resolveId(String patientId) throws IOException { - resolveId(patientId, List.of()); + resolveId(patientId, List.of(200)); } public void resolveId(String patientId, List statusCodes) throws IOException { - var fhirResolveGen = FhirGenerators.resolveSearchResponse(() -> patientId, randomUuid()); - var rs = new LinkedList<>(statusCodes); - hds.when(mockRequestSpec) - .respond( - request -> - Optional.ofNullable(rs.poll()) - .map( - statusCode -> - statusCode < 400 - ? successResponse(statusCode, fhirResolveGen) - : response().withStatusCode(statusCode)) - .orElseGet(() -> successResponse(200, fhirResolveGen))); + var fhirResolveGen = resolveSearchResponse(() -> patientId, randomUuid()); + var seq = sequentialMock(hds); + var butLast = statusCodes.subList(0, statusCodes.size() - 1); + for (var statusCode : butLast) { + var response = + statusCode < 400 ? successResponse(statusCode, fhirResolveGen) : status(statusCode); + seq.then(mockRequestSpec, response); + } + seq.thereafter(mockRequestSpec, successResponse(statusCodes.getLast(), fhirResolveGen)); } - private static HttpResponse successResponse( - Integer statusCode, FhirGenerator fhirResolveGen) { - return response() - .withStatusCode(statusCode) - .withContentType(MediaType.parse(APPLICATION_FHIR_JSON_VALUE)) - .withBody(fhirResourceToString(fhirResolveGen.generateResource())); + private static ResponseDefinitionBuilder successResponse( + int statusCode, FhirGenerator fhirResolveGen) { + return fhirResponse(fhirResolveGen.generateResource(), statusCode); } public void isDown() { - hds.when(mockRequestSpec).error(HttpError.error().withDropConnection(true)); + hds.register(mockRequestSpec.willReturn(connectionReset())); } public void timeout() { - hds.when(mockRequestSpec).respond(request -> null, Delay.minutes(10)); + hds.register(mockRequestSpec.willReturn(delayedResponse())); } public void wrongContentType() { - hds.when(mockRequestSpec) - .respond( - response() - .withStatusCode(200) - .withContentType(MediaType.PLAIN_TEXT_UTF_8) - .withBody(fhirResourceToString(new Bundle()))); + hds.register( + mockRequestSpec.willReturn( + ok().withHeader(CONTENT_TYPE, TEXT_PLAIN_VALUE) + .withBody(fhirResourceToString(new Bundle())))); } public void moreThanOneResult() throws IOException { - var fhirResolveGen = FhirGenerators.resolveSearchResponse(() -> "id1", randomUuid()); + var fhirResolveGen = resolveSearchResponse(() -> "id1", randomUuid()); - hds.when(mockRequestSpec) - .respond( - response() - .withStatusCode(200) - .withContentType(MediaType.parse(APPLICATION_FHIR_JSON_VALUE)) - .withBody( - fhirResourceToString( - fhirResolveGen.generateResources().limit(2).collect(toBundle())))); + var bundle = fhirResolveGen.generateResources().limit(2).collect(toBundle()); + hds.register(mockRequestSpec.willReturn(fhirResponse(bundle, 200))); } public void emptyBundle() { - hds.when(mockRequestSpec) - .respond( - response() - .withStatusCode(200) - .withContentType(MediaType.parse(APPLICATION_FHIR_JSON_VALUE)) - .withBody(fhirResourceToString(new Bundle()))); + hds.register(mockRequestSpec.willReturn(fhirResponse(new Bundle(), 200))); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockTransportIds.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockTransportIds.java index 5e02a64c..84542406 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockTransportIds.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/rest/it/mock/MockTransportIds.java @@ -1,25 +1,24 @@ package care.smith.fts.cda.rest.it.mock; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static java.time.Duration.ofDays; +import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.MediaType.APPLICATION_JSON; import care.smith.fts.util.tca.TransportMappingResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.time.Duration; -import java.util.LinkedList; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.function.Function; import lombok.Builder; import lombok.extern.slf4j.Slf4j; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; @@ -28,15 +27,12 @@ public class MockTransportIds { private final ObjectMapper om; - private final MockServerClient tca; - private final HttpRequest mockRequestSpec; + private final WireMock tca; + private final MappingBuilder mockRequestSpec; private final Set transportIds; public MockTransportIds( - ObjectMapper om, - MockServerClient tca, - HttpRequest mockRequestSpec, - Set transportIds) { + ObjectMapper om, WireMock tca, MappingBuilder mockRequestSpec, Set transportIds) { this.om = om; this.tca = tca; this.mockRequestSpec = mockRequestSpec; @@ -44,50 +40,40 @@ public MockTransportIds( } public void success() throws JsonProcessingException { - var transportMapping = transportIds.stream().collect(toMap(Function.identity(), Function.identity())); - var pseudonymizeResponse = new TransportMappingResponse("transferId", transportMapping, Duration.ofDays(1)); - tca.when(mockRequestSpec) - .respond(successResponse(200, om.writeValueAsString(pseudonymizeResponse))); + var transportMapping = transportIds.stream().collect(toMap(identity(), identity())); + var pseudonymizeResponse = + new TransportMappingResponse("transferId", transportMapping, ofDays(1)); + tca.register( + mockRequestSpec.willReturn(jsonResponse(om.writeValueAsString(pseudonymizeResponse), 200))); } public void isDown() { - tca.when(mockRequestSpec).error(HttpError.error().withDropConnection(true)); + tca.register(mockRequestSpec.willReturn(connectionReset())); } public void timeout() { - tca.when(mockRequestSpec).respond(request -> null, Delay.minutes(10)); + tca.register(mockRequestSpec.willReturn(delayedResponse())); } public void unknownDomain(ObjectMapper om) throws JsonProcessingException { - tca.when(mockRequestSpec) - .respond( - successResponse( - 400, + tca.register( + mockRequestSpec.willReturn( + jsonResponse( om.writeValueAsString( ProblemDetail.forStatusAndDetail( - HttpStatus.BAD_REQUEST, "Unknown domain 'MII'")))); + HttpStatus.BAD_REQUEST, "Unknown domain 'MII'")), + 400))); } public void successWithStatusCode(List statusCodes) throws JsonProcessingException { - var tidMap = transportIds.stream().collect(toMap(Function.identity(), Function.identity())); + var tidMap = transportIds.stream().collect(toMap(identity(), identity())); var pseudonymizeResponse = - om.writeValueAsString(new TransportMappingResponse("transferId", tidMap, Duration.ofDays(1))); - var rs = new LinkedList<>(statusCodes); - tca.when(mockRequestSpec) - .respond( - request -> - Optional.ofNullable(rs.poll()) - .map( - statusCode -> { - log.trace("statusCode: {}", statusCode); - return statusCode < 400 - ? successResponse(200, pseudonymizeResponse) - : response().withStatusCode(statusCode); - }) - .orElseGet(() -> successResponse(200, pseudonymizeResponse))); - } + om.writeValueAsString(new TransportMappingResponse("transferId", tidMap, ofDays(1))); - private HttpResponse successResponse(int statusCode, String om) { - return response().withStatusCode(statusCode).withContentType(APPLICATION_JSON).withBody(om); + var seq = sequentialMock(tca); + for (var statusCode : statusCodes) { + seq.then(mockRequestSpec, statusCode < 400 ? ok(pseudonymizeResponse) : status(statusCode)); + } + seq.thereafter(mockRequestSpec, jsonResponse(pseudonymizeResponse, 200)); } } diff --git a/clinical-domain-agent/src/test/java/care/smith/fts/cda/services/FhirResolveServiceTest.java b/clinical-domain-agent/src/test/java/care/smith/fts/cda/services/FhirResolveServiceTest.java index c6e66eb6..31a7162f 100644 --- a/clinical-domain-agent/src/test/java/care/smith/fts/cda/services/FhirResolveServiceTest.java +++ b/clinical-domain-agent/src/test/java/care/smith/fts/cda/services/FhirResolveServiceTest.java @@ -1,57 +1,58 @@ package care.smith.fts.cda.services; import static care.smith.fts.test.MockServerUtil.clientConfig; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.*; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; +import static org.springframework.http.HttpStatus.OK; import static reactor.test.StepVerifier.create; import care.smith.fts.cda.ClinicalDomainAgent; import care.smith.fts.test.MockServerUtil; import care.smith.fts.util.WebClientFactory; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; -import org.mockserver.model.Header; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest(classes = ClinicalDomainAgent.class) -@ExtendWith(MockServerExtension.class) +@WireMockTest class FhirResolveServiceTest { private static final String PATIENT_ID = "patient-141392"; - private static final Header CONTENT_JSON = new Header("Content-Type", "application/json"); private static final String KDS_PATIENT = "https://some.example.com/pid"; @Autowired MeterRegistry meterRegistry; private FhirResolveService service; + private WireMock wireMock; @BeforeEach - void setUp(MockServerClient mockServer, @Autowired WebClientFactory clientFactory) throws Exception { - var client = clientFactory.create(clientConfig(mockServer)); + void setUp(WireMockRuntimeInfo wiremockRuntime, @Autowired WebClientFactory clientFactory) + throws Exception { + var client = clientFactory.create(clientConfig(wiremockRuntime)); this.service = new FhirResolveService(KDS_PATIENT, client, meterRegistry); + wireMock = wiremockRuntime.getWireMock(); try (var inStream = MockServerUtil.getResourceAsStream("metadata.json")) { var capStatement = requireNonNull(inStream).readAllBytes(); - mockServer - .when(request().withMethod("GET").withPath("/metadata")) - .respond(response().withBody(capStatement).withHeader(CONTENT_JSON)); + wireMock.register(get("/metadata").willReturn(jsonResponse(capStatement, OK.value()))); } } @Test - void noPatientsErrors(MockServerClient mockServer) throws Exception { + void noPatientsErrors() throws Exception { try (var inStream = getClass().getResourceAsStream("search-0.json")) { - var bundle = requireNonNull(inStream).readAllBytes(); - mockServer - .when(request().withMethod("GET").withPath("/Patient")) - .respond(response().withBody(bundle).withHeader(CONTENT_JSON)); + byte[] bundle = requireNonNull(inStream).readAllBytes(); + var response = jsonResponse(new String(bundle, UTF_8), 200); + wireMock.register(get(urlPathEqualTo("/Patient")).willReturn(response)); } create(service.resolve("external-141392")) @@ -62,12 +63,11 @@ void noPatientsErrors(MockServerClient mockServer) throws Exception { } @Test - void resolveFindsPatientId(MockServerClient mockServer) throws Exception { + void resolveFindsPatientId() throws Exception { try (var inStream = getClass().getResourceAsStream("search-1.json")) { var bundle = requireNonNull(inStream).readAllBytes(); - mockServer - .when(request().withMethod("GET").withPath("/Patient")) - .respond(response().withBody(bundle).withHeader(CONTENT_JSON)); + var response = jsonResponse(new String(bundle, UTF_8), 200); + wireMock.register(get(urlPathEqualTo("/Patient")).willReturn(response)); } create(service.resolve("external-141392")) @@ -76,12 +76,11 @@ void resolveFindsPatientId(MockServerClient mockServer) throws Exception { } @Test - void multiplePatientsError(MockServerClient mockServer) throws Exception { + void multiplePatientsError() throws Exception { try (var inStream = getClass().getResourceAsStream("search-2.json")) { var bundle = requireNonNull(inStream).readAllBytes(); - mockServer - .when(request().withMethod("GET").withPath("/Patient")) - .respond(response().withBody(bundle).withHeader(CONTENT_JSON)); + var response = jsonResponse(new String(bundle, UTF_8), 200); + wireMock.register(get(urlPathEqualTo("/Patient")).willReturn(response)); } create(service.resolve("external-075521")) @@ -106,7 +105,7 @@ void emptyPatientIdThrows() { } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } } diff --git a/clinical-domain-agent/src/test/resources/application.yaml b/clinical-domain-agent/src/test/resources/application.yaml index bb611196..a259197f 100644 --- a/clinical-domain-agent/src/test/resources/application.yaml +++ b/clinical-domain-agent/src/test/resources/application.yaml @@ -37,6 +37,5 @@ security: role: client logging.level: - org.mockserver.log: WARN care.smith.fts: DEBUG care.smith.fts.cda: TRACE diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/impl/DeidentifhirStepTest.java b/research-domain-agent/src/test/java/care/smith/fts/rda/impl/DeidentifhirStepTest.java index b8bc9854..02ef5e0d 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/impl/DeidentifhirStepTest.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/impl/DeidentifhirStepTest.java @@ -2,16 +2,20 @@ import static care.smith.fts.test.MockServerUtil.clientConfig; import static care.smith.fts.test.TestPatientGenerator.generateOnePatient; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.typesafe.config.ConfigFactory.parseResources; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; import static reactor.test.StepVerifier.create; import care.smith.fts.api.TransportBundle; import care.smith.fts.rda.services.deidentifhir.DeidentifhirUtil; import care.smith.fts.util.WebClientFactory; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import java.io.IOException; import org.hl7.fhir.r4.model.Bundle; @@ -19,39 +23,35 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -@ExtendWith(MockServerExtension.class) +@WireMockTest class DeidentifhirStepTest { @Autowired MeterRegistry meterRegistry; private DeidentifhirStep step; + private WireMock wireMock; @BeforeEach - void setUp(MockServerClient mockServer, @Autowired WebClientFactory clientFactory) { + void setUp(WireMockRuntimeInfo wireMockRuntime, @Autowired WebClientFactory clientFactory) { var config = parseResources(DeidentifhirUtil.class, "TransportToRD.profile"); - var client = clientFactory.create(clientConfig(mockServer)); + var client = clientFactory.create(clientConfig(wireMockRuntime)); step = new DeidentifhirStep(config, client, meterRegistry); + wireMock = wireMockRuntime.getWireMock(); } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } @Test - void correctRequestSent(MockServerClient mockServer) throws IOException { - mockServer - .when( - request() - .withMethod("POST") - .withPath("/api/v2/rd/research-mapping") - .withBody("transferId")) - .respond(response().withStatusCode(200)); + void correctRequestSent() throws IOException { + wireMock.register( + WireMock.post(urlPathEqualTo("/api/v2/rd/research-mapping")) + .withRequestBody(equalTo("transferId")) + .willReturn(ok())); var bundle = generateOnePatient("tid1", "2024", "identifierSystem"); @@ -59,10 +59,9 @@ void correctRequestSent(MockServerClient mockServer) throws IOException { } @Test - void emptyTCAResponseYieldsEmptyResult(MockServerClient mockServer) throws IOException { - mockServer - .when(request().withMethod("POST").withPath("/api/v2/rd/research-mapping")) - .respond(response().withStatusCode(200)); + void emptyTCAResponseYieldsEmptyResult() throws IOException { + wireMock.register( + WireMock.post(urlPathEqualTo("/api/v2/rd/research-mapping")).willReturn(ok())); var bundle = generateOnePatient("tid1", "2024", "identifierSystem"); @@ -70,22 +69,17 @@ void emptyTCAResponseYieldsEmptyResult(MockServerClient mockServer) throws IOExc } @Test - void deidentifySucceeds(MockServerClient mockServer) throws IOException { - mockServer - .when( - request() - .withMethod("POST") - .withPath("/api/v2/rd/research-mapping") - .withBody("transferId")) - .respond( - response() - .withBody( - json( - """ - {"tidPidMap": {"tid1": "pid1"}, - "dateShiftBy": "P12D"} - """)) - .withStatusCode(200)); + void deidentifySucceeds() throws IOException { + wireMock.register( + WireMock.post(urlPathEqualTo("/api/v2/rd/research-mapping")) + .withRequestBody(equalTo("transferId")) + .willReturn( + jsonResponse( + """ + {"tidPidMap": {"tid1": "pid1"}, + "dateShiftBy": "P12D"} + """, + 200))); var bundle = generateOnePatient("tid1", "2024", "identifierSystem"); diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/impl/FhirStoreBundleSenderTest.java b/research-domain-agent/src/test/java/care/smith/fts/rda/impl/FhirStoreBundleSenderTest.java index f60d8e92..2fabdee8 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/impl/FhirStoreBundleSenderTest.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/impl/FhirStoreBundleSenderTest.java @@ -1,45 +1,46 @@ package care.smith.fts.rda.impl; import static care.smith.fts.test.MockServerUtil.clientConfig; -import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.OK; +import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON_VALUE; +import static com.github.tomakehurst.wiremock.client.WireMock.badRequest; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.matching.UrlPattern.ANY; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static reactor.test.StepVerifier.create; import care.smith.fts.api.rda.BundleSender; import care.smith.fts.util.WebClientFactory; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import org.hl7.fhir.r4.model.Bundle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; -import org.mockserver.model.MediaType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.web.reactive.function.client.WebClient; @SpringBootTest -@ExtendWith(MockServerExtension.class) +@WireMockTest class FhirStoreBundleSenderTest { @Autowired MeterRegistry meterRegistry; private WebClient client; + private WireMock wireMock; @BeforeEach - void setUp(MockServerClient mockServer, @Autowired WebClientFactory clientFactory) { - client = clientFactory.create(clientConfig(mockServer)); + void setUp(WireMockRuntimeInfo wireMockRuntime, @Autowired WebClientFactory clientFactory) { + client = clientFactory.create(clientConfig(wireMockRuntime)); + wireMock = wireMockRuntime.getWireMock(); } @Test - void requestErrors(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST")) - .respond(response().withStatusCode(BAD_REQUEST.value())); + void requestErrors() { + wireMock.register(post(ANY).willReturn(badRequest())); var bundleSender = new FhirStoreBundleSender(client, meterRegistry); @@ -47,20 +48,16 @@ void requestErrors(MockServerClient mockServer) { } @Test - void bundleSent(MockServerClient mockServer) { - mockServer - .when( - request() - .withContentType(MediaType.parse(APPLICATION_FHIR_JSON.toString())) - .withMethod("POST")) - .respond(response().withStatusCode(OK.value())); + void bundleSent() { + wireMock.register( + post(ANY).withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON_VALUE)).willReturn(ok())); var bundleSender = new FhirStoreBundleSender(client, meterRegistry); create(bundleSender.send(new Bundle())).expectNext(new BundleSender.Result()).verifyComplete(); } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } } diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BaseIT.java b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BaseIT.java index 8920f53d..c22ba0ea 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BaseIT.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BaseIT.java @@ -4,19 +4,21 @@ import static org.springframework.util.FileSystemUtils.deleteRecursively; import care.smith.fts.test.MockServerUtil; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.junit.jupiter.api.AfterAll; -import org.mockserver.client.MockServerClient; +import org.junit.jupiter.api.BeforeAll; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; public abstract class BaseIT { private static final Path tempDir; - protected static final MockServerClient hds; - protected static final MockServerClient tca; + protected static final WireMockServer hds; + protected static final WireMockServer tca; @AfterAll static void afterAll() throws IOException { @@ -29,15 +31,15 @@ static void registerTempDir(DynamicPropertyRegistry registry) { } protected static void resetAll() { - tca.reset(); - hds.reset(); + tca.resetAll(); + hds.resetAll(); } static { try { tempDir = Files.createTempDirectory("ftsit"); - hds = hdsMockServer(); - tca = tcaMockServer(); + hds = MockServerUtil.onRandomPort(); + tca = MockServerUtil.onRandomPort(); createProject(); } catch (IOException e) { throw new IllegalStateException("Unable to create project config file", e); @@ -51,17 +53,9 @@ private static void createProject() throws IOException { var outStream = Files.newOutputStream(projectFile)) { var config = new String(inStream.readAllBytes(), UTF_8) - .replace("", "http://localhost:%d".formatted(tca.getPort())) - .replace("", "http://localhost:%d".formatted(hds.getPort())); + .replace("", tca.baseUrl()) + .replace("", hds.baseUrl()); outStream.write(config.getBytes(UTF_8)); } } - - private static MockServerClient tcaMockServer() throws IOException { - return MockServerUtil.onRandomPort(); - } - - private static MockServerClient hdsMockServer() throws IOException { - return MockServerUtil.onRandomPort(); - } } diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BundleSenderIT.java b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BundleSenderIT.java index 3ea13646..d95635cd 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BundleSenderIT.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/BundleSenderIT.java @@ -21,7 +21,7 @@ void setUp() throws IOException { } @Test - void hdsDown() { + void hdsDown() throws IOException { mockDeidentifier.success(); mockBundleSender.isDown(); @@ -31,7 +31,7 @@ void hdsDown() { } @Test - void hdsTimeout() { + void hdsTimeout() throws IOException { mockDeidentifier.success(); mockBundleSender.hasTimeout(); @@ -42,7 +42,7 @@ void hdsTimeout() { } @Test - void hdsFirstRequestFails() { + void hdsFirstRequestFails() throws IOException { mockDeidentifier.success(); mockBundleSender.success(List.of(500)); diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/DeidentifierIT.java b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/DeidentifierIT.java index 5063a0fa..d983167e 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/DeidentifierIT.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/DeidentifierIT.java @@ -45,7 +45,7 @@ void tcaReturnsWrongContentType() { } @Test - void tcaFirstRequestFails() { + void tcaFirstRequestFails() throws IOException { mockDeidentifier.success(List.of(500)); mockBundleSender.success(); diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/TransferProcessControllerIT.java b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/TransferProcessControllerIT.java index df3804f8..1d1eb7aa 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/TransferProcessControllerIT.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/TransferProcessControllerIT.java @@ -55,18 +55,20 @@ public class TransferProcessControllerIT extends BaseIT { protected final ObjectMapper om = new ObjectMapper().registerModule(new JavaTimeModule()); - protected final MockDeidentifier mockDeidentifier = new MockDeidentifier(om, tca); - protected final MockBundleSender mockBundleSender = new MockBundleSender(hds); - + protected MockDeidentifier mockDeidentifier; + protected MockBundleSender mockBundleSender; @BeforeEach - void setUp(@LocalServerPort int port, @Autowired TestWebClientFactory factory) { + final void setUpTransferProcessControllerIT( + @LocalServerPort int port, @Autowired TestWebClientFactory factory) { this.port = port; client = factory.webClient("https://localhost:" + port); + mockDeidentifier = new MockDeidentifier(om, tca); + mockBundleSender = new MockBundleSender(hds); } @AfterEach - void tearDown() { + final void tearDownTransferProcessControllerIT() { resetAll(); } @@ -74,7 +76,8 @@ protected FirstStep startProcess(Duration timeout, Bundle bundle) { return startProcess(timeout, bundle, s -> s.phase() != RUNNING); } - protected FirstStep startProcess(Duration timeout, Bundle bundle, Predicate until) { + protected FirstStep startProcess( + Duration timeout, Bundle bundle, Predicate until) { return client .post() .uri("/api/v2/process/test/patient") @@ -83,12 +86,13 @@ protected FirstStep startProcess(Duration timeout, Bundle bundle, Predic .retrieve() .toBodilessEntity() .mapNotNull(r -> r.getHeaders().get("Content-Location")) - .flatMapMany(r -> Flux.interval(Duration.ofMillis(0), Duration.ofMillis(500)) - .flatMap(i -> retrieveStatus(r)) - .takeUntil(until) - .take(timeout) - .timeout(timeout, retrieveStatus(r)) - ) + .flatMapMany( + r -> + Flux.interval(Duration.ofMillis(0), Duration.ofMillis(500)) + .flatMap(i -> retrieveStatus(r)) + .takeUntil(until) + .take(timeout) + .timeout(timeout, retrieveStatus(r))) .last() .as(StepVerifier::create); } diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockBundleSender.java b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockBundleSender.java index 3e8aac33..570a44ab 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockBundleSender.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockBundleSender.java @@ -1,31 +1,35 @@ package care.smith.fts.rda.rest.it.mock; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON_VALUE; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; - -import java.util.LinkedList; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.matching.UrlPattern.ANY; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; import java.util.List; -import java.util.Optional; -import org.mockserver.client.MockServerClient; -import org.mockserver.matchers.MatchType; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.MediaType; public class MockBundleSender { - private final MockServerClient hds; - private final HttpRequest request; - public MockBundleSender(MockServerClient hds) { - this.hds = hds; + public static final String SCENARIO_NAME = "hdsSequentialRequests"; + private final WireMock hds; + private final MappingBuilder request; + + public MockBundleSender(WireMockServer hds) { + this.hds = new WireMock(hds); request = - request() - .withMethod("POST") - .withContentType(MediaType.parse(APPLICATION_FHIR_JSON_VALUE)) - .withBody(json("{\"resourceType\":\"Bundle\"}", MatchType.ONLY_MATCHING_FIELDS)); + post(ANY) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON_VALUE)) + .withRequestBody(equalToJson("{\"resourceType\":\"Bundle\"}", true, true)); } public void success() { @@ -33,20 +37,18 @@ public void success() { } public void success(List statusCodes) { - var rs = new LinkedList<>(statusCodes); - hds.when(request) - .respond( - request -> - Optional.ofNullable(rs.poll()) - .map(statusCode -> response().withStatusCode(statusCode)) - .orElseGet(() -> response().withStatusCode(200))); + var seq = sequentialMock(hds); + for (int statusCode : statusCodes) { + seq = seq.then(request, status(statusCode)); + } + seq.thereafter(request, ok()); } public void isDown() { - hds.when(request()).error(HttpError.error().withDropConnection(true)); + hds.register(any(ANY).willReturn(connectionReset())); } public void hasTimeout() { - hds.when(request()).respond(request -> null, Delay.minutes(10)); + hds.register(any(ANY).willReturn(delayedResponse())); } } diff --git a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockDeidentifier.java b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockDeidentifier.java index 64d429e3..567e4496 100644 --- a/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockDeidentifier.java +++ b/research-domain-agent/src/test/java/care/smith/fts/rda/rest/it/mock/MockDeidentifier.java @@ -1,76 +1,74 @@ package care.smith.fts.rda.rest.it.mock; +import static care.smith.fts.test.MockServerUtil.connectionReset; +import static care.smith.fts.test.MockServerUtil.delayedResponse; +import static care.smith.fts.test.MockServerUtil.sequentialMock; import static care.smith.fts.test.TidPidMap.getTidPidMapAsJson; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.MediaType.APPLICATION_JSON; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.matching.UrlPattern.ANY; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE; import care.smith.fts.util.tca.ResearchMappingResponse; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; +import java.io.IOException; import java.time.Duration; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.Delay; -import org.mockserver.model.HttpError; -import org.mockserver.model.HttpResponse; -import org.mockserver.model.MediaType; public class MockDeidentifier { + public static final String SCENARIO_NAME = "tcaSequentialRequests"; private final ObjectMapper om; - private final MockServerClient tca; + private final WireMock tca; - public MockDeidentifier(ObjectMapper om, MockServerClient tca) { + public MockDeidentifier(ObjectMapper om, WireMockServer tca) { this.om = om; - this.tca = tca; + this.tca = new WireMock(tca); } - public void success() { + public void success() throws IOException { success(List.of()); } - public void success(List statusCodes) { - var rs = new LinkedList<>(statusCodes); - tca.when( - request() - .withMethod("POST") - .withPath("/api/v2/rd/research-mapping") - .withContentType(APPLICATION_JSON)) - .respond( - request -> { - var tidPidMap = om.readValue(getTidPidMapAsJson(), Map.class); - var resolveResponse = new ResearchMappingResponse(tidPidMap, Duration.ofMillis(12345)); - var body = om.writeValueAsString(resolveResponse); - return Optional.ofNullable(rs.poll()) - .map( - statusCode -> - statusCode < 400 - ? successResponse(statusCode, body) - : response().withStatusCode(statusCode)) - .orElseGet(() -> successResponse(200, body)); - }); - } + public void success(List statusCodes) throws IOException { + var tidPidMap = om.readValue(getTidPidMapAsJson(), new TypeReference>() {}); + var resolveResponse = new ResearchMappingResponse(tidPidMap, Duration.ofMillis(12345)); + var body = om.writeValueAsString(resolveResponse); - private HttpResponse successResponse(int statusCode, String body) { - return response().withStatusCode(statusCode).withContentType(APPLICATION_JSON).withBody(body); + var request = + post("/api/v2/rd/research-mapping") + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON_VALUE)); + var seq = sequentialMock(tca); + for (int statusCode : statusCodes) { + ResponseDefinitionBuilder response; + response = statusCode < 400 ? jsonResponse(body, statusCode) : status(statusCode); + seq = seq.then(request, response); + } + seq.thereafter(request, jsonResponse(body, 200)); } public void isDown() { - tca.when(request()).error(HttpError.error().withDropConnection(true)); + tca.register(any(ANY).willReturn(connectionReset())); } public void hasTimeout() { - tca.when(request()).respond(request -> null, Delay.minutes(10)); + tca.register(any(ANY).willReturn(delayedResponse())); } public void returnsWrongContentType() { - tca.when(request().withMethod("POST").withPath("/api/v2/rd/research-mapping")) - .respond( - HttpResponse.response() - .withStatusCode(200) - .withContentType(MediaType.PLAIN_TEXT_UTF_8)); + tca.register( + post("/api/v2/rd/research-mapping") + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON_VALUE)) + .willReturn(ok().withHeader(CONTENT_TYPE, "text/plain"))); } } diff --git a/research-domain-agent/src/test/resources/application.yaml b/research-domain-agent/src/test/resources/application.yaml index 11dc95f8..4ed80ef5 100644 --- a/research-domain-agent/src/test/resources/application.yaml +++ b/research-domain-agent/src/test/resources/application.yaml @@ -31,6 +31,5 @@ security: role: cd-agent logging.level: - org.mockserver.log: WARN care.smith.fts: DEBUG care.smith.fts.rda: TRACE diff --git a/test-util/pom.xml b/test-util/pom.xml index dca640c2..a57a035e 100644 --- a/test-util/pom.xml +++ b/test-util/pom.xml @@ -44,8 +44,15 @@ - org.mock-server - mockserver-junit-jupiter + org.wiremock + wiremock-standalone + 3.10.0 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + test diff --git a/test-util/src/main/java/care/smith/fts/test/MockServerUtil.java b/test-util/src/main/java/care/smith/fts/test/MockServerUtil.java index 42dd763f..ea01aa93 100644 --- a/test-util/src/main/java/care/smith/fts/test/MockServerUtil.java +++ b/test-util/src/main/java/care/smith/fts/test/MockServerUtil.java @@ -1,22 +1,34 @@ package care.smith.fts.test; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; -import static org.mockserver.model.MediaType.create; +import static care.smith.fts.test.MockServerUtil.SequentialMock.newScenario; +import static care.smith.fts.util.FhirUtils.fhirResourceToString; +import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON_VALUE; +import static care.smith.fts.util.NanoIdUtils.nanoId; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.noContent; +import static com.github.tomakehurst.wiremock.client.WireMock.status; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static java.lang.Math.toIntExact; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import care.smith.fts.util.HttpClientConfig; -import java.io.IOException; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.http.Fault; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.stubbing.Scenario; import java.io.InputStream; -import java.net.ServerSocket; -import org.mockserver.client.MockServerClient; -import org.mockserver.model.MediaType; - -/** - * This class is mainly used for loading test resources from the underlying package (specifically - * from other test packages) and may therefor be empty. - */ +import java.time.Duration; +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +import org.hl7.fhir.r4.model.Resource; + public interface MockServerUtil { - static HttpClientConfig clientConfig(MockServerClient server) { - var address = "http://localhost:%d".formatted(server.getPort()); + + static HttpClientConfig clientConfig(WireMockRuntimeInfo server) { + var address = server.getHttpBaseUrl(); return new HttpClientConfig(address); } @@ -24,18 +36,126 @@ static InputStream getResourceAsStream(String resourceName) { return MockServerUtil.class.getResourceAsStream(resourceName); } - static MockServerClient onRandomPort() { - return startClientAndServer(findFreePort()); + static WireMockServer onRandomPort() { + var wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + return wireMockServer; + } + + String APPLICATION_FHIR_JSON = "application/fhir+json"; + + String FIRST = Scenario.STARTED; + String REST = "OtherRequests"; + + static SequentialMock sequentialMock(WireMock wireMock) { + return new InitialSequentialMock(wireMock); + } + + static ResponseDefinitionBuilder accepted() { + return status(202); + } + + interface SequentialMock { + SequentialMock then(MappingBuilder mapping, ResponseDefinitionBuilder response); + + void thereafter(MappingBuilder mapping, ResponseDefinitionBuilder response); + + static String newScenario() { + return "seq/%s".formatted(nanoId(4)); + } } - private static int findFreePort() { - try (ServerSocket socket = new ServerSocket(0)) { - socket.setReuseAddress(true); - return socket.getLocalPort(); - } catch (IOException e) { - throw new IllegalStateException("Unable to find free port", e); + class InitialSequentialMock implements SequentialMock { + private final WireMock wireMock; + + public InitialSequentialMock(WireMock wireMock) { + this.wireMock = wireMock; + } + + @Override + public SequentialMock then(MappingBuilder mapping, ResponseDefinitionBuilder response) { + return new ChainedSequentialMock(this, wireMock, mapping, response); + } + + @Override + public void thereafter(MappingBuilder mapping, ResponseDefinitionBuilder response) { + var scenario = mapping.inScenario(newScenario()); + wireMock.register(scenario.whenScenarioStateIs(FIRST).willReturn(response)); } } - MediaType APPLICATION_FHIR_JSON = create("application", "fhir+json"); + @Slf4j + class ChainedSequentialMock implements SequentialMock { + private final SequentialMock previous; + private final WireMock wireMock; + private final MappingBuilder mapping; + private final ResponseDefinitionBuilder response; + + public ChainedSequentialMock( + SequentialMock previous, + WireMock wireMock, + MappingBuilder mapping, + ResponseDefinitionBuilder response) { + this.previous = previous; + this.wireMock = wireMock; + this.mapping = mapping; + this.response = response; + } + + @Override + public SequentialMock then(MappingBuilder mapping, ResponseDefinitionBuilder response) { + return new ChainedSequentialMock(this, wireMock, mapping, response); + } + + @Override + public void thereafter(MappingBuilder mapping, ResponseDefinitionBuilder response) { + String scenarioName = newScenario(); + wireMock.register( + mapping + .withId(UUID.randomUUID()) + .inScenario(scenarioName) + .whenScenarioStateIs(REST) + .willReturn(response)); + build(scenarioName, REST); + } + + private void build(String scenarioName, String nextState) { + var stateName = hasPrevious() ? nanoId(8) : FIRST; + log.debug("Registering {}[{} -> {}]", scenarioName, stateName, nextState); + wireMock.register( + mapping + .withId(UUID.randomUUID()) + .inScenario(scenarioName) + .whenScenarioStateIs(stateName) + .willSetStateTo(nextState) + .willReturn(response)); + if (hasPrevious()) { + ((ChainedSequentialMock) previous).build(scenarioName, stateName); + } + } + + private boolean hasPrevious() { + return previous instanceof ChainedSequentialMock; + } + } + + static ResponseDefinitionBuilder fhirResponse(Resource bundle, int statusCode) { + return fhirResponse(fhirResourceToString(bundle), statusCode); + } + + static ResponseDefinitionBuilder fhirResponse(String body, int statusCode) { + return status(statusCode).withHeader(CONTENT_TYPE, APPLICATION_FHIR_JSON_VALUE).withBody(body); + } + + static ResponseDefinitionBuilder connectionReset() { + return aResponse().withFault(Fault.CONNECTION_RESET_BY_PEER); + } + + static ResponseDefinitionBuilder delayedResponse() { + return delayedResponse(Duration.ofMinutes(10)); + } + + static ResponseDefinitionBuilder delayedResponse(Duration d) { + return noContent().withFixedDelay(toIntExact(d.toMillis())); + } } diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/BaseIT.java b/trust-center-agent/src/test/java/care/smith/fts/tca/BaseIT.java index 296834d9..6324f631 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/BaseIT.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/BaseIT.java @@ -2,8 +2,9 @@ import static care.smith.fts.test.MockServerUtil.onRandomPort; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; import com.redis.testcontainers.RedisContainer; -import org.mockserver.client.MockServerClient; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -11,24 +12,32 @@ public class BaseIT { // renovate: datasource=github-releases depName=valkey-io/valkey private static final String VALKEY_VERSION = "8.0.1"; - protected static final MockServerClient gics = onRandomPort(); - protected static final MockServerClient gpas = onRandomPort(); - protected static final RedisContainer keystore = + private static final WireMockServer gics = onRandomPort(); + private static final WireMockServer gpas = onRandomPort(); + private static final RedisContainer keystore = new RedisContainer("valkey/valkey:" + VALKEY_VERSION + "-alpine"); static { keystore.start(); } + protected WireMock gics() { + return new WireMock(gics); + } + + protected WireMock gpas() { + return new WireMock(gpas); + } + @DynamicPropertySource static void registerGicsMockUrl(DynamicPropertyRegistry registry) { - String baseUrl = "http://localhost:%d/ttp-fhir/fhir/gics".formatted(gics.getPort()); + String baseUrl = "http://localhost:%d/ttp-fhir/fhir/gics".formatted(gics.port()); registry.add("consent.gics.fhir.base-url", () -> baseUrl); } @DynamicPropertySource static void registerGpasMockUrl(DynamicPropertyRegistry registry) { - String baseUrl = "http://localhost:%d/ttp-fhir/fhir/gpas".formatted(gpas.getPort()); + String baseUrl = "http://localhost:%d/ttp-fhir/fhir/gpas".formatted(gpas.port()); registry.add("de-identification.gpas.fhir.base-url", () -> baseUrl); } diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchAllTest.java b/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchAllTest.java index aac2cc90..d041b068 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchAllTest.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchAllTest.java @@ -2,12 +2,15 @@ import static care.smith.fts.test.FhirGenerators.randomUuid; import static care.smith.fts.util.FhirUtils.toBundle; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.lang.String.valueOf; +import static java.util.Map.entry; +import static java.util.Map.ofEntries; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; -import static org.mockserver.model.MediaType.APPLICATION_JSON; import static org.springframework.web.util.UriComponentsBuilder.fromUriString; import static reactor.test.StepVerifier.create; @@ -18,9 +21,11 @@ import care.smith.fts.util.FhirUtils; import care.smith.fts.util.error.UnknownDomainException; import care.smith.fts.util.tca.ConsentFetchAllRequest; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import java.io.IOException; -import java.util.List; import java.util.Set; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; @@ -30,13 +35,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.mockserver.model.JsonBody; -import org.mockserver.model.Parameter; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -47,9 +45,10 @@ @Slf4j @SpringBootTest -@ExtendWith(MockServerExtension.class) +@WireMockTest @Import(TestWebClientFactory.class) class FhirConsentedPatientsProviderFetchAllTest { + @Autowired WebClient.Builder httpClientBuilder; @Autowired MeterRegistry meterRegistry; @@ -58,7 +57,6 @@ class FhirConsentedPatientsProviderFetchAllTest { private static final String POLICY_SYSTEM = "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy"; - private FhirConsentedPatientsProvider fhirConsentProvider; @Qualifier("defaultPageSize") @Autowired @@ -74,32 +72,31 @@ class FhirConsentedPatientsProviderFetchAllTest { new ConsentFetchAllRequest("MII", POLICIES, POLICY_SYSTEM); private static String address; private static FhirGenerator gicsConsentGenerator; - private static JsonBody jsonBody; + private static final String jsonBody = + """ + { + "resourceType": "Parameters", + "parameter": [{"name": "domain", "valueString": "MII"}] + } + """; + private static WireMock wireMock; @BeforeAll - static void setUp(MockServerClient mockServer) throws IOException { - address = "http://localhost:%d".formatted(mockServer.getPort()); + static void setUp(WireMockRuntimeInfo wireMockRuntime) throws IOException { + address = wireMockRuntime.getHttpBaseUrl(); + wireMock = wireMockRuntime.getWireMock(); gicsConsentGenerator = FhirGenerators.gicsResponse(randomUuid(), randomUuid()); - jsonBody = - json( - """ - { - "resourceType": "Parameters", - "parameter": [{"name": "domain", "valueString": "MII"}] - } - """, - ONLY_MATCHING_FIELDS); } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } @Test - void paging(MockServerClient mockServer) { + void paging() { int totalEntries = 2 * defaultPageSize; - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); @@ -110,24 +107,22 @@ void paging(MockServerClient mockServer) { .collect(toBundle()) .setTotal(totalEntries); - HttpRequest postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - HttpResponse httpResponse = - response().withBody(FhirUtils.fhirResourceToString(bundle), APPLICATION_JSON); - mockServer - .when( - postRequest.withQueryStringParameters( - List.of( - new Parameter("_offset", "0"), - new Parameter("_count", String.valueOf(defaultPageSize))))) - .respond(httpResponse); - mockServer - .when( - postRequest.withQueryStringParameters( - List.of( - new Parameter("_offset", String.valueOf(defaultPageSize)), - new Parameter("_count", String.valueOf(defaultPageSize))))) - .respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(defaultPageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle), 200))); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo(valueOf(defaultPageSize))), + entry("_count", equalTo(valueOf(defaultPageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle), 200))); var expectedNextLink = "http://localhost:8080/api/v2/cd/consented-patients/fetch-all?from=%s&count=%s" @@ -154,11 +149,11 @@ void paging(MockServerClient mockServer) { } @Test - void noNextLinkOnLastPage(MockServerClient mockServer) { + void noNextLinkOnLastPage() { int totalEntries = 1; int pageSize = 1; - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); @@ -169,17 +164,14 @@ void noNextLinkOnLastPage(MockServerClient mockServer) { .collect(toBundle()) .setTotal(totalEntries); - HttpRequest postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - HttpResponse httpResponse = - response().withBody(FhirUtils.fhirResourceToString(bundle), APPLICATION_JSON); - mockServer - .when( - postRequest.withQueryStringParameters( - List.of( - new Parameter("_offset", "0"), - new Parameter("_count", String.valueOf(pageSize))))) - .respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(pageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle), 200))); create( fhirConsentProvider.fetchAll( @@ -191,11 +183,11 @@ void noNextLinkOnLastPage(MockServerClient mockServer) { } @Test - void noConsents(MockServerClient mockServer) { + void noConsents() { int totalEntries = 0; int pageSize = 1; - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); Bundle bundle = @@ -205,17 +197,14 @@ void noConsents(MockServerClient mockServer) { .collect(toBundle()) .setTotal(totalEntries); - HttpRequest postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - HttpResponse httpResponse = - response().withBody(FhirUtils.fhirResourceToString(bundle), APPLICATION_JSON); - mockServer - .when( - postRequest.withQueryStringParameters( - List.of( - new Parameter("_offset", "0"), - new Parameter("_count", String.valueOf(pageSize))))) - .respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(pageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle), 200))); create( fhirConsentProvider.fetchAll( @@ -231,10 +220,10 @@ void noConsents(MockServerClient mockServer) { } @Test - void unknownDomainCausesGicsNotFound(MockServerClient mockServer) { + void unknownDomainCausesGicsNotFound() { int pageSize = 2; - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); @@ -242,13 +231,14 @@ void unknownDomainCausesGicsNotFound(MockServerClient mockServer) { var issue = operationOutcome.addIssue().setSeverity(IssueSeverity.ERROR); issue.setDiagnostics("No consents found for domain"); - var postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - var httpResponse = - response() - .withStatusCode(404) - .withBody(FhirUtils.fhirResourceToString(operationOutcome), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(pageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(operationOutcome), 404))); create( fhirConsentProvider.fetchAll( @@ -260,10 +250,10 @@ void unknownDomainCausesGicsNotFound(MockServerClient mockServer) { } @Test - void somethingElseCausesGicsNotFound(MockServerClient mockServer) { + void somethingElseCausesGicsNotFound() { int pageSize = 2; - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); @@ -271,13 +261,14 @@ void somethingElseCausesGicsNotFound(MockServerClient mockServer) { var issue = operationOutcome.addIssue().setSeverity(IssueSeverity.ERROR); issue.setDiagnostics("Something's not right"); - var postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - var httpResponse = - response() - .withStatusCode(404) - .withBody(FhirUtils.fhirResourceToString(operationOutcome), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(pageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(operationOutcome), 404))); create( fhirConsentProvider.fetchAll( @@ -289,23 +280,24 @@ void somethingElseCausesGicsNotFound(MockServerClient mockServer) { } @Test - void diagnosticsIsNullInHandleGicsNotFound(MockServerClient mockServer) { + void diagnosticsIsNullInHandleGicsNotFound() { int pageSize = 2; - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); var operationOutcome = new OperationOutcome(); operationOutcome.addIssue().setSeverity(IssueSeverity.ERROR); - var postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - var httpResponse = - response() - .withStatusCode(404) - .withBody(FhirUtils.fhirResourceToString(operationOutcome), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(pageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(operationOutcome), 404))); create( fhirConsentProvider.fetchAll( @@ -317,12 +309,12 @@ void diagnosticsIsNullInHandleGicsNotFound(MockServerClient mockServer) { } @Test - void emptyPoliciesYieldEmptyBundle(MockServerClient mockServer) { + void emptyPoliciesYieldEmptyBundle() { int totalEntries = 0; int pageSize = 2; var consentRequest = new ConsentFetchAllRequest("MII", Set.of(), POLICY_SYSTEM); - fhirConsentProvider = + var fhirConsentProvider = new FhirConsentedPatientsProvider( httpClientBuilder.baseUrl(address).build(), meterRegistry); Bundle bundle = @@ -332,11 +324,14 @@ void emptyPoliciesYieldEmptyBundle(MockServerClient mockServer) { .collect(toBundle()) .setTotal(totalEntries); - HttpRequest postRequest = - request().withMethod("POST").withPath("/$allConsentsForDomain").withBody(jsonBody); - HttpResponse httpResponse = - response().withBody(FhirUtils.fhirResourceToString(bundle), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post(urlPathEqualTo("/$allConsentsForDomain")) + .withRequestBody(equalToJson(jsonBody)) + .withQueryParams( + ofEntries( + entry("_offset", equalTo("0")), + entry("_count", equalTo(valueOf(pageSize))))) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle), 200))); create( fhirConsentProvider.fetchAll( diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchTest.java b/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchTest.java index 8db85735..d4947fb1 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchTest.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/consent/FhirConsentedPatientsProviderFetchTest.java @@ -3,12 +3,10 @@ import static care.smith.fts.test.FhirGenerators.fromList; import static care.smith.fts.test.FhirGenerators.randomUuid; import static care.smith.fts.util.FhirUtils.toBundle; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; -import static org.mockserver.model.MediaType.APPLICATION_JSON; import static org.springframework.web.util.UriComponentsBuilder.fromUriString; import static reactor.test.StepVerifier.create; @@ -19,6 +17,9 @@ import care.smith.fts.util.FhirUtils; import care.smith.fts.util.error.UnknownDomainException; import care.smith.fts.util.tca.ConsentFetchRequest; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import java.io.IOException; import java.util.List; @@ -30,13 +31,8 @@ import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.mockserver.model.JsonBody; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -46,9 +42,10 @@ @Slf4j @SpringBootTest -@ExtendWith(MockServerExtension.class) +@WireMockTest @Import(TestWebClientFactory.class) class FhirConsentedPatientsProviderFetchTest { + @Autowired WebClient.Builder httpClientBuilder; @Autowired MeterRegistry meterRegistry; @@ -74,50 +71,37 @@ class FhirConsentedPatientsProviderFetchTest { POLICY_SYSTEM, PATIENT_IDENTIFIER_SYSTEM, List.of("id1", "id2", "id3", "id4")); + + private static final String jsonBody1 = + """ + { + "resourceType": "Parameters", + "parameter": [ + {"name": "domain", "valueString": "MII"}, + {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id1"}}, + {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id2"}} + ]} + """; + private static String address; + private static WireMock wireMock; private static FhirGenerator gicsConsentGenerator; - private static JsonBody jsonBody1; - private static JsonBody jsonBody2; @BeforeAll - static void setUp(MockServerClient mockServer) throws IOException { - address = "http://localhost:%d".formatted(mockServer.getPort()); + static void setUp(WireMockRuntimeInfo wireMockRuntime) throws IOException { + address = wireMockRuntime.getHttpBaseUrl(); + wireMock = wireMockRuntime.getWireMock(); gicsConsentGenerator = FhirGenerators.gicsResponse(randomUuid(), fromList(List.of("id1", "id2", "id3", "id4"))); - - jsonBody1 = - json( - """ - { - "resourceType": "Parameters", - "parameter": [ - {"name": "domain", "valueString": "MII"}, - {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id1"}}, - {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id2"}} - ]} - """, - ONLY_MATCHING_FIELDS); - jsonBody2 = - json( - """ - { - "resourceType": "Parameters", - "parameter": [ - {"name": "domain", "valueString": "MII"}, - {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id3"}}, - {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id4"}} - ]} - """, - ONLY_MATCHING_FIELDS); } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } @Test - void paging(MockServerClient mockServer) { + void paging() { int pageSize = 2; int totalEntries = 2 * pageSize; @@ -138,19 +122,24 @@ void paging(MockServerClient mockServer) { .collect(toBundle()) .setTotal(totalEntries); - var postRequest1 = - request().withMethod("POST").withPath("/$allConsentsForPerson").withBody(jsonBody1); - - var httpResponse1 = - response().withBody(FhirUtils.fhirResourceToString(bundle1), APPLICATION_JSON); - mockServer.when(postRequest1).respond(httpResponse1); - - var postRequest2 = - request().withMethod("POST").withPath("/$allConsentsForPerson").withBody(jsonBody2); - - var httpResponse2 = - response().withBody(FhirUtils.fhirResourceToString(bundle2), APPLICATION_JSON); - mockServer.when(postRequest2).respond(httpResponse2); + wireMock.register( + post("/$allConsentsForPerson") + .withRequestBody(equalToJson(jsonBody1)) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle1), 200))); + + String jsonBody2 = """ + { + "resourceType": "Parameters", + "parameter": [ + {"name": "domain", "valueString": "MII"}, + {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id3"}}, + {"name": "personIdentifier", "valueIdentifier": {"system": "https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym", "value": "id4"}} + ]} + """; + wireMock.register( + post("/$allConsentsForPerson") + .withRequestBody(equalToJson(jsonBody2)) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle2), 200))); var expectedNextLink = "http://trustcenteragent:8080/api/v2/cd/consented-patients/fetch?from=%s&count=%s" @@ -177,7 +166,7 @@ void paging(MockServerClient mockServer) { } @Test - void noConsents(MockServerClient mockServer) { + void noConsents() { int totalEntries = 0; int pageSize = 2; @@ -191,11 +180,10 @@ void noConsents(MockServerClient mockServer) { .collect(toBundle()) .setTotal(totalEntries); - HttpRequest postRequest = - request().withMethod("POST").withPath("/$allConsentsForPerson").withBody(jsonBody1); - HttpResponse httpResponse = - response().withBody(FhirUtils.fhirResourceToString(bundle), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post("/$allConsentsForPerson") + .withRequestBody(equalToJson(jsonBody1)) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(bundle), 200))); var expectedNextLink = "http://trustcenteragent:8080/api/v2/cd/consented-patients/fetch?from=%s&count=%s" @@ -214,7 +202,7 @@ void noConsents(MockServerClient mockServer) { } @Test - void unknownDomainCausesGicsNotFound(MockServerClient mockServer) { + void unknownDomainCausesGicsNotFound() { int pageSize = 2; fhirConsentProvider = @@ -225,13 +213,10 @@ void unknownDomainCausesGicsNotFound(MockServerClient mockServer) { var issue = operationOutcome.addIssue().setSeverity(IssueSeverity.ERROR); issue.setDiagnostics("No consents found for domain"); - var postRequest = - request().withMethod("POST").withPath("/$allConsentsForPerson").withBody(jsonBody1); - var httpResponse = - response() - .withStatusCode(404) - .withBody(FhirUtils.fhirResourceToString(operationOutcome), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post("/$allConsentsForPerson") + .withRequestBody(equalToJson(jsonBody1, true, true)) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(operationOutcome), 404))); create( fhirConsentProvider.fetch( @@ -243,7 +228,7 @@ void unknownDomainCausesGicsNotFound(MockServerClient mockServer) { } @Test - void somethingElseCausesGicsNotFound(MockServerClient mockServer) { + void somethingElseCausesGicsNotFound() { int pageSize = 2; fhirConsentProvider = @@ -254,13 +239,10 @@ void somethingElseCausesGicsNotFound(MockServerClient mockServer) { var issue = operationOutcome.addIssue().setSeverity(IssueSeverity.ERROR); issue.setDiagnostics("Something's not right"); - var postRequest = - request().withMethod("POST").withPath("/$allConsentsForPerson").withBody(jsonBody1); - var httpResponse = - response() - .withStatusCode(404) - .withBody(FhirUtils.fhirResourceToString(operationOutcome), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post("/$allConsentsForPerson") + .withRequestBody(equalToJson(jsonBody1, true, true)) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(operationOutcome), 404))); create( fhirConsentProvider.fetch( @@ -272,7 +254,7 @@ void somethingElseCausesGicsNotFound(MockServerClient mockServer) { } @Test - void diagnosticsIsNullInHandleGicsNotFound(MockServerClient mockServer) { + void diagnosticsIsNullInHandleGicsNotFound() { int pageSize = 2; fhirConsentProvider = @@ -282,13 +264,10 @@ void diagnosticsIsNullInHandleGicsNotFound(MockServerClient mockServer) { var operationOutcome = new OperationOutcome(); operationOutcome.addIssue().setSeverity(IssueSeverity.ERROR); - var postRequest = - request().withMethod("POST").withPath("/$allConsentsForPerson").withBody(jsonBody1); - var httpResponse = - response() - .withStatusCode(404) - .withBody(FhirUtils.fhirResourceToString(operationOutcome), APPLICATION_JSON); - mockServer.when(postRequest).respond(httpResponse); + wireMock.register( + post("/$allConsentsForPerson") + .withRequestBody(equalToJson(jsonBody1, true, true)) + .willReturn(jsonResponse(FhirUtils.fhirResourceToString(operationOutcome), 404))); create( fhirConsentProvider.fetch( diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/deidentification/FhirMappingProviderTest.java b/trust-center-agent/src/test/java/care/smith/fts/tca/deidentification/FhirMappingProviderTest.java index bb574f10..27f98aa1 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/deidentification/FhirMappingProviderTest.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/deidentification/FhirMappingProviderTest.java @@ -2,24 +2,29 @@ import static care.smith.fts.test.FhirGenerators.fromList; import static care.smith.fts.test.MockServerUtil.APPLICATION_FHIR_JSON; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.HttpStatus.BAD_REQUEST; import static reactor.test.StepVerifier.create; import care.smith.fts.tca.deidentification.configuration.TransportMappingConfiguration; import care.smith.fts.test.FhirGenerators; import care.smith.fts.test.TestWebClientFactory; -import care.smith.fts.util.MediaTypes; import care.smith.fts.util.error.UnknownDomainException; import care.smith.fts.util.tca.TCADomains; import care.smith.fts.util.tca.TransportMappingRequest; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import io.micrometer.core.instrument.MeterRegistry; import java.io.IOException; import java.time.Duration; @@ -34,9 +39,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; -import org.mockserver.model.MediaType; import org.redisson.api.RMapCacheReactive; import org.redisson.api.RedissonClient; import org.redisson.api.RedissonReactiveClient; @@ -50,7 +52,7 @@ @Slf4j @SpringBootTest -@ExtendWith(MockServerExtension.class) +@WireMockTest @ExtendWith(MockitoExtension.class) @Import(TestWebClientFactory.class) class FhirMappingProviderTest { @@ -67,10 +69,12 @@ class FhirMappingProviderTest { @Autowired MeterRegistry meterRegistry; private FhirMappingProvider mappingProvider; + private WireMock wireMock; @BeforeEach - void setUp(MockServerClient mockServer) { - var address = "http://localhost:%d".formatted(mockServer.getPort()); + void setUp(WireMockRuntimeInfo wireMockRuntime) { + var address = wireMockRuntime.getHttpBaseUrl(); + wireMock = wireMockRuntime.getWireMock(); given(redisClient.reactive()).willReturn(redis); @@ -86,7 +90,7 @@ void setUp(MockServerClient mockServer) { } @Test - void generateTransportMapping(MockServerClient mockServer) throws IOException { + void generateTransportMapping() throws IOException { var fhirGenerator = FhirGenerators.gpasGetOrCreateResponse( fromList(List.of("id1", "Salt_id1", "PT336H_id1")), @@ -95,27 +99,21 @@ void generateTransportMapping(MockServerClient mockServer) throws IOException { List.of("id1", "Salt_id1", "PT336H_id1") .forEach( key -> - mockServer - .when( - request() - .withMethod("POST") - .withPath("/$pseudonymizeAllowCreate") - .withBody( - json( - """ - { "resourceType": "Parameters", - "parameter": [ - {"name": "target", "valueString": "domain"}, - {"name": "original", "valueString": "%s"}]} - """ - .formatted(key), - ONLY_MATCHING_FIELDS)) - .withContentType(APPLICATION_FHIR_JSON)) - .respond( - response() - .withBody( - fhirGenerator.generateString(), - MediaType.create("application", "fhir+json")))); + wireMock.register( + post(urlEqualTo("/$pseudonymizeAllowCreate")) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .withRequestBody( + equalToJson( + """ + { "resourceType": "Parameters", + "parameter": [ + {"name": "target", "valueString": "domain"}, + {"name": "original", "valueString": "%s"}]} + """ + .formatted(key), + true, + true)) + .willReturn(fhirResponse(fhirGenerator.generateString(), 200)))); given(redis.getMapCache(anyString())).willReturn(mapCache); given(mapCache.expire(Duration.ofMinutes(10))).willReturn(Mono.just(false)); @@ -170,19 +168,17 @@ void fetchResearchMappingWhenRedisDown() { } @Test - void fetchResearchMappingWithUnknownDomainException(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST").withPath("/$pseudonymizeAllowCreate")) - .respond( - response() - .withStatusCode(400) - .withContentType(MediaType.parse(MediaTypes.APPLICATION_FHIR_JSON_VALUE)) - .withBody( + void fetchResearchMappingWithUnknownDomainException() { + wireMock.register( + post("/$pseudonymizeAllowCreate") + .willReturn( + fhirResponse( """ {"resourceType": "OperationOutcome", "issue": [{"severity": "error", "code": "processing", "diagnostics": "Unknown domain"}]} - """)); + """, + BAD_REQUEST.value()))); given(redis.getMapCache(anyString())).willReturn(mapCache); given(mapCache.expire(Duration.ofMinutes(10))).willReturn(Mono.just(false)); @@ -193,19 +189,17 @@ void fetchResearchMappingWithUnknownDomainException(MockServerClient mockServer) } @Test - void fetchResearchMappingWithUnknownError(MockServerClient mockServer) { - mockServer - .when(request().withMethod("POST").withPath("/$pseudonymizeAllowCreate")) - .respond( - response() - .withStatusCode(400) - .withContentType(MediaType.parse(MediaTypes.APPLICATION_FHIR_JSON_VALUE)) - .withBody( + void fetchResearchMappingWithUnknownError() { + wireMock.register( + post("/$pseudonymizeAllowCreate") + .willReturn( + fhirResponse( """ - {"resourceType":"OperationOutcome", - "issue": [{"severity": "error", "code": "processing", - "diagnostics": "Unknown error"}]} - """)); +{"resourceType": "OperationOutcome", +"issue": [{"severity": "error", "code": "processing", + "diagnostics": "Unknown error"}]} +""", + BAD_REQUEST.value()))); given(redis.getMapCache(anyString())).willReturn(mapCache); given(mapCache.expire(Duration.ofMinutes(10))).willReturn(Mono.just(false)); @@ -227,7 +221,7 @@ void fetchResearchMappingWrongDateShiftValue() { } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown() { + wireMock.resetMappings(); } } diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/rest/DeIdentificationControllerIT.java b/trust-center-agent/src/test/java/care/smith/fts/tca/rest/DeIdentificationControllerIT.java index 93e6778a..e30d1347 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/rest/DeIdentificationControllerIT.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/rest/DeIdentificationControllerIT.java @@ -1,22 +1,24 @@ package care.smith.fts.tca.rest; import static care.smith.fts.test.FhirGenerators.fromList; +import static care.smith.fts.test.FhirGenerators.gpasGetOrCreateResponse; import static care.smith.fts.test.MockServerUtil.APPLICATION_FHIR_JSON; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static java.time.Duration.ofDays; import static java.util.Map.entry; import static java.util.Map.ofEntries; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static reactor.test.StepVerifier.create; import care.smith.fts.tca.BaseIT; -import care.smith.fts.test.FhirGenerators; import care.smith.fts.test.TestWebClientFactory; import care.smith.fts.util.tca.ResearchMappingResponse; import care.smith.fts.util.tca.TransportMappingResponse; @@ -25,13 +27,11 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.mockserver.model.MediaType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; @@ -58,20 +58,19 @@ static void setUp(@LocalServerPort int port, @Autowired TestWebClientFactory fac @Test void successfulRequest() throws IOException { var fhirGenerator = - FhirGenerators.gpasGetOrCreateResponse( + gpasGetOrCreateResponse( fromList(List.of("id-144218", "Salt_id-144218", "PT336H_id-144218")), fromList(List.of("469680023", "123", "12345"))); List.of("id-144218", "Salt_id-144218", "PT336H_id-144218") .forEach( key -> - gpas.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate") - .withContentType(APPLICATION_FHIR_JSON) - .withBody( - json( + gpas() + .register( + post(urlEqualTo("/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate")) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .withRequestBody( + equalToJson( """ { "resourceType": "Parameters", "parameter": [ @@ -79,12 +78,9 @@ void successfulRequest() throws IOException { {"name": "original", "valueString": "%s"}]} """ .formatted(key), - ONLY_MATCHING_FIELDS))) - .respond( - response() - .withBody( - fhirGenerator.generateString(), - MediaType.create("application", "fhir+json")))); + true, + true)) + .willReturn(fhirResponse(fhirGenerator.generateString(), 200)))); var response = doPost( @@ -110,48 +106,31 @@ void successfulRequest() throws IOException { } @Test - void firstRequestToGpasFails() { + void firstRequestToGpasFails() throws IOException { var statusCodes = new LinkedList<>(List.of(500)); var map = Map.of("id-144218", "469680023", "Salt_id-144218", "123", "PT336H_id-144218", "12345"); - List.of("id-144218", "Salt_id-144218", "PT336H_id-144218") - .forEach( - key -> - gpas.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate") - .withContentType(APPLICATION_FHIR_JSON) - .withBody( - json( - """ - { "resourceType": "Parameters", - "parameter": [ - {"name": "target", "valueString": "MII"}, - {"name": "original", "valueString": "%s"}]} - """ - .formatted(key), - ONLY_MATCHING_FIELDS))) - .respond( - request -> - Optional.ofNullable(statusCodes.poll()) - .filter(statusCode -> statusCode >= 400) - .map(statusCode -> response().withStatusCode(statusCode)) - .orElseGet( - () -> { - try { - return response() - .withBody( - FhirGenerators.gpasGetOrCreateResponse( - () -> key, () -> map.get(key)) - .generateString(), - MediaType.create("application", "fhir+json")); - } catch (IOException e) { - throw new RuntimeException(e); - } - }))); + for (String s : List.of("id-144218", "Salt_id-144218", "PT336H_id-144218")) { + String body = gpasGetOrCreateResponse(() -> s, () -> map.get(s)).generateString(); + gpas() + .register( + post(urlEqualTo("/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate")) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .withRequestBody( + equalToJson( + """ + { "resourceType": "Parameters", + "parameter": [ + {"name": "target", "valueString": "MII"}, + {"name": "original", "valueString": "%s"}]} + """ + .formatted(s), + true, + true)) + .willReturn(fhirResponse(body, 200))); + } var response = doPost( @@ -201,20 +180,19 @@ void rejectInvalidIds() { @Test void transportMappingIdsAndDateShiftingValuesAndFetchPseudonyms() throws IOException { var fhirGenerator = - FhirGenerators.gpasGetOrCreateResponse( + gpasGetOrCreateResponse( fromList(List.of("id-144218", "Salt_id-144218", "PT336H_id-144218")), fromList(List.of("469680023", "123", "12345"))); List.of("id-144218", "Salt_id-144218", "PT336H_id-144218") .forEach( key -> - gpas.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate") - .withContentType(APPLICATION_FHIR_JSON) - .withBody( - json( + gpas() + .register( + post(urlEqualTo("/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate")) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .withRequestBody( + equalToJson( """ { "resourceType": "Parameters", "parameter": [ @@ -222,12 +200,9 @@ void transportMappingIdsAndDateShiftingValuesAndFetchPseudonyms() throws IOExcep {"name": "original", "valueString": "%s"}]} """ .formatted(key), - ONLY_MATCHING_FIELDS))) - .respond( - response() - .withBody( - fhirGenerator.generateString(), - MediaType.create("application", "fhir+json")))); + true, + true)) + .willReturn(fhirResponse(fhirGenerator.generateString(), 200)))); var transferId = doPost( @@ -279,7 +254,7 @@ private static Mono doPost(Map body) { @AfterEach void tearDown() { - gics.reset(); - gpas.reset(); + gics().resetMappings(); + gpas().resetMappings(); } } diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchAllConsentControllerIT.java b/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchAllConsentControllerIT.java index 94bed3bd..4d0b137c 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchAllConsentControllerIT.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchAllConsentControllerIT.java @@ -2,12 +2,19 @@ import static care.smith.fts.test.FhirGenerators.randomUuid; import static care.smith.fts.test.MockServerUtil.APPLICATION_FHIR_JSON; +import static care.smith.fts.test.MockServerUtil.FIRST; +import static care.smith.fts.test.MockServerUtil.REST; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.serverError; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Map.entry; import static java.util.Map.ofEntries; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static reactor.test.StepVerifier.create; import care.smith.fts.tca.BaseIT; @@ -15,10 +22,7 @@ import care.smith.fts.test.TestWebClientFactory; import care.smith.fts.util.MediaTypes; import java.io.IOException; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterEach; @@ -46,15 +50,11 @@ static void setUp(@LocalServerPort int port, @Autowired TestWebClientFactory fac @Test void successfulRequest() throws IOException { var consentGenerator = FhirGenerators.gicsResponse(randomUuid(), () -> "FTS001"); - gics.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gics/$allConsentsForDomain") - .withContentType(APPLICATION_FHIR_JSON)) - .respond( - response() - .withContentType(APPLICATION_FHIR_JSON) - .withBody(consentGenerator.generateString())); + gics() + .register( + post(urlPathEqualTo("/ttp-fhir/fhir/gics/$allConsentsForDomain")) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .willReturn(fhirResponse(consentGenerator.generateString(), 200))); var response = fetchAll( @@ -86,30 +86,22 @@ private static Mono fetchAll(Map body) { @Test void firstRequestToGicsFails() throws IOException { var consentGenerator = FhirGenerators.gicsResponse(randomUuid(), () -> "FTS001"); - var statusCodes = new LinkedList<>(List.of(500)); - gics.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gics/$allConsentsForDomain") - .withContentType(APPLICATION_FHIR_JSON)) - .respond( - request -> - Optional.ofNullable(statusCodes.poll()) - .map( - statusCode -> - statusCode < 400 - ? response() - .withStatusCode(statusCode) - .withContentType(APPLICATION_FHIR_JSON) - .withBody(consentGenerator.generateString()) - : response().withStatusCode(statusCode)) - .orElseGet( - () -> - response() - .withStatusCode(200) - .withContentType(APPLICATION_FHIR_JSON) - .withBody(consentGenerator.generateString()))); + gics() + .register( + post(urlPathEqualTo("/ttp-fhir/fhir/gics/$allConsentsForDomain")) + .inScenario("firstRequestFails") + .whenScenarioStateIs(FIRST) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .willSetStateTo(REST) + .willReturn(serverError())); + gics() + .register( + post(urlPathEqualTo("/ttp-fhir/fhir/gics/$allConsentsForDomain")) + .inScenario("firstRequestFails") + .whenScenarioStateIs(REST) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .willReturn(jsonResponse(consentGenerator.generateString(), 200))); var response = fetchAll( @@ -129,6 +121,6 @@ void firstRequestToGicsFails() throws IOException { @AfterEach void tearDown() { - gics.reset(); + gics().resetMappings(); } } diff --git a/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchConsentControllerIT.java b/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchConsentControllerIT.java index a82a22ee..4ef2e611 100644 --- a/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchConsentControllerIT.java +++ b/trust-center-agent/src/test/java/care/smith/fts/tca/rest/FetchConsentControllerIT.java @@ -2,12 +2,17 @@ import static care.smith.fts.test.FhirGenerators.randomUuid; import static care.smith.fts.test.MockServerUtil.APPLICATION_FHIR_JSON; +import static care.smith.fts.test.MockServerUtil.FIRST; +import static care.smith.fts.test.MockServerUtil.REST; +import static care.smith.fts.test.MockServerUtil.fhirResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.serverError; import static java.util.Map.entry; import static java.util.Map.ofEntries; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static reactor.test.StepVerifier.create; import care.smith.fts.tca.BaseIT; @@ -15,10 +20,8 @@ import care.smith.fts.test.TestWebClientFactory; import care.smith.fts.util.MediaTypes; import java.io.IOException; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterEach; @@ -46,21 +49,16 @@ static void setUp(@LocalServerPort int port, @Autowired TestWebClientFactory fac @Test void successfulRequest() throws IOException { var consentGenerator = FhirGenerators.gicsResponse(randomUuid(), () -> "FTS001"); - gics.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gics/$allConsentsForPerson") - .withContentType(APPLICATION_FHIR_JSON)) - .respond( - response() - .withContentType(APPLICATION_FHIR_JSON) - .withBody(consentGenerator.generateString())); + gics() + .register( + post("/ttp-fhir/fhir/gics/$allConsentsForPerson") + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .willReturn(fhirResponse(consentGenerator.generateString(), 200))); checkResponse(); } private static void checkResponse() { - var response = fetch( ofEntries( @@ -92,36 +90,28 @@ private static Mono fetch(Map body) { @Test void firstRequestToGicsFails() throws IOException { var consentGenerator = FhirGenerators.gicsResponse(randomUuid(), () -> "FTS001"); - var statusCodes = new LinkedList<>(List.of(500)); - gics.when( - request() - .withMethod("POST") - .withPath("/ttp-fhir/fhir/gics/$allConsentsForPerson") - .withContentType(APPLICATION_FHIR_JSON)) - .respond( - request -> - Optional.ofNullable(statusCodes.poll()) - .map( - statusCode -> - statusCode < 400 - ? response() - .withStatusCode(statusCode) - .withContentType(APPLICATION_FHIR_JSON) - .withBody(consentGenerator.generateString()) - : response().withStatusCode(statusCode)) - .orElseGet( - () -> - response() - .withStatusCode(200) - .withContentType(APPLICATION_FHIR_JSON) - .withBody(consentGenerator.generateString()))); + gics() + .register( + post("/ttp-fhir/fhir/gics/$allConsentsForPerson") + .inScenario("firstRequestFails") + .whenScenarioStateIs(FIRST) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .willSetStateTo(REST) + .willReturn(serverError())); + gics() + .register( + post("/ttp-fhir/fhir/gics/$allConsentsForPerson") + .inScenario("firstRequestFails") + .whenScenarioStateIs(REST) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FHIR_JSON)) + .willReturn(fhirResponse(consentGenerator.generateString(), 200))); checkResponse(); } @AfterEach void tearDown() { - gics.reset(); + gics().resetMappings(); } } diff --git a/trust-center-agent/src/test/resources/application.yaml b/trust-center-agent/src/test/resources/application.yaml index 3a5f2e88..9f7fbd1f 100644 --- a/trust-center-agent/src/test/resources/application.yaml +++ b/trust-center-agent/src/test/resources/application.yaml @@ -24,7 +24,6 @@ deIdentification: ttl: PT10M logging.level: - org.mockserver.log: WARN care.smith.fts: DEBUG care.smith.fts.tca: TRACE diff --git a/util/pom.xml b/util/pom.xml index 8670ed4f..90d67049 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -82,8 +82,9 @@ - org.mock-server - mockserver-junit-jupiter + org.wiremock + wiremock-standalone + 3.10.0 test diff --git a/util/src/test/java/care/smith/fts/util/WebClientDefaultsTest.java b/util/src/test/java/care/smith/fts/util/WebClientDefaultsTest.java index 655c6921..f4e71661 100644 --- a/util/src/test/java/care/smith/fts/util/WebClientDefaultsTest.java +++ b/util/src/test/java/care/smith/fts/util/WebClientDefaultsTest.java @@ -1,24 +1,23 @@ package care.smith.fts.util; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static java.time.Duration.ofSeconds; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.JsonBody.json; import static reactor.test.StepVerifier.create; import care.smith.fts.api.ConsentedPatient; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import java.net.http.HttpClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; import org.springframework.web.reactive.function.client.WebClient; -@ExtendWith(MockServerExtension.class) +@WireMockTest class WebClientDefaultsTest { private static final ObjectMapper objectMapper = @@ -27,18 +26,19 @@ class WebClientDefaultsTest { HttpClient.newBuilder().connectTimeout(ofSeconds(10)).build(); @Test - void customizeWebClientAndDecodeToMono(MockServerClient mockServer) { - var address = "http://localhost:%d".formatted(mockServer.getPort()); - mockServer - .when(request().withMethod("POST").withPath("/")) - .respond( - response() - .withBody( - json( - """ - {"id":"patient","consentedPolicies":{"policies":{"a":[ - {"start":-23220777600.000000000,"end":-23220604800.000000000}]}}} - """))); + void customizeWebClientAndDecodeToMono(WireMockRuntimeInfo wireMockRuntime) { + var address = wireMockRuntime.getHttpBaseUrl(); + var wireMock = wireMockRuntime.getWireMock(); + wireMock.register( + post(urlEqualTo("/")) + .willReturn( + jsonResponse( + """ + {"id":"patient","consentedPolicies":{"policies":{"a":[ + {"start":-23220777600.000000000,"end":-23220604800.000000000}]}}} + """, + 200))); + WebClient.Builder webClientBuilder = WebClient.builder(); new WebClientDefaults(httpClient, objectMapper).customize(webClientBuilder); WebClient webClient = webClientBuilder.baseUrl(address).build(); @@ -53,7 +53,8 @@ void customizeWebClientAndDecodeToMono(MockServerClient mockServer) { } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown(WireMockRuntimeInfo wireMockRuntime) { + var wireMock = wireMockRuntime.getWireMock(); + wireMock.resetMappings(); } } diff --git a/util/src/test/java/care/smith/fts/util/WebClientFhirCodecTest.java b/util/src/test/java/care/smith/fts/util/WebClientFhirCodecTest.java index f1b71eac..54344e7c 100644 --- a/util/src/test/java/care/smith/fts/util/WebClientFhirCodecTest.java +++ b/util/src/test/java/care/smith/fts/util/WebClientFhirCodecTest.java @@ -1,22 +1,24 @@ package care.smith.fts.util; import static ca.uhn.fhir.context.FhirContext.forR4; +import static care.smith.fts.util.FhirUtils.fhirResourceToString; import static care.smith.fts.util.FhirUtils.toBundle; +import static com.github.tomakehurst.wiremock.client.WireMock.created; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.model.MediaType.APPLICATION_JSON; import static reactor.test.StepVerifier.create; import ca.uhn.fhir.context.FhirContext; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; import java.util.stream.Stream; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.client.MockServerClient; -import org.mockserver.junit.jupiter.MockServerExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; @@ -27,19 +29,19 @@ import org.springframework.web.reactive.function.client.WebClient; @SpringBootTest(classes = {FhirCodecConfiguration.class, WebClientFhirCodecTest.Config.class}) -@ExtendWith(MockServerExtension.class) +@WireMockTest class WebClientFhirCodecTest { @Autowired WebClient.Builder client; @Test - void decodeResponse(MockServerClient mockServer) { - var address = "http://localhost:%d".formatted(mockServer.getPort()); + void decodeResponse(WireMockRuntimeInfo wireMockRuntime) { + var address = wireMockRuntime.getHttpBaseUrl(); + var wireMock = wireMockRuntime.getWireMock(); Bundle bundle = Stream.of(new Patient().setId("patient-094857")).collect(toBundle()); - mockServer - .when(request().withMethod("GET").withPath("/")) - .respond(response().withBody(FhirUtils.fhirResourceToString(bundle), APPLICATION_JSON)); + wireMock.register( + get(urlEqualTo("/")).willReturn(jsonResponse(fhirResourceToString(bundle), 200))); WebClient webClient = client.baseUrl(address).build(); create(webClient.get().retrieve().bodyToMono(Bundle.class)) @@ -48,13 +50,12 @@ void decodeResponse(MockServerClient mockServer) { } @Test - void encodeRequest(MockServerClient mockServer) { - var address = "http://localhost:%d".formatted(mockServer.getPort()); + void encodeRequest(WireMockRuntimeInfo wireMockRuntime) { + var address = wireMockRuntime.getHttpBaseUrl(); + var wireMock = wireMockRuntime.getWireMock(); Bundle bundle = Stream.of(new Patient().setId("patient-094857")).collect(toBundle()); - mockServer - .when(request().withMethod("POST").withPath("/")) - .respond(response().withStatusCode(201)); + wireMock.register(post(urlEqualTo("/")).willReturn(created())); WebClient webClient = client.baseUrl(address).build(); var response = @@ -70,8 +71,9 @@ void encodeRequest(MockServerClient mockServer) { } @AfterEach - void tearDown(MockServerClient mockServer) { - mockServer.reset(); + void tearDown(WireMockRuntimeInfo wireMockRuntime) { + var wireMock = wireMockRuntime.getWireMock(); + wireMock.resetMappings(); } @TestConfiguration diff --git a/util/src/test/java/care/smith/fts/util/auth/HttpServerAuthConfigTest.java b/util/src/test/java/care/smith/fts/util/auth/HttpServerAuthConfigTest.java index 58abecf8..bd7779dc 100644 --- a/util/src/test/java/care/smith/fts/util/auth/HttpServerAuthConfigTest.java +++ b/util/src/test/java/care/smith/fts/util/auth/HttpServerAuthConfigTest.java @@ -22,8 +22,7 @@ void noUserDetailsForNoneAuth() { void multipleAuthMethodsThrow() { var config = new HttpServerAuthConfig(); config.setAuth( - new AuthMethod( - null, new HttpServerBasicAuth(List.of()), new HttpServerNoneAuth())); + new AuthMethod(null, new HttpServerBasicAuth(List.of()), new HttpServerNoneAuth())); assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(config::userDetailsService)