X-Git-Url: https://juplo.de/gitweb/?a=blobdiff_plain;f=dist%2Fhttp-resources%2F2.0.0%2Fxref-test%2Fde%2Fjuplo%2Fhttpresources%2FThymeleafWithHttpResourceProtocolResolverIT.html;fp=dist%2Fhttp-resources%2F2.0.0%2Fxref-test%2Fde%2Fjuplo%2Fhttpresources%2FThymeleafWithHttpResourceProtocolResolverIT.html;h=909fec7afddb5bf5e7a31f7506a2839bdf5ed593;hb=96ec104e2974d001e9bc82c3af8b21029b2042d4;hp=0000000000000000000000000000000000000000;hpb=de1fa457a1c69c673d4dd5c0a2c9af568f74ea12;p=website diff --git a/dist/http-resources/2.0.0/xref-test/de/juplo/httpresources/ThymeleafWithHttpResourceProtocolResolverIT.html b/dist/http-resources/2.0.0/xref-test/de/juplo/httpresources/ThymeleafWithHttpResourceProtocolResolverIT.html new file mode 100644 index 00000000..909fec7a --- /dev/null +++ b/dist/http-resources/2.0.0/xref-test/de/juplo/httpresources/ThymeleafWithHttpResourceProtocolResolverIT.html @@ -0,0 +1,279 @@ + + +
++1 package de.juplo.httpresources; +2 +3 import org.apache.http.entity.StringEntity; +4 import org.junit.jupiter.api.BeforeEach; +5 import org.junit.jupiter.api.Test; +6 import org.mockserver.integration.ClientAndServer; +7 import org.slf4j.Logger; +8 import org.slf4j.LoggerFactory; +9 import org.springframework.beans.factory.annotation.Autowired; +10 import org.springframework.boot.autoconfigure.SpringBootApplication; +11 import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties; +12 import org.springframework.boot.test.context.SpringBootTest; +13 import org.springframework.cache.Cache; +14 import org.springframework.context.ApplicationContext; +15 import org.springframework.context.annotation.Bean; +16 import org.springframework.core.io.Resource; +17 import org.springframework.stereotype.Controller; +18 import org.springframework.test.web.servlet.MockMvc; +19 import org.springframework.test.web.servlet.setup.MockMvcBuilders; +20 import org.springframework.ui.Model; +21 import org.springframework.web.bind.annotation.RequestMapping; +22 import org.springframework.web.bind.annotation.RequestParam; +23 import org.springframework.web.context.WebApplicationContext; +24 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +25 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +26 import org.springframework.web.util.NestedServletException; +27 import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; +28 +29 import java.io.IOException; +30 import java.net.URI; +31 import java.net.URISyntaxException; +32 import java.nio.file.Files; +33 import java.nio.file.Paths; +34 import java.time.Duration; +35 import java.util.Scanner; +36 import java.util.stream.Collectors; +37 +38 import static org.junit.jupiter.api.Assertions.assertThrows; +39 import static org.mockserver.model.HttpRequest.request; +40 import static org.mockserver.verify.VerificationTimes.exactly; +41 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +42 import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +43 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +44 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +45 +46 +47 /** +48 * This test-case showcases the usage of the {@link HttpResourceProtocolResolver} +49 * with Thymeleaf. +50 * +51 * Since Thymeleaf does not use the Resource-Chain mechanism, that Spring introduced +52 * for the handling of static resources, the {@code HttpResourceProtocolResolver} has +53 * to be used and the protocol has to be hardwired into the configuration as +54 * {@code spring.thymeleaf.prefix}. +55 * The downside of this approach is, that the templates are only resolved against +56 * the configured remote-URI and do not take part in the fallback-mechanism, that +57 * can be configured through {@code spring.resources.static-locations}. +58 * +59 * The test-case was addapted accordingly, to show the changed behaviour. +60 */ +61 @SpringBootTest({ +62 "juplo.http-resources.protocol-resolver.enabled=true", +63 "spring.thymeleaf.cache=true" }) +64 public class ThymeleafWithHttpResourceProtocolResolverIT extends IntegrationTestBase +65 { +66 private static final Logger LOG = +67 LoggerFactory.getLogger(ThymeleafWithHttpResourceProtocolResolverIT.class); +68 +69 +70 @Autowired +71 HttpResources resources; +72 @Autowired +73 Cache cache; +74 @Autowired +75 WebApplicationContext context; +76 +77 MockMvc mvc; +78 +79 +80 @BeforeEach +81 public void setUp() +82 { +83 cache.clear(); +84 mvc = MockMvcBuilders +85 .webAppContextSetup(context) +86 .alwaysDo(print()) +87 .build(); +88 } +89 +90 +91 @Test +92 public void testRenderLocalTemplate() throws Exception +93 { +94 LOG.info("<-- start of test-case"); +95 +96 assertThrows( +97 NestedServletException.class, +98 () -> mvc.perform(get(URI.create("http://test/controller.html?template=local")))); +99 +100 server.verify(FETCH("/local.html"), exactly(2)); +101 } +102 +103 @Test +104 public void testRenderRemoteTemplate() throws Exception +105 { +106 LOG.info("<-- start of test-case"); +107 +108 mvc +109 .perform(get(URI.create("http://test/controller.html?template=remote"))) +110 .andExpect(status().isOk()) +111 .andExpect(content().string(read("/rendered/remote.html"))); +112 +113 server.verify(FETCH("/remote.html"), exactly(1)); +114 } +115 +116 @Test +117 public void testRenderCachedRemoteTemplate() throws Exception +118 { +119 LOG.info("<-- start of test-case"); +120 +121 mvc +122 .perform(get(URI.create("http://test/controller.html?template=remote"))) +123 .andExpect(status().isOk()) +124 .andExpect(content().string(read("/rendered/remote.html"))); +125 +126 mvc +127 .perform(get(URI.create("http://test/controller.html?template=remote"))) +128 .andExpect(status().isOk()) +129 .andExpect(content().string(read("/rendered/remote.html"))); +130 +131 mvc +132 .perform(get(URI.create("http://test/controller.html?template=remote"))) +133 .andExpect(status().isOk()) +134 .andExpect(content().string(read("/rendered/remote.html"))); +135 +136 mvc +137 .perform(get(URI.create("http://test/controller.html?template=remote"))) +138 .andExpect(status().isOk()) +139 .andExpect(content().string(read("/rendered/remote.html"))); +140 +141 server.verify(FETCH("/remote.html"), exactly(1)); +142 } +143 +144 @Test +145 public void testRenderModifiedRemoteTemplate() throws Exception +146 { +147 LOG.info("<-- start of test-case"); +148 +149 mvc +150 .perform(get(URI.create("http://test/controller.html?template=remote"))) +151 .andExpect(status().isOk()) +152 .andExpect(content().string(read("/rendered/remote.html"))); +153 +154 CLOCK.timetravel(Duration.ofSeconds(10)); +155 server.when(FETCH("/remote.html")).forward(NGINX("/modified.html")); +156 +157 mvc +158 .perform(get(URI.create("http://test/controller.html?template=remote"))) +159 .andExpect(status().isOk()) +160 .andExpect(content().string(read("/rendered/modified.html"))); +161 +162 server.verify(FETCH("/remote.html"), exactly(2)); +163 server.verify( +164 request() +165 .withPath("/remote.html") +166 .withHeader("If-Modified-Since") +167 .withHeader("If-None-Match"), +168 exactly(1)); +169 } +170 +171 +172 @Controller +173 public static class TestController +174 { +175 +176 @RequestMapping("/controller.html") +177 public String controller( +178 @RequestParam String template, +179 Model model +180 ) +181 { +182 model.addAttribute("template", template); +183 return template; +184 } +185 +186 } +187 +188 +189 static StringEntity body(String resource) throws URISyntaxException, IOException +190 { +191 return new StringEntity(read(resource)); +192 } +193 +194 static String read(String resource) throws URISyntaxException, IOException +195 { +196 URI uri = ThymeleafWithHttpResourceProtocolResolverIT.class.getResource(resource).toURI(); +197 return Files.readAllLines(Paths.get(uri)).stream().collect(Collectors.joining("\n")); +198 } +199 +200 static String read(Resource resource) throws URISyntaxException, IOException +201 { +202 Scanner s = new Scanner(resource.getInputStream()).useDelimiter("\\A"); +203 return s.hasNext() ? s.next() : ""; +204 } +205 +206 +207 @SpringBootApplication +208 public static class Application implements WebMvcConfigurer +209 { +210 @Autowired +211 ClientAndServer server; +212 +213 /** +214 * This mimics the autoconfiguration of Thymeleaf in Spring-Boot. +215 * Reason: Found no other way to inject the property {@code spring.thymeleaf.prefix} dynamically +216 */ +217 @Bean +218 public SpringResourceTemplateResolver defaultTemplateResolver( +219 ThymeleafProperties properties, +220 ApplicationContext applicationContext, +221 ClientAndServer server) +222 { +223 SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); +224 resolver.setApplicationContext(applicationContext); +225 resolver.setPrefix("http://localhost:" + server.getLocalPort() + "/"); // << Cannot be passed in via properties +226 resolver.setSuffix(properties.getSuffix()); +227 resolver.setTemplateMode(properties.getMode()); +228 if (properties.getEncoding() != null) +229 { +230 resolver.setCharacterEncoding(properties.getEncoding().name()); +231 } +232 resolver.setCacheable(false); // << With caching enabled, the caching of HttpResources cannot be tested +233 Integer order = properties.getTemplateResolverOrder(); +234 if (order != null) +235 { +236 resolver.setOrder(order); +237 } +238 resolver.setCheckExistence(properties.isCheckTemplate()); +239 return resolver; +240 } +241 +242 @Bean +243 public TestController testController() +244 { +245 return new TestController(); +246 } +247 +248 @Override +249 public void addResourceHandlers(ResourceHandlerRegistry registry) +250 { +251 LOG.info( +252 "{} resource-handler for static location {}", +253 registry.hasMappingForPattern("/**") ? "Overwriting" : "Setting", +254 "/**" +255 ); +256 registry +257 .addResourceHandler("/**") +258 .addResourceLocations( +259 "classpath:/static/", +260 "classpath:/public/", +261 "http://localhost:" + server.getLocalPort(), +262 "classpath:/fallback/") +263 .resourceChain(false); +264 } +265 } +266 } ++