4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\HttpKernel\Tests\HttpCache;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\HttpFoundation\Request;
16 use Symfony\Component\HttpFoundation\Response;
17 use Symfony\Component\HttpKernel\HttpCache\Store;
19 class StoreTest extends TestCase
29 protected function setUp()
31 $this->request = Request::create('/');
32 $this->response = new Response('hello world', 200, array());
34 HttpCacheTestCase::clearDirectory(sys_get_temp_dir().'/http_cache');
36 $this->store = new Store(sys_get_temp_dir().'/http_cache');
39 protected function tearDown()
42 $this->request = null;
43 $this->response = null;
45 HttpCacheTestCase::clearDirectory(sys_get_temp_dir().'/http_cache');
48 public function testReadsAnEmptyArrayWithReadWhenNothingCachedAtKey()
50 $this->assertEmpty($this->getStoreMetadata('/nothing'));
53 public function testUnlockFileThatDoesExist()
55 $cacheKey = $this->storeSimpleEntry();
56 $this->store->lock($this->request);
58 $this->assertTrue($this->store->unlock($this->request));
61 public function testUnlockFileThatDoesNotExist()
63 $this->assertFalse($this->store->unlock($this->request));
66 public function testRemovesEntriesForKeyWithPurge()
68 $request = Request::create('/foo');
69 $this->store->write($request, new Response('foo'));
71 $metadata = $this->getStoreMetadata($request);
72 $this->assertNotEmpty($metadata);
74 $this->assertTrue($this->store->purge('/foo'));
75 $this->assertEmpty($this->getStoreMetadata($request));
77 // cached content should be kept after purging
78 $path = $this->store->getPath($metadata[0][1]['x-content-digest'][0]);
79 $this->assertTrue(is_file($path));
81 $this->assertFalse($this->store->purge('/bar'));
84 public function testStoresACacheEntry()
86 $cacheKey = $this->storeSimpleEntry();
88 $this->assertNotEmpty($this->getStoreMetadata($cacheKey));
91 public function testSetsTheXContentDigestResponseHeaderBeforeStoring()
93 $cacheKey = $this->storeSimpleEntry();
94 $entries = $this->getStoreMetadata($cacheKey);
95 list($req, $res) = $entries[0];
97 $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $res['x-content-digest'][0]);
100 public function testFindsAStoredEntryWithLookup()
102 $this->storeSimpleEntry();
103 $response = $this->store->lookup($this->request);
105 $this->assertNotNull($response);
106 $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
109 public function testDoesNotFindAnEntryWithLookupWhenNoneExists()
111 $request = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
113 $this->assertNull($this->store->lookup($request));
116 public function testCanonizesUrlsForCacheKeys()
118 $this->storeSimpleEntry($path = '/test?x=y&p=q');
119 $hitsReq = Request::create($path);
120 $missReq = Request::create('/test?p=x');
122 $this->assertNotNull($this->store->lookup($hitsReq));
123 $this->assertNull($this->store->lookup($missReq));
126 public function testDoesNotFindAnEntryWithLookupWhenTheBodyDoesNotExist()
128 $this->storeSimpleEntry();
129 $this->assertNotNull($this->response->headers->get('X-Content-Digest'));
130 $path = $this->getStorePath($this->response->headers->get('X-Content-Digest'));
132 $this->assertNull($this->store->lookup($this->request));
135 public function testRestoresResponseHeadersProperlyWithLookup()
137 $this->storeSimpleEntry();
138 $response = $this->store->lookup($this->request);
140 $this->assertEquals($response->headers->all(), array_merge(array('content-length' => 4, 'x-body-file' => array($this->getStorePath($response->headers->get('X-Content-Digest')))), $this->response->headers->all()));
143 public function testRestoresResponseContentFromEntityStoreWithLookup()
145 $this->storeSimpleEntry();
146 $response = $this->store->lookup($this->request);
147 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test')), $response->getContent());
150 public function testInvalidatesMetaAndEntityStoreEntriesWithInvalidate()
152 $this->storeSimpleEntry();
153 $this->store->invalidate($this->request);
154 $response = $this->store->lookup($this->request);
155 $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
156 $this->assertFalse($response->isFresh());
159 public function testSucceedsQuietlyWhenInvalidateCalledWithNoMatchingEntries()
161 $req = Request::create('/test');
162 $this->store->invalidate($req);
163 $this->assertNull($this->store->lookup($this->request));
166 public function testDoesNotReturnEntriesThatVaryWithLookup()
168 $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
169 $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'));
170 $res = new Response('test', 200, array('Vary' => 'Foo Bar'));
171 $this->store->write($req1, $res);
173 $this->assertNull($this->store->lookup($req2));
176 public function testDoesNotReturnEntriesThatSlightlyVaryWithLookup()
178 $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
179 $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bam'));
180 $res = new Response('test', 200, array('Vary' => array('Foo', 'Bar')));
181 $this->store->write($req1, $res);
183 $this->assertNull($this->store->lookup($req2));
186 public function testStoresMultipleResponsesForEachVaryCombination()
188 $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
189 $res1 = new Response('test 1', 200, array('Vary' => 'Foo Bar'));
190 $key = $this->store->write($req1, $res1);
192 $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'));
193 $res2 = new Response('test 2', 200, array('Vary' => 'Foo Bar'));
194 $this->store->write($req2, $res2);
196 $req3 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom'));
197 $res3 = new Response('test 3', 200, array('Vary' => 'Foo Bar'));
198 $this->store->write($req3, $res3);
200 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 3')), $this->store->lookup($req3)->getContent());
201 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 2')), $this->store->lookup($req2)->getContent());
202 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent());
204 $this->assertCount(3, $this->getStoreMetadata($key));
207 public function testOverwritesNonVaryingResponseWithStore()
209 $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
210 $res1 = new Response('test 1', 200, array('Vary' => 'Foo Bar'));
211 $key = $this->store->write($req1, $res1);
212 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent());
214 $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'));
215 $res2 = new Response('test 2', 200, array('Vary' => 'Foo Bar'));
216 $this->store->write($req2, $res2);
217 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 2')), $this->store->lookup($req2)->getContent());
219 $req3 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
220 $res3 = new Response('test 3', 200, array('Vary' => 'Foo Bar'));
221 $key = $this->store->write($req3, $res3);
222 $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 3')), $this->store->lookup($req3)->getContent());
224 $this->assertCount(2, $this->getStoreMetadata($key));
227 public function testLocking()
229 $req = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
230 $this->assertTrue($this->store->lock($req));
232 $path = $this->store->lock($req);
233 $this->assertTrue($this->store->isLocked($req));
235 $this->store->unlock($req);
236 $this->assertFalse($this->store->isLocked($req));
239 public function testPurgeHttps()
241 $request = Request::create('https://example.com/foo');
242 $this->store->write($request, new Response('foo'));
244 $this->assertNotEmpty($this->getStoreMetadata($request));
246 $this->assertTrue($this->store->purge('https://example.com/foo'));
247 $this->assertEmpty($this->getStoreMetadata($request));
250 public function testPurgeHttpAndHttps()
252 $requestHttp = Request::create('https://example.com/foo');
253 $this->store->write($requestHttp, new Response('foo'));
255 $requestHttps = Request::create('http://example.com/foo');
256 $this->store->write($requestHttps, new Response('foo'));
258 $this->assertNotEmpty($this->getStoreMetadata($requestHttp));
259 $this->assertNotEmpty($this->getStoreMetadata($requestHttps));
261 $this->assertTrue($this->store->purge('http://example.com/foo'));
262 $this->assertEmpty($this->getStoreMetadata($requestHttp));
263 $this->assertEmpty($this->getStoreMetadata($requestHttps));
266 protected function storeSimpleEntry($path = null, $headers = array())
268 if (null === $path) {
272 $this->request = Request::create($path, 'get', array(), array(), array(), $headers);
273 $this->response = new Response('test', 200, array('Cache-Control' => 'max-age=420'));
275 return $this->store->write($this->request, $this->response);
278 protected function getStoreMetadata($key)
280 $r = new \ReflectionObject($this->store);
281 $m = $r->getMethod('getMetadata');
282 $m->setAccessible(true);
284 if ($key instanceof Request) {
285 $m1 = $r->getMethod('getCacheKey');
286 $m1->setAccessible(true);
287 $key = $m1->invoke($this->store, $key);
290 return $m->invoke($this->store, $key);
293 protected function getStorePath($key)
295 $r = new \ReflectionObject($this->store);
296 $m = $r->getMethod('getPath');
297 $m->setAccessible(true);
299 return $m->invoke($this->store, $key);