source: josm/trunk/test/unit/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJobTest.java@ 17442

Last change on this file since 17442 was 17075, checked in by Don-vip, 4 years ago

see #15102 - see #16637 - get rid of real HTTP calls to http://httpstat.us in unit tests, mock them

  • Property svn:eol-style set to native
File size: 26.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.cache;
3
4import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
5import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
6import static com.github.tomakehurst.wiremock.client.WireMock.get;
7import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
8import static com.github.tomakehurst.wiremock.client.WireMock.head;
9import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor;
10import static com.github.tomakehurst.wiremock.client.WireMock.status;
11import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
12import static org.junit.Assert.assertArrayEquals;
13import static org.junit.Assert.assertEquals;
14import static org.junit.Assert.assertFalse;
15import static org.junit.Assert.assertTrue;
16
17import java.io.IOException;
18import java.net.MalformedURLException;
19import java.net.URL;
20import java.nio.charset.StandardCharsets;
21import java.util.concurrent.TimeUnit;
22
23import org.apache.commons.jcs3.access.behavior.ICacheAccess;
24import org.apache.commons.jcs3.engine.behavior.ICacheElement;
25import org.junit.Before;
26import org.junit.Rule;
27import org.junit.Test;
28import org.openstreetmap.josm.TestUtils;
29import org.openstreetmap.josm.data.cache.ICachedLoaderListener.LoadResult;
30import org.openstreetmap.josm.data.imagery.TileJobOptions;
31import org.openstreetmap.josm.testutils.JOSMTestRules;
32import org.openstreetmap.josm.tools.Logging;
33
34import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
35import com.github.tomakehurst.wiremock.junit.WireMockRule;
36import com.github.tomakehurst.wiremock.matching.UrlPattern;
37
38import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39
40/**
41 * Unit tests for class {@link JCSCachedTileLoaderJob}.
42 */
43public class JCSCachedTileLoaderJobTest {
44
45 /**
46 * mocked tile server
47 */
48 @Rule
49 public WireMockRule tileServer = new WireMockRule(WireMockConfiguration.options()
50 .dynamicPort());
51
52 private static class TestCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, CacheEntry> {
53 private final String url;
54 private final String key;
55
56 TestCachedTileLoaderJob(String url, String key) {
57 this(url, key, (int) TimeUnit.DAYS.toSeconds(1));
58 }
59
60 TestCachedTileLoaderJob(String url, String key, int minimumExpiry) {
61 super(getCache(), new TileJobOptions(30000, 30000, null, minimumExpiry));
62
63 this.url = url;
64 this.key = key;
65 }
66
67 @Override
68 public String getCacheKey() {
69 return key;
70 }
71
72 @Override
73 public URL getUrl() {
74 try {
75 return new URL(url);
76 } catch (MalformedURLException e) {
77 throw new RuntimeException(e);
78 }
79 }
80
81 @Override
82 protected CacheEntry createCacheEntry(byte[] content) {
83 return new CacheEntry(content);
84 }
85 }
86
87 private static class Listener implements ICachedLoaderListener {
88 private CacheEntryAttributes attributes;
89 private boolean ready;
90 private LoadResult result;
91 private byte[] data;
92
93 @Override
94 public synchronized void loadingFinished(CacheEntry data, CacheEntryAttributes attributes, LoadResult result) {
95 this.attributes = attributes;
96 this.ready = true;
97 this.result = result;
98 if (data != null) {
99 this.data = data.content;
100 }
101 this.notifyAll();
102 }
103 }
104
105 /**
106 * Setup test.
107 */
108 @Rule
109 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
110 public JOSMTestRules test = new JOSMTestRules().preferences().timeout(20_000);
111
112 /**
113 * Always clear cache before tests
114 * @throws Exception when clearing fails
115 */
116 @Before
117 public void clearCache() throws Exception {
118 getCache().clear();
119 }
120
121 /**
122 * Test status codes
123 * @throws InterruptedException in case of thread interruption
124 * @throws IOException in case of I/O error
125 */
126 @Test
127 public void testStatusCodes() throws IOException, InterruptedException {
128 doTestStatusCode(200);
129 doTestStatusCode(401);
130 doTestStatusCode(402);
131 doTestStatusCode(403);
132 doTestStatusCode(404);
133 doTestStatusCode(405);
134 doTestStatusCode(500);
135 doTestStatusCode(501);
136 doTestStatusCode(502);
137 }
138
139 /**
140 * Test unknown host
141 * @throws IOException in case of I/O error
142 */
143 @Test
144 public void testUnknownHost() throws IOException {
145 String key = "key_unknown_host";
146 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob("http://unkownhost.unkownhost/unkown", key);
147 Listener listener = submitJob(job);
148 assertEquals(LoadResult.FAILURE, listener.result); // because response will be cached, and that is checked below
149 assertEquals("java.net.UnknownHostException: unkownhost.unkownhost", listener.attributes.getErrorMessage());
150
151 ICacheAccess<String, CacheEntry> cache = getCache();
152 CacheEntry e = new CacheEntry(new byte[]{0, 1, 2, 3});
153 CacheEntryAttributes attributes = new CacheEntryAttributes();
154 attributes.setExpirationTime(2);
155 cache.put(key, e, attributes);
156
157 job = new TestCachedTileLoaderJob("http://unkownhost.unkownhost/unkown", key);
158 listener = submitJob(job);
159 assertEquals(LoadResult.SUCCESS, listener.result);
160 assertFalse(job.isCacheElementValid());
161 }
162
163 private void doTestStatusCode(int responseCode) throws IOException {
164 tileServer.stubFor(get(urlEqualTo("/httpstat/" + responseCode)).willReturn(aResponse().withStatus(responseCode)));
165 TestCachedTileLoaderJob job = getStatusLoaderJob(responseCode);
166 Listener listener = submitJob(job);
167 assertEquals(responseCode, listener.attributes.getResponseCode());
168 }
169
170 private Listener submitJob(TestCachedTileLoaderJob job) throws IOException {
171 return submitJob(job, true);
172 }
173
174 private Listener submitJob(TestCachedTileLoaderJob job, boolean force) throws IOException {
175 Listener listener = new Listener();
176 job.submit(listener, force);
177 synchronized (listener) {
178 while (!listener.ready) {
179 try {
180 listener.wait();
181 } catch (InterruptedException e) {
182 // do nothing, wait
183 Logging.trace(e);
184 }
185 }
186 }
187 return listener;
188 }
189
190 /**
191 * That no request is made when entry is in cache and force == false
192 * @throws IOException exception
193 */
194 @Test
195 public void testNoRequestMadeWhenEntryInCache() throws IOException {
196 ICacheAccess<String, CacheEntry> cache = getCache();
197 long expires = TimeUnit.DAYS.toMillis(1);
198 long testStart = System.currentTimeMillis();
199 cache.put("test",
200 new CacheEntry("cached entry".getBytes(StandardCharsets.UTF_8)),
201 createEntryAttributes(expires, 200, testStart, "eTag")
202 );
203 createHeadGetStub(urlEqualTo("/test"), expires, testStart, "eTag", "mock entry");
204
205 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test");
206 Listener listener = submitJob(job, false);
207 tileServer.verify(0, getRequestedFor(anyUrl()));
208 assertArrayEquals("cached entry".getBytes(StandardCharsets.UTF_8), listener.data);
209 }
210
211 /**
212 * that request is made, when object is in cache, but force mode is used
213 * @throws IOException exception
214 */
215 @Test
216 public void testRequestMadeWhenEntryInCacheAndForce() throws IOException {
217 ICacheAccess<String, CacheEntry> cache = getCache();
218 long expires = TimeUnit.DAYS.toMillis(1);
219 long testStart = System.currentTimeMillis();
220 cache.put("test",
221 new CacheEntry("cached dummy".getBytes(StandardCharsets.UTF_8)),
222 createEntryAttributes(expires, 200, testStart + expires, "eTag")
223 );
224 createHeadGetStub(urlEqualTo("/test"), expires, testStart, "eTag", "mock entry");
225
226 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test");
227 Listener listener = submitJob(job, true);
228 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
229 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
230 }
231
232 /**
233 * Mock returns no cache-control / expires headers
234 * Expire time should be set to DEFAULT_EXPIRE_TIME
235 * @throws IOException exception
236 */
237 @Test
238 public void testSettingMinimumExpiryWhenNoExpires() throws IOException {
239 long testStart = System.currentTimeMillis();
240 tileServer.stubFor(get(urlEqualTo("/test")).willReturn(aResponse().withBody("mock entry")));
241
242 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test");
243 Listener listener = submitJob(job, false);
244 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
245
246 assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
247 JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME + " (DEFAULT_EXPIRE_TIME)",
248 listener.attributes.getExpirationTime() >= testStart + JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME);
249
250 assertTrue("Cache entry expiration is " +
251 (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
252 " which is not less than " +
253 JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME + " (DEFAULT_EXPIRE_TIME)",
254 listener.attributes.getExpirationTime() <= System.currentTimeMillis() + JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME
255 );
256
257 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
258 }
259
260 /**
261 * Mock returns expires headers, but Cache-Control
262 * Expire time should be set to max-age
263 * @throws IOException exception
264 */
265 @Test
266 public void testSettingExpireByMaxAge() throws IOException {
267 long testStart = System.currentTimeMillis();
268 long expires = TimeUnit.DAYS.toSeconds(1);
269 tileServer.stubFor(get(urlEqualTo("/test"))
270 .willReturn(aResponse()
271 .withHeader("Cache-control", "max-age=" + expires)
272 .withBody("mock entry")
273 )
274 );
275
276 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test");
277 Listener listener = submitJob(job, false);
278 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
279
280 assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
281 TimeUnit.SECONDS.toMillis(expires) + " (max-age)",
282 listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(expires));
283
284 assertTrue("Cache entry expiration is " +
285 (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
286 " which is not less than " +
287 TimeUnit.SECONDS.toMillis(expires) + " (max-age)",
288 listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expires)
289 );
290
291 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
292 }
293
294 /**
295 * mock returns expiration: JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10
296 * minimum expire time: JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2
297 * @throws IOException exception
298 */
299 @Test
300 public void testSettingMinimumExpiryByMinimumExpiryTimeLessThanDefault() throws IOException {
301 long testStart = System.currentTimeMillis();
302 int minimumExpiryTimeSeconds = (int) (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2);
303
304 createHeadGetStub(urlEqualTo("/test"), (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10), testStart, "eTag", "mock entry");
305
306 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test", minimumExpiryTimeSeconds);
307 Listener listener = submitJob(job, false);
308 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
309 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
310
311
312 assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
313 TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
314 listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds));
315
316 assertTrue("Cache entry expiration is " +
317 (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
318 " which is not less than " +
319 TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
320 listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds)
321 );
322 }
323
324 /**
325 * mock returns expiration: JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10
326 * minimum expire time: JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME * 2
327 * @throws IOException exception
328 */
329
330 @Test
331 public void testSettingMinimumExpiryByMinimumExpiryTimeGreaterThanDefault() throws IOException {
332 long testStart = System.currentTimeMillis();
333 int minimumExpiryTimeSeconds = (int) (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME * 2);
334
335 createHeadGetStub(urlEqualTo("/test"), (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10), testStart, "eTag", "mock entry");
336
337 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test", minimumExpiryTimeSeconds);
338 Listener listener = submitJob(job, false);
339 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
340 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
341
342
343 assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
344 TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
345 listener.attributes.getExpirationTime() >= testStart + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds));
346
347 assertTrue("Cache entry expiration is " +
348 (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
349 " which is not less than " +
350 TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds) + " (minimumExpireTime)",
351 listener.attributes.getExpirationTime() <= System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(minimumExpiryTimeSeconds)
352 );
353 }
354
355 /**
356 * Check if Cache-Control takes precedence over max-age
357 * Expires is lower - JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10
358 * Cache control : JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2
359 *
360 * Both are smaller than DEFAULT_EXPIRE_TIME, so we can test, that it's not DEFAULT_EXPIRE_TIME that extended
361 * expiration
362 *
363 * @throws IOException exception
364 */
365
366 @Test
367 public void testCacheControlVsExpires() throws IOException {
368 long testStart = System.currentTimeMillis();
369 int minimumExpiryTimeSeconds = 0;
370
371 tileServer.stubFor(get(urlEqualTo("/test"))
372 .willReturn(aResponse()
373 .withHeader("Expires", TestUtils.getHTTPDate(testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10)))
374 .withHeader("Cache-Control", "max-age=" +
375 TimeUnit.MILLISECONDS.toSeconds((JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2)))
376 .withBody("mock entry")
377 )
378 );
379 tileServer.stubFor(head(urlEqualTo("/test"))
380 .willReturn(aResponse()
381 .withHeader("Expires", TestUtils.getHTTPDate(testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10)))
382 .withHeader("Cache-Control", "max-age=" +
383 TimeUnit.MILLISECONDS.toSeconds((JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2)))
384 )
385 );
386 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test", minimumExpiryTimeSeconds);
387 Listener listener = submitJob(job, false);
388 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
389 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
390
391
392 assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
393 (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10) + " (Expires header)",
394 listener.attributes.getExpirationTime() >= testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10));
395
396 assertTrue("Cache entry expiration is " +
397 (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
398 " which is not less than " +
399 (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2) + " (Cache-Control: max-age=)",
400 listener.attributes.getExpirationTime() <= System.currentTimeMillis() + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2)
401 );
402 }
403
404 /**
405 * Check if Cache-Control s-max-age is honored
406 * mock returns expiration: JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10
407 * minimum expire time: JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME * 2
408 *
409 * @throws IOException exception
410 */
411 @Test
412 public void testMaxAgeVsSMaxAge() throws IOException {
413 long testStart = System.currentTimeMillis();
414 int minimumExpiryTimeSeconds = 0;
415
416 tileServer.stubFor(get(urlEqualTo("/test"))
417 .willReturn(aResponse()
418 .withHeader("Cache-Control", "" +
419 "max-age=" + TimeUnit.MILLISECONDS.toSeconds((JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10)) + "," +
420 "s-max-age=" + TimeUnit.MILLISECONDS.toSeconds((JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2))
421 )
422 .withBody("mock entry")
423 )
424 );
425 tileServer.stubFor(head(urlEqualTo("/test"))
426 .willReturn(aResponse()
427 .withHeader("Cache-Control", "" +
428 "max-age=" + TimeUnit.MILLISECONDS.toSeconds((JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10)) + "," +
429 "s-max-age=" + TimeUnit.MILLISECONDS.toSeconds((JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2))
430 )
431 ));
432 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test", minimumExpiryTimeSeconds);
433 Listener listener = submitJob(job, false);
434 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
435 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
436
437 assertTrue("Cache entry expiration is " + (listener.attributes.getExpirationTime() - testStart) + " which is not larger than " +
438 (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10) + " (Cache-Control: max-age)",
439 listener.attributes.getExpirationTime() >= testStart + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 10));
440
441 assertTrue("Cache entry expiration is " +
442 (listener.attributes.getExpirationTime() - System.currentTimeMillis()) +
443 " which is not less than " +
444 (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2) + " (Cache-Control: s-max-age)",
445 listener.attributes.getExpirationTime() <= System.currentTimeMillis() + (JCSCachedTileLoaderJob.DEFAULT_EXPIRE_TIME / 2)
446 );
447 }
448
449 /**
450 * Check if verifying cache entries using HEAD requests work properly
451 * @throws IOException exception
452 */
453 @Test
454 public void testCheckUsingHead() throws IOException {
455 ICacheAccess<String, CacheEntry> cache = getCache();
456 long expires = TimeUnit.DAYS.toMillis(1);
457 long testStart = System.currentTimeMillis();
458 cache.put("test",
459 new CacheEntry("cached dummy".getBytes(StandardCharsets.UTF_8)),
460 createEntryAttributes(-1 * expires, 200, testStart, "eTag--gzip") // Jetty adds --gzip to etags when compressing output
461 );
462
463 tileServer.stubFor(get(urlEqualTo("/test"))
464 .willReturn(aResponse()
465 .withHeader("Expires", TestUtils.getHTTPDate(testStart + expires))
466 .withHeader("Last-Modified", Long.toString(testStart))
467 .withHeader("ETag", "eTag") // Jetty adds "--gzip" suffix for compressed content
468 .withBody("mock entry")
469 )
470 );
471 tileServer.stubFor(head(urlEqualTo("/test"))
472 .willReturn(aResponse()
473 .withHeader("Expires", TestUtils.getHTTPDate(testStart + expires))
474 .withHeader("Last-Modified", Long.toString(testStart))
475 .withHeader("ETag", "eTag--gzip") // but doesn't add to uncompressed
476 )
477 );
478
479 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test");
480 Listener listener = submitJob(job, false); // cache entry is expired, no need to force refetch
481 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
482 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
483
484 // cache entry should be retrieved from cache
485 listener = submitJob(job, false);
486 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
487 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
488
489 // invalidate entry in cache
490 ICacheElement<String, CacheEntry> cacheEntry = cache.getCacheElement("test");
491 CacheEntryAttributes attributes = (CacheEntryAttributes) cacheEntry.getElementAttributes();
492 attributes.setExpirationTime(testStart - TimeUnit.DAYS.toMillis(1));
493 cache.put("test", cacheEntry.getVal(), attributes);
494
495 // because cache entry is invalid - HEAD request shall be made
496 tileServer.verify(0, headRequestedFor(urlEqualTo("/test"))); // no head requests were made until now
497 listener = submitJob(job, false);
498 tileServer.verify(1, headRequestedFor(urlEqualTo("/test"))); // verify head requests were made
499 tileServer.verify(1, getRequestedFor(urlEqualTo("/test"))); // verify no more get requests were made
500 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
501 assertTrue(listener.attributes.getExpirationTime() >= testStart + expires);
502
503 // cache entry should be retrieved from cache
504 listener = submitJob(job, false); // cache entry is expired, no need to force refetch
505 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
506 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
507 assertArrayEquals("mock entry".getBytes(StandardCharsets.UTF_8), listener.data);
508 }
509
510 /**
511 * Check if server returns 304 - it will update cache attributes and not ask again for it
512 * @throws IOException exception
513 */
514 @Test
515 public void testCheckUsing304() throws IOException {
516 ICacheAccess<String, CacheEntry> cache = getCache();
517 long expires = TimeUnit.DAYS.toMillis(1);
518 long testStart = System.currentTimeMillis();
519 cache.put("test",
520 new CacheEntry("cached dummy".getBytes(StandardCharsets.UTF_8)),
521 createEntryAttributes(-1 * expires, 200, testStart, "eTag")
522 );
523
524 tileServer.stubFor(get(urlEqualTo("/test"))
525 .willReturn(status(304)
526 .withHeader("Expires", TestUtils.getHTTPDate(testStart + expires))
527 .withHeader("Last-Modified", Long.toString(testStart))
528 .withHeader("ETag", "eTag")
529 )
530 );
531
532 TestCachedTileLoaderJob job = new TestCachedTileLoaderJob(tileServer.url("/test"), "test");
533 Listener listener = submitJob(job, false);
534 tileServer.verify(1, getRequestedFor(urlEqualTo("/test")));
535 assertArrayEquals("cached dummy".getBytes(StandardCharsets.UTF_8), listener.data);
536 assertTrue(testStart + expires <= listener.attributes.getExpirationTime());
537 submitJob(job, false);
538 tileServer.verify(1, getRequestedFor(urlEqualTo("/test"))); // no more requests were made
539 }
540
541 private void createHeadGetStub(UrlPattern url, long expires, long lastModified, String eTag, String body) {
542 tileServer.stubFor(get(url)
543 .willReturn(aResponse()
544 .withHeader("Expires", TestUtils.getHTTPDate(lastModified + expires))
545 .withHeader("Last-Modified", Long.toString(lastModified))
546 .withHeader("ETag", eTag)
547 .withBody(body)
548 )
549 );
550 tileServer.stubFor(head(url)
551 .willReturn(aResponse()
552 .withHeader("Expires", TestUtils.getHTTPDate(lastModified + expires))
553 .withHeader("Last-Modified", Long.toString(lastModified))
554 .withHeader("ETag", eTag)
555 )
556 );
557 }
558
559 private CacheEntryAttributes createEntryAttributes(long expirationTime, int responseCode, long lastModification, String eTag) {
560 CacheEntryAttributes entryAttributes = new CacheEntryAttributes();
561 entryAttributes.setExpirationTime(lastModification + expirationTime);
562 entryAttributes.setResponseCode(responseCode);
563 entryAttributes.setLastModification(lastModification);
564 entryAttributes.setEtag(eTag);
565 return entryAttributes;
566 }
567
568 private TestCachedTileLoaderJob getStatusLoaderJob(int responseCode) {
569 return new TestCachedTileLoaderJob(tileServer.url("/httpstat/" + responseCode), "key_" + responseCode);
570 }
571
572 private static ICacheAccess<String, CacheEntry> getCache() {
573 return JCSCacheManager.getCache("test");
574 }
575}
Note: See TracBrowser for help on using the repository browser.