From 3eacf3aea1ea719a5293019b3571b04025217509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Copetti?= Date: Sat, 5 Apr 2025 18:53:36 -0300 Subject: [PATCH] Add configurable rootDir option --- .../testcontainers/WireMockContainer.java | 68 ++++++- .../WireMockContainerRootDirTest.java | 183 ++++++++++++++++++ .../__files/default-root-dir-file.json | 1 + .../mappings/hello-default-root-dir.json | 10 + .../WireMockContainerRootDirTest/file.json | 1 + .../WireMockContainerRootDirTest/hello.json | 10 + .../__files/nested/root-dir-nested-file.json | 1 + .../root-dir/__files/root-dir-file.json | 1 + .../root-dir/mappings/hello-root-dir.json | 10 + .../nested/hello-nested-root-dir.json | 10 + 10 files changed, 288 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest.java create mode 100644 src/test/resources/__files/default-root-dir-file.json create mode 100644 src/test/resources/mappings/hello-default-root-dir.json create mode 100644 src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/file.json create mode 100644 src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/hello.json create mode 100644 src/test/resources/root-dir/__files/nested/root-dir-nested-file.json create mode 100644 src/test/resources/root-dir/__files/root-dir-file.json create mode 100644 src/test/resources/root-dir/mappings/hello-root-dir.json create mode 100644 src/test/resources/root-dir/mappings/nested/hello-nested-root-dir.json diff --git a/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java b/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java index bf73751..cdf614d 100644 --- a/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java +++ b/src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java @@ -59,8 +59,11 @@ public class WireMockContainer extends GenericContainer { public static final DockerImageName WIREMOCK_2_LATEST = DockerImageName.parse(OFFICIAL_IMAGE_NAME).withTag(WIREMOCK_2_LATEST_TAG); - private static final String MAPPINGS_DIR = "/home/wiremock/mappings/"; - private static final String FILES_DIR = "/home/wiremock/__files/"; + private static final String MAPPINGS_DIR = "mappings/"; + private static final String FILES_DIR = "__files/"; + private static final String CONTAINER_WORKING_DIR = "/home/wiremock/"; + private static final String CONTAINER_MAPPINGS_DIR = CONTAINER_WORKING_DIR + MAPPINGS_DIR; + private static final String CONTAINER_FILES_DIR = CONTAINER_WORKING_DIR + FILES_DIR; private static final String EXTENSIONS_DIR = "/var/wiremock/extensions/"; private static final int PORT = 8080; @@ -81,6 +84,8 @@ public class WireMockContainer extends GenericContainer { private final Map plugins = new HashMap<>(); private boolean isBannerDisabled = true; + private File rootDir = new File("src/test/resources"); + /** * Create image from the specified full image name (repo, image, tag) */ @@ -130,7 +135,7 @@ public WireMockContainer withBanner() { isBannerDisabled = false; return this; } - + /** * Adds CLI argument to the WireMock call. * @param arg Argument @@ -374,12 +379,15 @@ public Integer getPort() { protected void configure() { super.configure(); addExposedPorts(PORT); + + loadAllFilesFromRootDirectory(); + for (Stub stub : mappingStubs.values()) { - withCopyToContainer(Transferable.of(stub.json), MAPPINGS_DIR + stub.name + ".json"); + withCopyToContainer(Transferable.of(stub.json), CONTAINER_MAPPINGS_DIR + stub.name + ".json"); } for (Map.Entry mount : mappingFiles.entrySet()) { - withCopyToContainer(mount.getValue(), FILES_DIR + mount.getKey()); + withCopyToContainer(mount.getValue(), CONTAINER_FILES_DIR + mount.getKey()); } final ArrayList extensionClassNames = new ArrayList<>(); @@ -403,6 +411,54 @@ protected void configure() { withCommand(wireMockArgs.toString()); } + /** + * Configures the root directory where mappings and files will be loaded recursively. + * If not set, {@code src/test/resources} will be used by default. + *

+ * Files will be loaded from {@code $rootDir/__files} and mappings from {@code $rootDir/mappings}. + * + * @param rootDir the root directory + * @return this instance + */ + public WireMockContainer withRootDir(File rootDir) { + this.rootDir = rootDir; + return this; + } + + private void loadAllFilesFromRootDirectory() { + if (rootDir == null || !rootDir.isDirectory()) { + return; + } + + Path mappingsPath = rootDir.toPath().resolve(MAPPINGS_DIR); + getAllFiles(mappingsPath).forEach(path -> withMappingFromJSON(readAllContent(path))); + + Path filesPath = rootDir.toPath().resolve(FILES_DIR); + getAllFiles(filesPath).forEach(path -> + withFile(filesPath.relativize(path).toString(), path.toFile())); + } + + private List getAllFiles(Path path) { + + if (!Files.exists(path)) { + return Collections.emptyList(); + } + + try (Stream stream = Files.walk(path)){ + return stream.filter(Files::isRegularFile).collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String readAllContent(Path path) { + try { + return new String(Files.readAllBytes(path)); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + private static final class Stub { final String name; final String json; @@ -412,6 +468,4 @@ public Stub(String name, String json) { this.json = json; } } - - } diff --git a/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest.java b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest.java new file mode 100644 index 0000000..b66b044 --- /dev/null +++ b/src/test/java/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2025 WireMock Inc, Oleg Nenashev and all project contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wiremock.integrations.testcontainers; + +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.wiremock.integrations.testcontainers.testsupport.http.HttpResponse; +import org.wiremock.integrations.testcontainers.testsupport.http.TestHttpClient; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; + +@Testcontainers(parallel = true) +class WireMockContainerRootDirTest { + @Container + WireMockContainer wiremockServer = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE) + .withMapping("hello", WireMockContainerRootDirTest.class, "hello.json") + .withFileFromResource("file.json", WireMockContainerRootDirTest.class, "file.json") + .withRootDir(new File("src/test/resources/root-dir")); + + @Test + void testThatLoadsMappingFromRootDirectory() throws Exception { + // given + String url = wiremockServer.getUrl("hello/root/dir"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("response body") + .contains("Hello root dir"); + } + + @Test + void testThatLoadsMappingFromNestedRootDirectory() throws Exception { + // given + String url = wiremockServer.getUrl("hello/nested/root/dir"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("response body") + .contains("Hello nested root dir"); + } + + @Test + void testThatServesFileFromRootDirectory() throws Exception { + // given + String url = wiremockServer.getUrl("root-dir-file.json"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("response body") + .isEqualTo("{ \"message\": \"file from root dir\" }"); + } + + @Test + void testThatServesNestedFileFromRootDirectory() throws Exception { + // given + String url = wiremockServer.getUrl("nested/root-dir-nested-file.json"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("response body") + .isEqualTo("{ \"message\": \"nested file from root dir\" }"); + } + + @Test + void testThatDirectMappingsAndFilesAreLoadedWithCustomRootDirEnabled() throws Exception { + // given + String url = wiremockServer.getUrl("/hello"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("Wrong response body") + .contains("file contents from direct mapping"); + } + + @Test + void testThatDirectMappingsAndFilesAreLoadedEvenWhenRootDirIsSpecified() throws Exception { + // given + String url = wiremockServer.getUrl("/hello"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("Wrong response body") + .contains("file contents from direct mapping"); + } + + @Test + void testThatMappingsAndFileAreLoadedFromDefaultRootDir() throws Exception { + + try (WireMockContainer wmc = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE)) { + wmc.start(); + + // given + String url = wmc.getUrl("/hello/default/root/dir"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("Wrong response body") + .contains("contents from default root dir file"); + } + } + + @Test + void testThatInvalidRootDirIsIgnored() throws Exception { + + try (WireMockContainer wmc = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE) + .withMapping("hello", WireMockContainerRootDirTest.class, "hello.json") + .withFileFromResource("file.json", WireMockContainerRootDirTest.class, "file.json") + .withRootDir(new File("invalid/root/dir"))) { + wmc.start(); + + // given + String url = wmc.getUrl("/hello"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("Wrong response body") + .contains("file contents from direct mapping"); + } + } + + @Test + void testThatNullRootDirIsIgnored() throws Exception { + + try (WireMockContainer wmc = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE) + .withMapping("hello", WireMockContainerRootDirTest.class, "hello.json") + .withFileFromResource("file.json", WireMockContainerRootDirTest.class, "file.json") + .withRootDir(null)) { + wmc.start(); + + // given + String url = wmc.getUrl("/hello"); + + // when + HttpResponse response = new TestHttpClient().get(url); + + // then + assertThat(response.getBody()) + .as("Wrong response body") + .contains("file contents from direct mapping"); + } + } + +} diff --git a/src/test/resources/__files/default-root-dir-file.json b/src/test/resources/__files/default-root-dir-file.json new file mode 100644 index 0000000..eba18fc --- /dev/null +++ b/src/test/resources/__files/default-root-dir-file.json @@ -0,0 +1 @@ +{ "message": "contents from default root dir file" } \ No newline at end of file diff --git a/src/test/resources/mappings/hello-default-root-dir.json b/src/test/resources/mappings/hello-default-root-dir.json new file mode 100644 index 0000000..c7752db --- /dev/null +++ b/src/test/resources/mappings/hello-default-root-dir.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/hello/default/root/dir" + }, + "response": { + "status": 200, + "bodyFileName": "default-root-dir-file.json" + } +} \ No newline at end of file diff --git a/src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/file.json b/src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/file.json new file mode 100644 index 0000000..eceda1b --- /dev/null +++ b/src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/file.json @@ -0,0 +1 @@ +{ "message": "file contents from direct mapping" } diff --git a/src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/hello.json b/src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/hello.json new file mode 100644 index 0000000..6f6e779 --- /dev/null +++ b/src/test/resources/org/wiremock/integrations/testcontainers/WireMockContainerRootDirTest/hello.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/hello" + }, + "response": { + "status": 200, + "bodyFileName": "file.json" + } +} diff --git a/src/test/resources/root-dir/__files/nested/root-dir-nested-file.json b/src/test/resources/root-dir/__files/nested/root-dir-nested-file.json new file mode 100644 index 0000000..0a68de7 --- /dev/null +++ b/src/test/resources/root-dir/__files/nested/root-dir-nested-file.json @@ -0,0 +1 @@ +{ "message": "nested file from root dir" } \ No newline at end of file diff --git a/src/test/resources/root-dir/__files/root-dir-file.json b/src/test/resources/root-dir/__files/root-dir-file.json new file mode 100644 index 0000000..bf37140 --- /dev/null +++ b/src/test/resources/root-dir/__files/root-dir-file.json @@ -0,0 +1 @@ +{ "message": "file from root dir" } \ No newline at end of file diff --git a/src/test/resources/root-dir/mappings/hello-root-dir.json b/src/test/resources/root-dir/mappings/hello-root-dir.json new file mode 100644 index 0000000..5417c9f --- /dev/null +++ b/src/test/resources/root-dir/mappings/hello-root-dir.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/hello/root/dir" + }, + "response": { + "status": 200, + "body": "Hello root dir" + } +} \ No newline at end of file diff --git a/src/test/resources/root-dir/mappings/nested/hello-nested-root-dir.json b/src/test/resources/root-dir/mappings/nested/hello-nested-root-dir.json new file mode 100644 index 0000000..6ae9899 --- /dev/null +++ b/src/test/resources/root-dir/mappings/nested/hello-nested-root-dir.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/hello/nested/root/dir" + }, + "response": { + "status": 200, + "body": "Hello nested root dir" + } +} \ No newline at end of file