View Javadoc
1   package de.juplo.httpresources;
2   
3   
4   import static org.assertj.core.api.Assertions.*;
5   import static org.mockserver.verify.VerificationTimes.exactly;
6   
7   import static org.mockserver.model.HttpRequest.request;
8   import org.junit.jupiter.api.BeforeEach;
9   import org.junit.jupiter.api.Test;
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  import org.springframework.beans.factory.annotation.Autowired;
13  import org.springframework.beans.factory.annotation.Value;
14  import org.springframework.boot.autoconfigure.SpringBootApplication;
15  import org.springframework.boot.test.context.SpringBootTest;
16  import org.springframework.cache.Cache;
17  import org.springframework.core.io.Resource;
18  import org.springframework.util.MimeType;
19  
20  import java.time.*;
21  
22  
23  /**
24   *
25   * @author Kai Moritz
26   */
27  @SpringBootTest
28  public class HttpResourcesIT extends IntegrationTestBase
29  {
30    private final static Logger LOG =
31        LoggerFactory.getLogger(HttpResourcesIT.class);
32  
33  
34    @Autowired
35    HttpResources resources;
36    @Autowired
37    Cache cache;
38  
39    @Value("classpath:remote/remote.html")
40    Resource remote;
41    @Value("classpath:remote/modified.html")
42    Resource modified;
43  
44  
45    @BeforeEach
46    public void clearCache()
47    {
48      cache.clear();
49    }
50  
51  
52    @Test
53    public void testFetchExistent() throws Exception
54    {
55      LOG.info("<-- Start of test-case");
56  
57      HttpResource resource = resources.getResource(address("/remote.html"));
58  
59      assertThat(resource.exists()).isTrue();
60      assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
61      assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
62      server.verify(FETCH("/remote.html"), exactly(1));
63    }
64  
65    @Test
66    public void testFetchNonExistent() throws Exception
67    {
68      LOG.info("<-- Start of test-case");
69  
70      HttpResource resource = resources.getResource(address("/peter.html"));
71  
72      assertThat(resource.exists()).isFalse();
73      server.verify(FETCH("/peter.html"), exactly(2));
74    }
75  
76    @Test
77    public void testFetchServerResponse500() throws Exception
78    {
79      LOG.info("<-- Start of test-case");
80  
81      server.when(FETCH("/peter.html")).respond(INTERNAL_SERVER_ERROR());
82  
83      HttpResource resource = resources.getResource(address("/peter.html"));
84  
85      assertThat(resource.exists()).isFalse();
86      server.verify(FETCH("/peter.html"), exactly(2));
87    }
88  
89    @Test
90    public void testCaching() throws Exception
91    {
92      LOG.info("<-- Start of test-case");
93  
94      String uri = address("/remote.html");
95  
96      HttpResource resource;
97  
98      LOG.debug("First access to {}", uri);
99      resource= resources.getResource(uri);
100     assertThat(resource.exists()).isTrue();
101     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
102     assertThat(resource.isModified()).isFalse();
103     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
104 
105     LOG.debug("Second access to {}", uri);
106     resource= resources.getResource(uri);
107     assertThat(resource.exists()).isTrue();
108     assertThat(resource.isModified()).isFalse();
109     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
110     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
111 
112     LOG.debug("Third access to {}", uri);
113     resource= resources.getResource(uri);
114     assertThat(resource.exists()).isTrue();
115     assertThat(resource.isModified()).isFalse();
116     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
117     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
118 
119     LOG.debug("Fourth access to {} after a pause of 10 seconds -- resource should be expired", uri);
120     CLOCK.timetravel(Duration.ofSeconds(10));
121     assertThat(resource.exists()).isTrue();
122     assertThat(resource.isModified()).isFalse();
123     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
124     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
125 
126     LOG.debug("Fifth access to {}", uri);
127     resource= resources.getResource(uri);
128     assertThat(resource.exists()).isTrue();
129     assertThat(resource.isModified()).isFalse();
130     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
131     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
132 
133     LOG.debug("Sixth access to {}", uri);
134     resource= resources.getResource(uri);
135     assertThat(resource.exists()).isTrue();
136     assertThat(resource.isModified()).isFalse();
137     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
138     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
139 
140     server.verify(FETCH("/remote.html"), exactly(2));
141     server.verify(
142         request()
143             .withPath("/remote.html")
144             .withHeader("If-Modified-Since")
145             .withHeader("If-None-Match"),
146         exactly(1));
147   }
148 
149   @Test
150   public void testFetchEncoded() throws Exception
151   {
152     LOG.info("<-- Start of test-case");
153 
154     // To test an encoded response, we have to contact Nginx directly
155     String uri = NGINX_URI() + "/remote.html";
156 
157     HttpResource resource = resources.getResource(uri);
158 
159     assertThat(resource.exists()).isTrue();
160     assertThat(resource.contentType()).isEqualTo(MimeType.valueOf("text/html"));
161     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
162   }
163 
164   @Test
165   public void testModifiedResource() throws Exception
166   {
167     LOG.info("<-- Start of test-case");
168 
169     String uri = address("/remote.html");
170 
171     HttpResource resource;
172 
173     resource= resources.getResource(uri);
174 
175     LOG.debug("Initial access to resource {}", uri);
176     assertThat(resource.isModified()).isFalse();
177     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
178 
179     CLOCK.timetravel(Duration.ofSeconds(10));
180     server.when(FETCH("/remote.html")).forward(NGINX("/modified.html"));
181 
182     LOG.debug("Access to modified resource {} after expiration", uri);
183     assertThat(resource.isModified()).isTrue();
184     assertThat(resource.getInputStream()).hasSameContentAs(modified.getInputStream());
185     assertThat(resource.isModified()).isFalse();
186 
187     server.verify(FETCH("/remote.html"), exactly(2));
188     server.verify(
189         request()
190             .withPath("/remote.html")
191             .withHeader("If-Modified-Since")
192             .withHeader("If-None-Match"),
193         exactly(1));
194   }
195 
196   @Test
197   public void testModifiedResourceAccessOnly() throws Exception
198   {
199     LOG.info("<-- Start of test-case");
200 
201     String uri = address("/remote.html");
202 
203     HttpResource resource;
204 
205     resource= resources.getResource(uri);
206 
207     LOG.debug("Initial access to resource {}", uri);
208     assertThat(resource.getInputStream()).hasSameContentAs(remote.getInputStream());
209 
210     CLOCK.timetravel(Duration.ofSeconds(10));
211     server.when(FETCH("/remote.html")).forward(NGINX("/modified.html"));
212 
213     LOG.debug("Access to modified resource {} after expiration", uri);
214     assertThat(resource.getInputStream()).hasSameContentAs(modified.getInputStream());
215 
216     server.verify(FETCH("/remote.html"), exactly(2));
217     server.verify(
218         request()
219             .withPath("/remote.html")
220             .withHeader("If-Modified-Since")
221             .withHeader("If-None-Match"),
222         exactly(1));
223   }
224 
225 
226   @SpringBootApplication
227   static class Application
228   {
229   }
230 }