Skip to content

Commit 423b614

Browse files
vivu001hohwille
andauthored
#756: Add tests for CustomToolRepository using WireMock (#2031)
Co-authored-by: Jörg Hohwiller <hohwille@users.noreply.github.com>
1 parent d118743 commit 423b614

1 file changed

Lines changed: 346 additions & 8 deletions

File tree

Lines changed: 346 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,366 @@
11
package com.devonfw.tools.ide.tool.custom;
22

3+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
4+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
5+
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
6+
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
7+
8+
import java.nio.file.Path;
9+
import java.util.List;
10+
311
import org.assertj.core.api.Assertions;
412
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.io.TempDir;
14+
15+
import com.devonfw.tools.ide.context.IdeTestContext;
16+
import com.devonfw.tools.ide.tool.ToolCommandlet;
17+
import com.devonfw.tools.ide.tool.repository.ToolRepository;
18+
import com.devonfw.tools.ide.url.model.file.json.ToolSecurity;
19+
import com.devonfw.tools.ide.version.GenericVersionRange;
20+
import com.devonfw.tools.ide.version.VersionIdentifier;
21+
import com.devonfw.tools.ide.version.VersionRange;
22+
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
23+
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
524

625
/**
726
* Test of {@link CustomToolRepository}.
827
*/
28+
@WireMockTest
929
class CustomToolRepositoryTest extends Assertions {
1030

31+
private static final String ORIGINAL_BASE_URL = "https://some-file-server.company.com";
32+
private static final Path SETTINGS_PATH = Path.of("src/test/resources/customtools");
33+
private static final String BASE_PATH = "/projects/my-project";
34+
private static final String TOOL_JBOSS_EAP = "jboss-eap";
35+
private static final String VERSION_JBOSS = "7.1.4.GA";
36+
private static final String TOOL_FIREFOX = "firefox";
37+
private static final String VERSION_FIREFOX = "70.0.1";
38+
39+
private IdeTestContext context;
40+
private CustomToolRepository repository;
41+
42+
/**
43+
* Tests that the repository can be loaded from custom-tools.json and provides the correct number of tools.
44+
*
45+
* @param tempDir temporary test directory used as working directory and settings base.
46+
* @param wmRuntimeInfo wireMock server on a random port.
47+
*/
48+
@Test
49+
void testLoadRepositoryFromJson(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
50+
// arrange
51+
setUpRepository(tempDir, wmRuntimeInfo);
52+
53+
// assert
54+
assertThat(this.repository).isNotNull();
55+
assertThat(this.repository.getTools()
56+
.stream()
57+
.map(tool -> tool.getTool() + ":" + tool.getVersion()))
58+
.contains("jboss-eap:7.1.4.GA", "firefox:70.0.1");
59+
}
60+
61+
/**
62+
* Tests that the custom tool collection exposed by the repository is immutable.
63+
*
64+
* @param tempDir temporary test directory used as working directory and settings base.
65+
* @param wmRuntimeInfo wireMock server on a random port.
66+
*/
67+
@Test
68+
void testGetToolsReturnsImmutableCollection(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
69+
// arrange
70+
setUpRepository(tempDir, wmRuntimeInfo);
71+
CustomToolMetadata tool = new CustomToolMetadata("extra", "1.0", null, null, "https://example.com/extra.tgz", null, "https://example.com");
72+
73+
// act & assert
74+
assertThatThrownBy(() -> this.repository.getTools().add(tool)).isInstanceOf(UnsupportedOperationException.class);
75+
}
76+
77+
/**
78+
* Tests that the repository correctly provides tool metadata for os-agnostic tools.
79+
*
80+
* @param tempDir temporary test directory used as working directory and settings base.
81+
* @param wmRuntimeInfo wireMock server on a random port.
82+
*/
1183
@Test
12-
void testRepositoryId() {
84+
void testGetToolMetadataOsAgnostic(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
85+
// arrange
86+
setUpRepository(tempDir, wmRuntimeInfo);
1387

88+
// act
89+
CustomToolMetadata jbossEap = findTool(TOOL_JBOSS_EAP);
90+
91+
// assert
92+
assertThat(jbossEap).isNotNull();
93+
assertThat(jbossEap.getTool()).isEqualTo(TOOL_JBOSS_EAP);
94+
assertThat(jbossEap.getVersion()).isEqualTo(VersionIdentifier.of(VERSION_JBOSS));
95+
assertThat(jbossEap.getOs()).isNull();
96+
assertThat(jbossEap.getArch()).isNull();
97+
assertThat(jbossEap.getUrl()).contains(BASE_PATH);
98+
}
99+
100+
// --- Version resolution --------------------------------------------------
101+
102+
/**
103+
* Tests that the repository correctly resolves version constraints.
104+
*
105+
* @param tempDir temporary test directory used as working directory and settings base.
106+
* @param wmRuntimeInfo wireMock server on a random port.
107+
*/
108+
@Test
109+
void testResolveVersionForTool(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
14110
// arrange
15-
String name = "jboss-eap";
16-
String version = "7.4.5.GA";
17-
String repositoryUrl = "https://host.domain.tld:port/folder/räpö$itöry+name/ochn%C3%B6n%F6";
111+
setUpRepository(tempDir, wmRuntimeInfo);
112+
GenericVersionRange versionRange = VersionIdentifier.of(VERSION_JBOSS);
18113

19114
// act
20-
CustomTool tool = new CustomTool(name, version, false, false, repositoryUrl);
115+
VersionIdentifier resolved = this.repository.resolveVersion(TOOL_JBOSS_EAP, TOOL_JBOSS_EAP, versionRange, null);
21116

22117
// assert
23-
assertThat(tool.name()).isEqualTo(name);
24-
assertThat(tool.version()).isSameAs(version);
25-
assertThat(tool.url()).isEqualTo(repositoryUrl);
118+
assertThat(resolved).isEqualTo(VersionIdentifier.of(VERSION_JBOSS));
119+
}
120+
121+
/**
122+
* Tests that a version that does not satisfy the constraint throws an exception.
123+
*
124+
* @param tempDir temporary test directory used as working directory and settings base.
125+
* @param wmRuntimeInfo wireMock server on a random port.
126+
*/
127+
@Test
128+
void testResolveInvalidVersionThrowsException(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
129+
// arrange
130+
setUpRepository(tempDir, wmRuntimeInfo);
131+
GenericVersionRange versionRange = VersionRange.of("8.0");
132+
133+
// act & assert
134+
assertThatThrownBy(() -> this.repository.resolveVersion(TOOL_JBOSS_EAP, TOOL_JBOSS_EAP, versionRange, null)).isInstanceOf(IllegalStateException.class)
135+
.hasMessageContaining("does not satisfy version");
136+
}
137+
138+
// --- Downloads via WireMock ----------------------------------------------
139+
140+
/**
141+
* Tests that a 404 response from the file server causes download to throw.
142+
*
143+
* @param tempDir temporary test directory used as working directory and settings base.
144+
* @param wmRuntimeInfo wireMock server on a random port.
145+
*/
146+
@Test
147+
void testDownloadReturns404ThrowsException(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
148+
// arrange
149+
setUpRepository(tempDir, wmRuntimeInfo);
150+
String expectedPath = jbossEapPath();
151+
152+
stubFor(get(urlEqualTo(expectedPath)).willReturn(aResponse().withStatus(404)));
153+
154+
// act & assert
155+
assertThatThrownBy(() -> this.repository.download(TOOL_JBOSS_EAP, TOOL_JBOSS_EAP, VersionIdentifier.of(VERSION_JBOSS), null))
156+
.isInstanceOf(IllegalStateException.class);
157+
}
158+
159+
/**
160+
* Tests that a 500 server error causes download to throw.
161+
*
162+
* @param tempDir temporary test directory used as working directory and settings base.
163+
* @param wmRuntimeInfo wireMock server on a random port.
164+
*/
165+
@Test
166+
void testDownloadReturns500ThrowsException(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
167+
// arrange
168+
setUpRepository(tempDir, wmRuntimeInfo);
169+
String expectedPath = jbossEapPath();
170+
171+
stubFor(get(urlEqualTo(expectedPath)).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
172+
173+
// act & assert
174+
assertThatThrownBy(() -> this.repository.download(TOOL_JBOSS_EAP, TOOL_JBOSS_EAP, VersionIdentifier.of(VERSION_JBOSS), null))
175+
.isInstanceOf(IllegalStateException.class);
26176
}
27177

178+
/**
179+
* Tests that download validates the requested version against the custom tool metadata.
180+
*
181+
* @param tempDir temporary test directory used as working directory and settings base.
182+
* @param wmRuntimeInfo wireMock server on a random port.
183+
*/
184+
@Test
185+
void testDownloadWithUndefinedVersionThrowsException(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
186+
// arrange
187+
setUpRepository(tempDir, wmRuntimeInfo);
188+
189+
// act & assert
190+
assertThatThrownBy(() -> this.repository.download(TOOL_JBOSS_EAP, TOOL_JBOSS_EAP, VersionIdentifier.of("7.1.5.GA"), null))
191+
.isInstanceOf(IllegalArgumentException.class)
192+
.hasMessageContaining("Undefined version")
193+
.hasMessageContaining(VERSION_JBOSS);
194+
}
195+
196+
// --- Repository metadata ----------------------------------------------------
197+
198+
/**
199+
* Tests that {@link com.devonfw.tools.ide.url.model.AbstractUrlMetadata#getSortedVersions(String, String, ToolCommandlet)} returns the single configured
200+
* custom tool version.
201+
*
202+
* @param tempDir temporary test directory used as working directory and settings base.
203+
* @param wmRuntimeInfo wireMock server on a random port.
204+
*/
205+
@Test
206+
void testGetSortedVersions(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
207+
// arrange
208+
setUpRepository(tempDir, wmRuntimeInfo);
209+
210+
// act
211+
List<VersionIdentifier> versions = this.repository.getSortedVersions(TOOL_FIREFOX, TOOL_FIREFOX, null);
212+
213+
// assert
214+
assertThat(versions).containsExactly(VersionIdentifier.of(VERSION_FIREFOX));
215+
}
216+
217+
/**
218+
* Tests that {@link com.devonfw.tools.ide.url.model.AbstractUrlMetadata#getSortedEditions(String)} returns the configured custom tool edition.
219+
*
220+
* @param tempDir temporary test directory used as working directory and settings base.
221+
* @param wmRuntimeInfo wireMock server on a random port.
222+
*/
223+
@Test
224+
void testGetSortedEditions(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
225+
// arrange
226+
setUpRepository(tempDir, wmRuntimeInfo);
227+
228+
// act & assert
229+
assertThat(this.repository.getSortedEditions(TOOL_FIREFOX)).containsExactly(TOOL_FIREFOX);
230+
}
231+
232+
/**
233+
* Tests that custom tools do not define additional dependencies.
234+
*
235+
* @param tempDir temporary test directory used as working directory and settings base.
236+
* @param wmRuntimeInfo wireMock server on a random port.
237+
*/
238+
@Test
239+
void testFindDependenciesReturnsEmptyCollection(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
240+
// arrange
241+
setUpRepository(tempDir, wmRuntimeInfo);
242+
243+
// act & assert
244+
assertThat(this.repository.findDependencies(TOOL_FIREFOX, TOOL_FIREFOX, VersionIdentifier.of(VERSION_FIREFOX))).isEmpty();
245+
}
246+
247+
/**
248+
* Tests that custom tools return empty security metadata.
249+
*
250+
* @param tempDir temporary test directory used as working directory and settings base.
251+
* @param wmRuntimeInfo wireMock server on a random port.
252+
*/
253+
@Test
254+
void testFindSecurityReturnsEmptySecurity(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
255+
// arrange
256+
setUpRepository(tempDir, wmRuntimeInfo);
257+
258+
// act
259+
ToolSecurity security = this.repository.findSecurity(TOOL_FIREFOX, TOOL_FIREFOX);
260+
261+
// assert
262+
assertThat(security).isSameAs(ToolSecurity.getEmpty());
263+
}
264+
265+
// --- Commandlet -------------------------------------------------------------
266+
267+
/**
268+
* Tests that {@link CustomToolCommandlet} exposes the configured custom tool values.
269+
*/
270+
@Test
271+
void testCreateCustomToolCommandlet(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
272+
// arrange
273+
IdeTestContext context = new IdeTestContext(tempDir, wmRuntimeInfo);
274+
String repositoryUrl = "https://example.com/repo";
275+
CustomToolMetadata customTool = new CustomToolMetadata("custom-cli", "1.2.3", null, null,
276+
repositoryUrl + "/custom-cli/1.2.3/custom-cli-1.2.3.tgz", null, repositoryUrl);
277+
278+
// act
279+
CustomToolCommandlet commandlet = new CustomToolCommandlet(context, customTool);
280+
281+
// assert
282+
assertThat(commandlet.getName()).isEqualTo("custom-cli");
283+
assertThat(commandlet.getConfiguredVersion()).isEqualTo(VersionIdentifier.of("1.2.3"));
284+
assertThat(commandlet.getConfiguredEdition()).isEqualTo("custom-cli");
285+
assertThat(commandlet.getTags()).isNull();
286+
}
287+
288+
/**
289+
* Tests that {@link CustomToolCommandlet} delegates repository access to the context.
290+
*
291+
* @param tempDir the temporary test directory used as isolated working directory and parent directory for the generated settings folder.
292+
* @param wmRuntimeInfo wireMock server on a random port.
293+
*/
294+
@Test
295+
void testCustomToolCommandletReturnsContextCustomToolRepository(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
296+
// arrange
297+
setUpRepository(tempDir, wmRuntimeInfo);
298+
CustomToolMetadata customTool = findTool(TOOL_JBOSS_EAP);
299+
CustomToolCommandlet commandlet = new CustomToolCommandlet(this.context, customTool);
300+
301+
// act
302+
ToolRepository toolRepository = commandlet.getToolRepository();
303+
304+
// assert
305+
assertThat(toolRepository).isSameAs(this.context.getCustomToolRepository());
306+
}
307+
308+
/**
309+
* Creates a fully initialized {@link CustomToolRepository} for the current test.
310+
*
311+
* @param tempDir the temporary test directory used as isolated working directory and parent directory for the generated settings folder.
312+
* @param wmRuntimeInfo wireMock server on a random port.
313+
*/
314+
private void setUpRepository(Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
315+
this.context = new IdeTestContext(tempDir, wmRuntimeInfo);
316+
this.context.getNetworkStatus().simulateOnline();
317+
318+
Path settingsPath = tempDir.resolve("settings");
319+
this.context.getFileAccess().mkdirs(settingsPath);
320+
321+
CustomTools customTools = CustomToolsMapper.get().loadJsonFromFolder(SETTINGS_PATH);
322+
CustomTools resolvedCustomTools = resolveWireMockUrl(customTools, wmRuntimeInfo.getHttpBaseUrl());
323+
324+
CustomToolsMapper.get().saveJson(resolvedCustomTools, settingsPath.resolve(CustomToolsMapper.get().getStandardFilename()));
325+
326+
this.context.setSettingsPath(settingsPath);
327+
this.repository = CustomToolRepositoryImpl.of(this.context);
328+
}
329+
330+
/**
331+
* Replaces the static repository base URL from the test resource with the runtime base URL of the WireMock server.
332+
* <p>
333+
* The custom tools JSON contains stable, readable URls that model a real file server. During tests, downloads must be served by WireMock instead. This method
334+
* keeps the configured paths unchanged while replacing only the host/base URL, so assertions can still verify the expected repository paths.
335+
*
336+
* @param customTools the custom tools configuration loaded from the test resource.
337+
* @param wireMockBaseUrl the base URL of the active WireMock server.
338+
* @return a copy of {@code customTools} with all repository URLs pointing to WireMock.
339+
*/
340+
private CustomTools resolveWireMockUrl(CustomTools customTools, String wireMockBaseUrl) {
341+
List<CustomTool> tools = customTools.tools().stream().map(tool -> {
342+
String url = tool.url();
343+
if (url != null) {
344+
url = url.replace(ORIGINAL_BASE_URL, wireMockBaseUrl);
345+
}
346+
return new CustomTool(tool.name(), tool.version(), tool.osAgnostic(), tool.archAgnostic(), url);
347+
}).toList();
348+
349+
String url = customTools.url().replace(ORIGINAL_BASE_URL, wireMockBaseUrl);
350+
return new CustomTools(url, tools);
351+
}
352+
353+
/**
354+
* Finds the configured custom tool metadata by tool name.
355+
*
356+
* @param toolName the name of the custom tool to find.
357+
* @return the matching {@link CustomToolMetadata}, or {@code null} if no such tool is configured.
358+
*/
359+
private CustomToolMetadata findTool(String toolName) {
360+
return this.repository.getTools().stream().filter(t -> t.getTool().equals(toolName)).findFirst().orElse(null);
361+
}
362+
363+
private static String jbossEapPath() {
364+
return BASE_PATH + "/" + TOOL_JBOSS_EAP + "/" + VERSION_JBOSS + "/" + TOOL_JBOSS_EAP + "-" + VERSION_JBOSS + ".tgz";
365+
}
28366
}

0 commit comments

Comments
 (0)