View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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.assertNotEquals;
31  import static org.junit.jupiter.api.Assertions.assertNotNull;
32  import static org.junit.jupiter.api.Assertions.assertNull;
33  import static org.junit.jupiter.api.Assertions.assertSame;
34  import static org.junit.jupiter.api.Assertions.assertTrue;
35  import static org.junit.jupiter.api.Assertions.fail;
36  import static org.mockito.Mockito.mock;
37  
38  import java.time.Instant;
39  import java.time.temporal.ChronoField;
40  import java.util.Date;
41  import java.util.HashMap;
42  import java.util.Map;
43  
44  import org.apache.hc.client5.http.utils.DateUtils;
45  import org.apache.hc.core5.http.Header;
46  import org.apache.hc.core5.http.HttpStatus;
47  import org.apache.hc.core5.http.message.BasicHeader;
48  import org.junit.jupiter.api.BeforeEach;
49  import org.junit.jupiter.api.Test;
50  
51  public class TestHttpCacheEntry {
52  
53      private Instant now;
54      private Instant elevenSecondsAgo;
55      private Instant nineSecondsAgo;
56      private Resource mockResource;
57      private HttpCacheEntry entry;
58  
59      @BeforeEach
60      public void setUp() {
61          now = Instant.now();
62          elevenSecondsAgo = now.minusSeconds(11);
63          nineSecondsAgo = now.minusSeconds(9);
64          mockResource = mock(Resource.class);
65      }
66  
67      private HttpCacheEntry makeEntry(final Header[] headers) {
68          return new HttpCacheEntry(elevenSecondsAgo, nineSecondsAgo,
69                  HttpStatus.SC_OK, headers, mockResource);
70      }
71  
72      @Test
73      public void testGetHeadersReturnsCorrectHeaders() {
74          final Header[] headers = { new BasicHeader("foo", "fooValue"),
75                  new BasicHeader("bar", "barValue1"),
76                  new BasicHeader("bar", "barValue2")
77          };
78          entry = makeEntry(headers);
79          assertEquals(2, entry.getHeaders("bar").length);
80      }
81  
82      @Test
83      public void testGetFirstHeaderReturnsCorrectHeader() {
84          final Header[] headers = { new BasicHeader("foo", "fooValue"),
85                  new BasicHeader("bar", "barValue1"),
86                  new BasicHeader("bar", "barValue2")
87          };
88          entry = makeEntry(headers);
89          assertEquals("barValue1", entry.getFirstHeader("bar").getValue());
90      }
91  
92      @Test
93      public void testGetHeadersReturnsEmptyArrayIfNoneMatch() {
94          final Header[] headers = { new BasicHeader("foo", "fooValue"),
95                  new BasicHeader("bar", "barValue1"),
96                  new BasicHeader("bar", "barValue2")
97          };
98          entry = makeEntry(headers);
99          assertEquals(0, entry.getHeaders("baz").length);
100     }
101 
102     @Test
103     public void testGetFirstHeaderReturnsNullIfNoneMatch() {
104         final Header[] headers = { new BasicHeader("foo", "fooValue"),
105                 new BasicHeader("bar", "barValue1"),
106                 new BasicHeader("bar", "barValue2")
107         };
108         entry = makeEntry(headers);
109         assertNull(entry.getFirstHeader("quux"));
110     }
111 
112     @Test
113     public void testCacheEntryWithOneVaryHeaderHasVariants() {
114         final Header[] headers = { new BasicHeader("Vary", "User-Agent") };
115         entry = makeEntry(headers);
116         assertTrue(entry.hasVariants());
117     }
118 
119     @Test
120     public void testCacheEntryWithMultipleVaryHeadersHasVariants() {
121         final Header[] headers = { new BasicHeader("Vary", "User-Agent"),
122                 new BasicHeader("Vary", "Accept-Encoding")
123         };
124         entry = makeEntry(headers);
125         assertTrue(entry.hasVariants());
126     }
127 
128     @Test
129     public void testCacheEntryWithVaryStarHasVariants(){
130         final Header[] headers = { new BasicHeader("Vary", "*") };
131         entry = makeEntry(headers);
132         assertTrue(entry.hasVariants());
133     }
134 
135 
136     @Test
137     public void testGetMethodReturnsCorrectRequestMethod() {
138         final Header[] headers = { new BasicHeader("foo", "fooValue"),
139                 new BasicHeader("bar", "barValue1"),
140                 new BasicHeader("bar", "barValue2")
141         };
142         entry = makeEntry(headers);
143         assertEquals(HeaderConstants.GET_METHOD, entry.getRequestMethod());
144     }
145 
146 
147 
148     @SuppressWarnings("unused")
149     @Test
150     public void mustProvideRequestDate() {
151         try {
152             new HttpCacheEntry(null, Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource);
153             fail("Should have thrown exception");
154         } catch (final NullPointerException expected) {
155         }
156     }
157 
158     @SuppressWarnings("unused")
159     @Test
160     public void mustProvideResponseDate() {
161         try {
162             new HttpCacheEntry(Instant.now(), null, HttpStatus.SC_OK, new Header[]{}, mockResource);
163             fail("Should have thrown exception");
164         } catch (final NullPointerException expected) {
165         }
166     }
167 
168     @SuppressWarnings("unused")
169     @Test
170     public void mustProvideResponseHeaders() {
171         try {
172             new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, null, mockResource);
173             fail("Should have thrown exception");
174         } catch (final NullPointerException expected) {
175         }
176     }
177 
178     @Test
179     public void statusCodeComesFromOriginalStatusLine() {
180         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource);
181         assertEquals(HttpStatus.SC_OK, entry.getStatus());
182     }
183 
184     @Test
185     public void canGetOriginalRequestDate() {
186         final Instant requestDate = Instant.now();
187         entry = new HttpCacheEntry(requestDate, Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource);
188         assertEquals(requestDate, entry.getRequestInstant());
189     }
190 
191     @Test
192     public void canGetOriginalResponseDate() {
193         final Instant responseDate = Instant.now();
194         entry = new HttpCacheEntry(Instant.now(), responseDate, HttpStatus.SC_OK, new Header[]{}, mockResource);
195         assertEquals(responseDate, entry.getResponseInstant());
196     }
197 
198     @Test
199     public void canGetOriginalResource() {
200         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource);
201         assertSame(mockResource, entry.getResource());
202     }
203 
204     @Test
205     public void canGetOriginalHeaders() {
206         final Header[] headers = {
207                 new BasicHeader("Server", "MockServer/1.0"),
208                 new BasicHeader("Date", DateUtils.formatStandardDate(now))
209         };
210         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource);
211         final Header[] result = entry.getHeaders();
212         assertEquals(headers.length, result.length);
213         for(int i=0; i<headers.length; i++) {
214             assertEquals(headers[i], result[i]);
215         }
216     }
217 
218     @SuppressWarnings("unused")
219     @Test
220     public void canConstructWithoutVariants() {
221         new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource);
222     }
223 
224     @SuppressWarnings("unused")
225     @Test
226     public void canProvideVariantMap() {
227         new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
228                 new Header[]{}, mockResource,
229                 new HashMap<>());
230     }
231 
232     @Test
233     public void canRetrieveOriginalVariantMap() {
234         final Map<String,String> variantMap = new HashMap<>();
235         variantMap.put("A","B");
236         variantMap.put("C","D");
237         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
238                 new Header[]{}, mockResource,
239                 variantMap);
240         final Map<String,String> result = entry.getVariantMap();
241         assertEquals(2, result.size());
242         assertEquals("B", result.get("A"));
243         assertEquals("D", result.get("C"));
244     }
245 
246     @Test
247     public void retrievedVariantMapIsNotModifiable() {
248         final Map<String,String> variantMap = new HashMap<>();
249         variantMap.put("A","B");
250         variantMap.put("C","D");
251         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
252                 new Header[]{}, mockResource,
253                 variantMap);
254         final Map<String,String> result = entry.getVariantMap();
255         try {
256             result.remove("A");
257             fail("Should have thrown exception");
258         } catch (final UnsupportedOperationException expected) {
259         }
260         try {
261             result.put("E","F");
262             fail("Should have thrown exception");
263         } catch (final UnsupportedOperationException expected) {
264         }
265     }
266 
267     @Test
268     public void canConvertToString() {
269         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
270                 new Header[]{}, mockResource);
271         assertNotNull(entry.toString());
272         assertNotEquals("", entry.toString());
273     }
274 
275     @Test
276     public void testMissingDateHeaderIsIgnored() {
277         final Header[] headers = new Header[] {};
278         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
279                 headers, mockResource);
280         assertNull(entry.getDate());
281     }
282 
283     @Test
284     public void testMalformedDateHeaderIsIgnored() {
285         final Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
286         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
287                 headers, mockResource);
288         assertNull(entry.getDate());
289     }
290 
291     @Test
292     public void testValidDateHeaderIsParsed() {
293         final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
294         final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(date)) };
295         entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
296                 headers, mockResource);
297         final Date dateHeaderValue = entry.getDate();
298         assertNotNull(dateHeaderValue);
299         assertEquals(DateUtils.toDate(date), dateHeaderValue);
300     }
301 
302 }