--- /dev/null
+package de.juplo.demo;
+
+import static de.juplo.demo.IntegrationTestBase.MOCK_SERVER;
+import java.util.regex.Pattern;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import static org.mockserver.matchers.Times.exactly;
+import static org.mockserver.model.HttpForward.forward;
+import static org.mockserver.model.HttpRequest.request;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@EnableAutoConfiguration
+class DemoApplicationIT extends IntegrationTestBase
+{
+ static Pattern MISSING_PATTERN =
+ Pattern.compile("^404\\ Not\\ Found\\ from\\ GET\\ http:\\/\\/localhost:[0-9]*/missing.txt$");
+
+
+ @Autowired
+ WebTestClient webClient;
+
+
+ @Test
+ @DisplayName("HtmlController - Remote-Response: 200")
+ void testHtmlControllerResponse200() throws Exception
+ {
+ MOCK_SERVER
+ .when(request().withPath("/test.txt"), exactly(1))
+ .forward(forward()
+ .withHost(NGINX.getContainerIpAddress())
+ .withPort(NGINX.getMappedPort(80)));
+
+ webClient
+ .get()
+ .uri("/?path=test.txt")
+ .exchange()
+ .expectStatus().isOk()
+ .expectHeader().contentTypeCompatibleWith(MediaType.TEXT_HTML)
+ .expectBody(String.class).value(rendered ->
+ {
+ Document doc = Jsoup.parse(rendered);
+ assertThat(
+ doc.select("html > body > main > div > div > div > pre").text())
+ .isEqualTo("Hello World!");
+ });
+ }
+
+ @Test
+ @DisplayName("HtmlController - Remote-Response: 404")
+ void testHtmlControllerResponse404() throws Exception
+ {
+ MOCK_SERVER
+ .when(request().withPath("/missing.txt"), exactly(1))
+ .forward(forward()
+ .withHost(NGINX.getContainerIpAddress())
+ .withPort(NGINX.getMappedPort(80)));
+
+ webClient
+ .get()
+ .uri("/?path=missing.txt")
+ .exchange()
+ .expectStatus().isOk()
+ .expectHeader().contentTypeCompatibleWith(MediaType.TEXT_HTML)
+ .expectBody(String.class).value(rendered ->
+ {
+ Document doc = Jsoup.parse(rendered);
+ assertThat(
+ doc.select("html > body > main > div > div > div > pre").text())
+ .matches(MISSING_PATTERN);
+ });
+ }
+
+ @Test
+ @DisplayName("RestController - Remote-Response: 200")
+ void testRestController200() throws Exception
+ {
+ MOCK_SERVER
+ .when(request().withPath("/test.txt"), exactly(1))
+ .forward(forward()
+ .withHost(NGINX.getContainerIpAddress())
+ .withPort(NGINX.getMappedPort(80)));
+
+ webClient
+ .get()
+ .uri("/?path=test.txt")
+ .header("Accept", MediaType.TEXT_PLAIN_VALUE)
+ .exchange()
+ .expectStatus().isOk()
+ .expectHeader().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)
+ .expectBody(String.class).isEqualTo("Hello World!\n");
+ }
+
+ @Test
+ @DisplayName("RestController - Remote-Response: 404")
+ void testRestController404() throws Exception
+ {
+ MOCK_SERVER
+ .when(request().withPath("/missing.txt"), exactly(1))
+ .forward(forward()
+ .withHost(NGINX.getContainerIpAddress())
+ .withPort(NGINX.getMappedPort(80)));
+
+ webClient
+ .get()
+ .uri("/?path=missing.txt")
+ .header("Accept", MediaType.TEXT_PLAIN_VALUE)
+ .exchange()
+ .expectStatus().isNotFound()
+ .expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)
+ .expectBody()
+ .jsonPath("status").isEqualTo(404)
+ .jsonPath("error").isEqualTo("Not Found")
+ .jsonPath("message").isEqualTo(
+ "Cause: 404 Not Found from GET http://localhost:" +
+ MOCK_SERVER.getLocalPort() +
+ "/missing.txt")
+ .jsonPath("timestamp").exists();
+
+ }
+
+ @Configuration
+ static class Application
+ {
+ @Bean
+ RemoteContentService service()
+ {
+ return new RemoteContentService(
+ WebClient
+ .builder()
+ .baseUrl("http://localhost:" + MOCK_SERVER.getLocalPort())
+ .build());
+ }
+
+ @Bean
+ HtmlController htmlController(RemoteContentService service)
+ {
+ return new HtmlController(service);
+ }
+
+ @Bean
+ RestController restController(RemoteContentService service)
+ {
+ return new RestController(service);
+ }
+ }
+}
--- /dev/null
+package de.juplo.demo;
+
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.mockserver.integration.ClientAndServer;
+import org.testcontainers.containers.NginxContainer;
+import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
+
+
+
+/**
+ * This class starts a
+ * {@link https://www.testcontainers.org/modules/nginx/ NginX-Webserver}
+ * via a {@link https://www.testcontainers.org/ Testcontainer} and a
+ * standalone {@link http://www.mock-server.com/#what-is-mockserver MockServer},
+ * that can be used to intercept requests for assertions / verifications.
+ * <p>
+ * We use the {@link
+ * https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers
+ * Singleton Pattern} to start the NginX-Testcontainer and start the MockServer
+ * programmatically via the {@link
+ * http://www.mock-server.com/mock_server/running_mock_server.html#client_api
+ * Client-API}.
+ * @author Kai Moritz
+ */
+public abstract class IntegrationTestBase
+{
+ static final NginxContainer NGINX;
+ static final ClientAndServer MOCK_SERVER;
+
+
+ static
+ {
+ NGINX = new NginxContainer().withCustomContent("src/test/resources/");
+ MOCK_SERVER = ClientAndServer.startClientAndServer();
+ }
+
+
+ @BeforeAll
+ static void startMockServer()
+ {
+ NGINX.waitingFor(new HttpWaitStrategy()).start();
+ }
+
+ @AfterAll
+ static void stopMockServer()
+ {
+ MOCK_SERVER.stop();
+ }
+}