1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.cache;
28
29 import static org.junit.jupiter.api.Assertions.assertEquals;
30 import static org.junit.jupiter.api.Assertions.assertFalse;
31 import static org.junit.jupiter.api.Assertions.assertNotEquals;
32 import static org.junit.jupiter.api.Assertions.assertNotNull;
33 import static org.junit.jupiter.api.Assertions.assertNull;
34 import static org.junit.jupiter.api.Assertions.assertSame;
35 import static org.junit.jupiter.api.Assertions.assertTrue;
36 import static org.mockito.Mockito.mock;
37
38 import java.time.Instant;
39 import java.time.LocalDate;
40 import java.time.Month;
41 import java.time.ZoneId;
42 import java.time.temporal.ChronoField;
43 import java.util.Collection;
44 import java.util.HashSet;
45 import java.util.Set;
46
47 import org.apache.hc.client5.http.impl.cache.HttpTestUtils;
48 import org.apache.hc.client5.http.utils.DateUtils;
49 import org.apache.hc.core5.http.Header;
50 import org.apache.hc.core5.http.HttpHeaders;
51 import org.apache.hc.core5.http.HttpStatus;
52 import org.apache.hc.core5.http.Method;
53 import org.apache.hc.core5.http.message.BasicHeader;
54 import org.apache.hc.core5.http.message.HeaderGroup;
55 import org.junit.jupiter.api.Assertions;
56 import org.junit.jupiter.api.BeforeEach;
57 import org.junit.jupiter.api.Test;
58
59 class TestHttpCacheEntry {
60
61 private Instant now;
62 private Instant elevenSecondsAgo;
63 private Instant nineSecondsAgo;
64 private Resource mockResource;
65 private HttpCacheEntry entry;
66
67 @BeforeEach
68 void setUp() {
69 now = Instant.now();
70 elevenSecondsAgo = now.minusSeconds(11);
71 nineSecondsAgo = now.minusSeconds(9);
72 mockResource = mock(Resource.class);
73 }
74
75 private HttpCacheEntry makeEntry(final Header... headers) {
76 return new HttpCacheEntry(elevenSecondsAgo, nineSecondsAgo,
77 "GET", "/", HttpTestUtils.headers(),
78 HttpStatus.SC_OK, HttpTestUtils.headers(headers), mockResource, null);
79 }
80
81 private HttpCacheEntry makeEntry(final Instant requestDate,
82 final Instant responseDate,
83 final int status,
84 final Header[] headers,
85 final Resource resource,
86 final Collection<String> variants) {
87 return new HttpCacheEntry(requestDate, responseDate,
88 "GET", "/", HttpTestUtils.headers(),
89 status, HttpTestUtils.headers(headers), resource, variants);
90 }
91
92 private HttpCacheEntry makeEntry(final Instant requestDate,
93 final Instant responseDate,
94 final int status,
95 final Header... headers) {
96 return new HttpCacheEntry(requestDate, responseDate,
97 "GET", "/", HttpTestUtils.headers(),
98 status, HttpTestUtils.headers(headers), mockResource, null);
99 }
100
101 @Test
102 void testGetHeadersReturnsCorrectHeaders() {
103 entry = makeEntry(
104 new BasicHeader("bar", "barValue1"),
105 new BasicHeader("bar", "barValue2"));
106 assertEquals(2, entry.getHeaders("bar").length);
107 }
108
109 @Test
110 void testGetFirstHeaderReturnsCorrectHeader() {
111 entry = makeEntry(
112 new BasicHeader("bar", "barValue1"),
113 new BasicHeader("bar", "barValue2"));
114 assertEquals("barValue1", entry.getFirstHeader("bar").getValue());
115 }
116
117 @Test
118 void testGetHeadersReturnsEmptyArrayIfNoneMatch() {
119 entry = makeEntry(
120 new BasicHeader("foo", "fooValue"),
121 new BasicHeader("bar", "barValue1"),
122 new BasicHeader("bar", "barValue2"));
123 assertEquals(0, entry.getHeaders("baz").length);
124 }
125
126 @Test
127 void testGetFirstHeaderReturnsNullIfNoneMatch() {
128 entry = makeEntry(
129 new BasicHeader("foo", "fooValue"),
130 new BasicHeader("bar", "barValue1"),
131 new BasicHeader("bar", "barValue2"));
132 assertNull(entry.getFirstHeader("quux"));
133 }
134
135 @Test
136 void testGetMethodReturnsCorrectRequestMethod() {
137 entry = makeEntry(
138 new BasicHeader("foo", "fooValue"),
139 new BasicHeader("bar", "barValue1"),
140 new BasicHeader("bar", "barValue2"));
141 assertEquals(Method.GET.name(), entry.getRequestMethod());
142 }
143
144 @Test
145 void statusCodeComesFromOriginalStatusLine() {
146 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
147 assertEquals(HttpStatus.SC_OK, entry.getStatus());
148 }
149
150 @Test
151 void canGetOriginalRequestDate() {
152 final Instant requestDate = Instant.now();
153 entry = makeEntry(requestDate, Instant.now(), HttpStatus.SC_OK);
154 assertEquals(requestDate, entry.getRequestInstant());
155 }
156
157 @Test
158 void canGetOriginalResponseDate() {
159 final Instant responseDate = Instant.now();
160 entry = makeEntry(Instant.now(), responseDate, HttpStatus.SC_OK);
161 assertEquals(responseDate, entry.getResponseInstant());
162 }
163
164 @Test
165 void canGetOriginalResource() {
166 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
167 assertSame(mockResource, entry.getResource());
168 }
169
170 @Test
171 void canGetOriginalHeaders() {
172 final Header[] headers = {
173 new BasicHeader("Server", "MockServer/1.0"),
174 new BasicHeader("Date", DateUtils.formatStandardDate(now))
175 };
176 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource, null);
177 final Header[] result = entry.getHeaders();
178 assertEquals(headers.length, result.length);
179 for(int i=0; i<headers.length; i++) {
180 assertEquals(headers[i], result[i]);
181 }
182 }
183
184 @Test
185 void canRetrieveOriginalVariantMap() {
186 final Set<String> variants = new HashSet<>();
187 variants.add("A");
188 variants.add("B");
189 variants.add("C");
190 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
191 new Header[]{}, mockResource,
192 variants);
193 final Set<String> result = entry.getVariants();
194 assertEquals(3, result.size());
195 assertTrue(result.contains("A"));
196 assertTrue(result.contains("B"));
197 assertTrue(result.contains("C"));
198 assertFalse(result.contains("D"));
199 }
200
201 @Test
202 void retrievedVariantMapIsNotModifiable() {
203 final Set<String> variants = new HashSet<>();
204 variants.add("A");
205 variants.add("B");
206 variants.add("C");
207 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
208 new Header[]{}, mockResource,
209 variants);
210 final Set<String> result = entry.getVariants();
211 Assertions.assertThrows(UnsupportedOperationException.class, () -> result.remove("A"));
212 Assertions.assertThrows(UnsupportedOperationException.class, () -> result.add("D"));
213 }
214
215 @Test
216 void canConvertToString() {
217 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
218 assertNotNull(entry.toString());
219 assertNotEquals("", entry.toString());
220 }
221
222 @Test
223 void testMissingDateHeaderIsIgnored() {
224 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
225 assertNull(entry.getInstant());
226 }
227
228 @Test
229 void testMalformedDateHeaderIsIgnored() {
230 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
231 new BasicHeader("Date", "asdf"));
232 assertNull(entry.getInstant());
233 }
234
235 @Test
236 void testValidDateHeaderIsParsed() {
237 final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
238 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
239 new BasicHeader("Date", DateUtils.formatStandardDate(date)));
240 final Instant dateHeaderValue = entry.getInstant();
241 assertNotNull(dateHeaderValue);
242 assertEquals(date, dateHeaderValue);
243 }
244
245 @Test
246 void testEpochDateHeaderIsParsed() {
247 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
248 new BasicHeader("Date", DateUtils.formatStandardDate(Instant.EPOCH)));
249 final Instant dateHeaderValue = entry.getInstant();
250 assertNotNull(dateHeaderValue);
251 assertEquals(Instant.EPOCH, dateHeaderValue);
252 }
253
254 @Test
255 void testDateParsedOnce() {
256 final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
257 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
258 new BasicHeader("Date", DateUtils.formatStandardDate(date)));
259 final Instant dateHeaderValue = entry.getInstant();
260 assertNotNull(dateHeaderValue);
261 assertSame(dateHeaderValue, entry.getInstant());
262 assertSame(dateHeaderValue, entry.getInstant());
263 }
264
265 @Test
266 void testExpiresParsedOnce() {
267 final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
268 entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
269 new BasicHeader("Last-Modified", DateUtils.formatStandardDate(date)));
270 final Instant lastModifiedHeaderValue = entry.getLastModified();
271 assertNotNull(lastModifiedHeaderValue);
272 assertSame(lastModifiedHeaderValue, entry.getLastModified());
273 assertSame(lastModifiedHeaderValue, entry.getLastModified());
274 }
275
276 private static Instant createInstant(final int year, final Month month, final int day) {
277 return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant();
278 }
279
280 @Test
281 void testIsCacheEntryNewer() {
282 assertFalse(HttpCacheEntry.isNewer(null, null));
283 entry = makeEntry();
284 final HeaderGroup message = new HeaderGroup();
285 assertFalse(HttpCacheEntry.isNewer(entry, message));
286
287 entry = makeEntry(new BasicHeader(HttpHeaders.DATE, "huh?"));
288 message.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
289 assertFalse(HttpCacheEntry.isNewer(entry, message));
290
291 entry = makeEntry(new BasicHeader(HttpHeaders.DATE, "huh?"));
292 message.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
293 assertFalse(HttpCacheEntry.isNewer(entry, message));
294
295 entry = makeEntry(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27))));
296 message.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
297 assertTrue(HttpCacheEntry.isNewer(entry, message));
298
299 entry = makeEntry(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25))));
300 message.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
301 assertFalse(HttpCacheEntry.isNewer(entry, message));
302 }
303
304 }