Skip to content

Commit 3eacf3a

Browse files
committed
Add configurable rootDir option
1 parent 8b6aede commit 3eacf3a

File tree

10 files changed

+288
-7
lines changed

10 files changed

+288
-7
lines changed

src/main/java/org/wiremock/integrations/testcontainers/WireMockContainer.java

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ public class WireMockContainer extends GenericContainer<WireMockContainer> {
5959
public static final DockerImageName WIREMOCK_2_LATEST =
6060
DockerImageName.parse(OFFICIAL_IMAGE_NAME).withTag(WIREMOCK_2_LATEST_TAG);
6161

62-
private static final String MAPPINGS_DIR = "/home/wiremock/mappings/";
63-
private static final String FILES_DIR = "/home/wiremock/__files/";
62+
private static final String MAPPINGS_DIR = "mappings/";
63+
private static final String FILES_DIR = "__files/";
64+
private static final String CONTAINER_WORKING_DIR = "/home/wiremock/";
65+
private static final String CONTAINER_MAPPINGS_DIR = CONTAINER_WORKING_DIR + MAPPINGS_DIR;
66+
private static final String CONTAINER_FILES_DIR = CONTAINER_WORKING_DIR + FILES_DIR;
6467

6568
private static final String EXTENSIONS_DIR = "/var/wiremock/extensions/";
6669
private static final int PORT = 8080;
@@ -81,6 +84,8 @@ public class WireMockContainer extends GenericContainer<WireMockContainer> {
8184
private final Map<String, WireMockPlugin> plugins = new HashMap<>();
8285
private boolean isBannerDisabled = true;
8386

87+
private File rootDir = new File("src/test/resources");
88+
8489
/**
8590
* Create image from the specified full image name (repo, image, tag)
8691
*/
@@ -130,7 +135,7 @@ public WireMockContainer withBanner() {
130135
isBannerDisabled = false;
131136
return this;
132137
}
133-
138+
134139
/**
135140
* Adds CLI argument to the WireMock call.
136141
* @param arg Argument
@@ -374,12 +379,15 @@ public Integer getPort() {
374379
protected void configure() {
375380
super.configure();
376381
addExposedPorts(PORT);
382+
383+
loadAllFilesFromRootDirectory();
384+
377385
for (Stub stub : mappingStubs.values()) {
378-
withCopyToContainer(Transferable.of(stub.json), MAPPINGS_DIR + stub.name + ".json");
386+
withCopyToContainer(Transferable.of(stub.json), CONTAINER_MAPPINGS_DIR + stub.name + ".json");
379387
}
380388

381389
for (Map.Entry<String, MountableFile> mount : mappingFiles.entrySet()) {
382-
withCopyToContainer(mount.getValue(), FILES_DIR + mount.getKey());
390+
withCopyToContainer(mount.getValue(), CONTAINER_FILES_DIR + mount.getKey());
383391
}
384392

385393
final ArrayList<String> extensionClassNames = new ArrayList<>();
@@ -403,6 +411,54 @@ protected void configure() {
403411
withCommand(wireMockArgs.toString());
404412
}
405413

414+
/**
415+
* Configures the root directory where mappings and files will be loaded recursively.
416+
* If not set, {@code src/test/resources} will be used by default.
417+
* <p>
418+
* Files will be loaded from {@code $rootDir/__files} and mappings from {@code $rootDir/mappings}.
419+
*
420+
* @param rootDir the root directory
421+
* @return this instance
422+
*/
423+
public WireMockContainer withRootDir(File rootDir) {
424+
this.rootDir = rootDir;
425+
return this;
426+
}
427+
428+
private void loadAllFilesFromRootDirectory() {
429+
if (rootDir == null || !rootDir.isDirectory()) {
430+
return;
431+
}
432+
433+
Path mappingsPath = rootDir.toPath().resolve(MAPPINGS_DIR);
434+
getAllFiles(mappingsPath).forEach(path -> withMappingFromJSON(readAllContent(path)));
435+
436+
Path filesPath = rootDir.toPath().resolve(FILES_DIR);
437+
getAllFiles(filesPath).forEach(path ->
438+
withFile(filesPath.relativize(path).toString(), path.toFile()));
439+
}
440+
441+
private List<Path> getAllFiles(Path path) {
442+
443+
if (!Files.exists(path)) {
444+
return Collections.emptyList();
445+
}
446+
447+
try (Stream<Path> stream = Files.walk(path)){
448+
return stream.filter(Files::isRegularFile).collect(Collectors.toList());
449+
} catch (IOException e) {
450+
throw new RuntimeException(e);
451+
}
452+
}
453+
454+
private String readAllContent(Path path) {
455+
try {
456+
return new String(Files.readAllBytes(path));
457+
} catch (IOException e) {
458+
throw new IllegalArgumentException(e);
459+
}
460+
}
461+
406462
private static final class Stub {
407463
final String name;
408464
final String json;
@@ -412,6 +468,4 @@ public Stub(String name, String json) {
412468
this.json = json;
413469
}
414470
}
415-
416-
417471
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright (C) 2025 WireMock Inc, Oleg Nenashev and all project contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.wiremock.integrations.testcontainers;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.testcontainers.junit.jupiter.Container;
20+
import org.testcontainers.junit.jupiter.Testcontainers;
21+
import org.wiremock.integrations.testcontainers.testsupport.http.HttpResponse;
22+
import org.wiremock.integrations.testcontainers.testsupport.http.TestHttpClient;
23+
24+
import java.io.File;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
@Testcontainers(parallel = true)
29+
class WireMockContainerRootDirTest {
30+
@Container
31+
WireMockContainer wiremockServer = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE)
32+
.withMapping("hello", WireMockContainerRootDirTest.class, "hello.json")
33+
.withFileFromResource("file.json", WireMockContainerRootDirTest.class, "file.json")
34+
.withRootDir(new File("src/test/resources/root-dir"));
35+
36+
@Test
37+
void testThatLoadsMappingFromRootDirectory() throws Exception {
38+
// given
39+
String url = wiremockServer.getUrl("hello/root/dir");
40+
41+
// when
42+
HttpResponse response = new TestHttpClient().get(url);
43+
44+
// then
45+
assertThat(response.getBody())
46+
.as("response body")
47+
.contains("Hello root dir");
48+
}
49+
50+
@Test
51+
void testThatLoadsMappingFromNestedRootDirectory() throws Exception {
52+
// given
53+
String url = wiremockServer.getUrl("hello/nested/root/dir");
54+
55+
// when
56+
HttpResponse response = new TestHttpClient().get(url);
57+
58+
// then
59+
assertThat(response.getBody())
60+
.as("response body")
61+
.contains("Hello nested root dir");
62+
}
63+
64+
@Test
65+
void testThatServesFileFromRootDirectory() throws Exception {
66+
// given
67+
String url = wiremockServer.getUrl("root-dir-file.json");
68+
69+
// when
70+
HttpResponse response = new TestHttpClient().get(url);
71+
72+
// then
73+
assertThat(response.getBody())
74+
.as("response body")
75+
.isEqualTo("{ \"message\": \"file from root dir\" }");
76+
}
77+
78+
@Test
79+
void testThatServesNestedFileFromRootDirectory() throws Exception {
80+
// given
81+
String url = wiremockServer.getUrl("nested/root-dir-nested-file.json");
82+
83+
// when
84+
HttpResponse response = new TestHttpClient().get(url);
85+
86+
// then
87+
assertThat(response.getBody())
88+
.as("response body")
89+
.isEqualTo("{ \"message\": \"nested file from root dir\" }");
90+
}
91+
92+
@Test
93+
void testThatDirectMappingsAndFilesAreLoadedWithCustomRootDirEnabled() throws Exception {
94+
// given
95+
String url = wiremockServer.getUrl("/hello");
96+
97+
// when
98+
HttpResponse response = new TestHttpClient().get(url);
99+
100+
// then
101+
assertThat(response.getBody())
102+
.as("Wrong response body")
103+
.contains("file contents from direct mapping");
104+
}
105+
106+
@Test
107+
void testThatDirectMappingsAndFilesAreLoadedEvenWhenRootDirIsSpecified() throws Exception {
108+
// given
109+
String url = wiremockServer.getUrl("/hello");
110+
111+
// when
112+
HttpResponse response = new TestHttpClient().get(url);
113+
114+
// then
115+
assertThat(response.getBody())
116+
.as("Wrong response body")
117+
.contains("file contents from direct mapping");
118+
}
119+
120+
@Test
121+
void testThatMappingsAndFileAreLoadedFromDefaultRootDir() throws Exception {
122+
123+
try (WireMockContainer wmc = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE)) {
124+
wmc.start();
125+
126+
// given
127+
String url = wmc.getUrl("/hello/default/root/dir");
128+
129+
// when
130+
HttpResponse response = new TestHttpClient().get(url);
131+
132+
// then
133+
assertThat(response.getBody())
134+
.as("Wrong response body")
135+
.contains("contents from default root dir file");
136+
}
137+
}
138+
139+
@Test
140+
void testThatInvalidRootDirIsIgnored() throws Exception {
141+
142+
try (WireMockContainer wmc = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE)
143+
.withMapping("hello", WireMockContainerRootDirTest.class, "hello.json")
144+
.withFileFromResource("file.json", WireMockContainerRootDirTest.class, "file.json")
145+
.withRootDir(new File("invalid/root/dir"))) {
146+
wmc.start();
147+
148+
// given
149+
String url = wmc.getUrl("/hello");
150+
151+
// when
152+
HttpResponse response = new TestHttpClient().get(url);
153+
154+
// then
155+
assertThat(response.getBody())
156+
.as("Wrong response body")
157+
.contains("file contents from direct mapping");
158+
}
159+
}
160+
161+
@Test
162+
void testThatNullRootDirIsIgnored() throws Exception {
163+
164+
try (WireMockContainer wmc = new WireMockContainer(TestConfig.WIREMOCK_DEFAULT_IMAGE)
165+
.withMapping("hello", WireMockContainerRootDirTest.class, "hello.json")
166+
.withFileFromResource("file.json", WireMockContainerRootDirTest.class, "file.json")
167+
.withRootDir(null)) {
168+
wmc.start();
169+
170+
// given
171+
String url = wmc.getUrl("/hello");
172+
173+
// when
174+
HttpResponse response = new TestHttpClient().get(url);
175+
176+
// then
177+
assertThat(response.getBody())
178+
.as("Wrong response body")
179+
.contains("file contents from direct mapping");
180+
}
181+
}
182+
183+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "message": "contents from default root dir file" }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"url": "/hello/default/root/dir"
5+
},
6+
"response": {
7+
"status": 200,
8+
"bodyFileName": "default-root-dir-file.json"
9+
}
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "message": "file contents from direct mapping" }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"url": "/hello"
5+
},
6+
"response": {
7+
"status": 200,
8+
"bodyFileName": "file.json"
9+
}
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "message": "nested file from root dir" }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "message": "file from root dir" }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"url": "/hello/root/dir"
5+
},
6+
"response": {
7+
"status": 200,
8+
"body": "Hello root dir"
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"url": "/hello/nested/root/dir"
5+
},
6+
"response": {
7+
"status": 200,
8+
"body": "Hello nested root dir"
9+
}
10+
}

0 commit comments

Comments
 (0)