1 package de.juplo.httpresources; 2 3 import org.junit.jupiter.api.BeforeEach; 4 import org.junit.jupiter.api.Test; 5 import org.mockserver.integration.ClientAndServer; 6 import org.slf4j.Logger; 7 import org.slf4j.LoggerFactory; 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.boot.autoconfigure.SpringBootApplication; 10 import org.springframework.boot.test.context.SpringBootTest; 11 import org.springframework.cache.Cache; 12 import org.springframework.context.annotation.Bean; 13 import org.springframework.test.web.servlet.MockMvc; 14 import org.springframework.test.web.servlet.setup.MockMvcBuilders; 15 import org.springframework.web.context.WebApplicationContext; 16 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 17 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 18 import org.springframework.web.servlet.resource.VersionResourceResolver; 19 20 import java.net.URI; 21 import java.time.Clock; 22 import java.time.Instant; 23 import java.time.ZoneId; 24 25 import static de.juplo.httpresources.TestUtil.LONG_NOW; 26 import static de.juplo.httpresources.TestUtil.read; 27 import static org.mockserver.verify.VerificationTimes.exactly; 28 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 29 import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 30 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 31 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 32 33 34 @SpringBootTest({ 35 "juplo.http-resources.protocol-resolver.enabled=true" 36 }) 37 public class VersionedResourcesIT extends IntegrationTestBase 38 { 39 private final static Logger LOG = 40 LoggerFactory.getLogger(VersionedResourcesIT.class); 41 42 43 @Autowired 44 HttpResources resources; 45 @Autowired 46 Cache cache; 47 @Autowired 48 WebApplicationContext context; 49 50 MockMvc mvc; 51 52 53 @BeforeEach 54 public void setUp() 55 { 56 cache.clear(); 57 mvc = MockMvcBuilders 58 .webAppContextSetup(context) 59 .alwaysDo(print()) 60 .build(); 61 } 62 63 64 @Test 65 public void testTransformation() throws Exception 66 { 67 LOG.info("<-- start of test-case"); 68 69 mvc 70 .perform(get(URI.create("http://test/css/static.css"))) 71 .andExpect(status().isOk()) 72 .andExpect(content().string(read("/transformed/css/static.css"))); 73 mvc 74 .perform(get(URI.create("http://test/css/sub/static.css"))) 75 .andExpect(status().isOk()) 76 .andExpect(content().string(read("/transformed/css/static.css"))); 77 78 mvc 79 .perform(get(URI.create("http://test/css/remote.css"))) 80 .andExpect(status().isOk()) 81 .andExpect(content().string(read("/transformed/css/remote.css"))); 82 mvc 83 .perform(get(URI.create("http://test/css/sub/remote.css"))) 84 .andExpect(status().isOk()) 85 .andExpect(content().string(read("/transformed/css/remote.css"))); 86 mvc 87 .perform(get(URI.create("http://test/css/remote-c901f3a6ab390732242f0fc09ca170e7.css"))) 88 .andExpect(status().isOk()) 89 .andExpect(content().string(read("/transformed/css/remote.css"))); 90 mvc 91 .perform(get(URI.create("http://test/css/sub/remote-c901f3a6ab390732242f0fc09ca170e7.css"))) 92 .andExpect(status().isOk()) 93 .andExpect(content().string(read("/transformed/css/remote.css"))); 94 95 mvc 96 .perform(get(URI.create("http://test/css/fallback.css"))) 97 .andExpect(status().isOk()) 98 .andExpect(content().string(read("/transformed/css/static.css"))); 99 mvc 100 .perform(get(URI.create("http://test/css/sub/fallback.css"))) 101 .andExpect(status().isOk()) 102 .andExpect(content().string(read("/transformed/css/static.css"))); 103 mvc 104 .perform(get(URI.create("http://test/css/fallback-c901f3a6ab390732242f0fc09ca170e7.css"))) 105 .andExpect(status().isOk()) 106 .andExpect(content().string(read("/transformed/css/static.css"))); 107 mvc 108 .perform(get(URI.create("http://test/css/sub/fallback-c901f3a6ab390732242f0fc09ca170e7.css"))) 109 .andExpect(status().isOk()) 110 .andExpect(content().string(read("/transformed/css/static.css"))); 111 112 mvc 113 .perform(get(URI.create("http://test/css/static-c901f3a6ab390732242f0fc09ca170e7.css"))) 114 .andExpect(status().isOk()) 115 .andExpect(content().string(read("/transformed/css/static.css"))); 116 mvc 117 .perform(get(URI.create("http://test/css/remote.css"))) 118 .andExpect(status().isOk()) 119 .andExpect(content().string(read("/transformed/css/remote.css"))); 120 mvc 121 .perform(get(URI.create("http://test/css/remote-c901f3a6ab390732242f0fc09ca170e7.css"))) 122 .andExpect(status().isOk()) 123 .andExpect(content().string(read("/transformed/css/remote.css"))); 124 mvc 125 .perform(get(URI.create("http://test/css/static-c901f3a6ab390732242f0fc09ca170e7.css"))) 126 .andExpect(status().isOk()) 127 .andExpect(content().string(read("/transformed/css/static.css"))); 128 129 server.verify(FETCH("/css/remote.css"), exactly(1)); 130 server.verify(FETCH("/css/fallback.css"), exactly(8)); 131 } 132 133 134 @SpringBootApplication 135 public static class Application implements WebMvcConfigurer 136 { 137 @Bean 138 public Clock clock() 139 { 140 return Clock.fixed(Instant.ofEpochMilli(LONG_NOW), ZoneId.systemDefault()); 141 } 142 143 @Bean 144 public VersionResourceResolver versionResourceResolver() 145 { 146 VersionResourceResolver resolver = new VersionResourceResolver(); 147 resolver.addFixedVersionStrategy("v12", "/img/**"); 148 resolver.addContentVersionStrategy("/**"); 149 return resolver; 150 } 151 152 153 @Autowired 154 VersionResourceResolver versionResourceResolver; 155 @Autowired 156 ClientAndServer server; 157 158 @Override 159 public void addResourceHandlers(ResourceHandlerRegistry registry) 160 { 161 registry 162 .addResourceHandler("/**") 163 .addResourceLocations( 164 "classpath:/static/", 165 "http://localhost:" + server.getLocalPort(), 166 "classpath:/fallback/") 167 .resourceChain(true) 168 .addResolver(versionResourceResolver); 169 } 170 } 171 }