Skip to content

Commit df54ddc

Browse files
committed
add tests for the symfony proxy client
1 parent 40b24bd commit df54ddc

File tree

7 files changed

+344
-1
lines changed

7 files changed

+344
-1
lines changed

src/ProxyClient/Symfony.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ public function __construct(array $servers, $baseUrl = null, ClientInterface $cl
5151
parent::__construct($servers, $baseUrl, $client);
5252

5353
$resolver = new OptionsResolver();
54-
$resolver->setDefined(array('purge_method'));
5554
$resolver->setDefaults(array(
5655
'purge_method' => PurgeSubscriber::DEFAULT_PURGE_METHOD,
5756
));

src/Test/Proxy/SymfonyProxy.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\HttpCache\Test\Proxy;
13+
14+
/**
15+
* Controls the Symfony HttpCache proxy server.
16+
*
17+
* SYMFONY_CACHE_DIR directory to use for cache
18+
* (default sys_get_temp_dir() + '/foshttpcache-symfony')
19+
*/
20+
class SymfonyProxy implements ProxyInterface
21+
{
22+
/**
23+
* Get Symfony cache directory
24+
*
25+
* @return string
26+
*/
27+
public function getCacheDir()
28+
{
29+
return defined('SYMFONY_CACHE_DIR') ? SYMFONY_CACHE_DIR : sys_get_temp_dir() . '/foshttpcache-symfony';
30+
}
31+
32+
/**
33+
* Start the proxy server
34+
*/
35+
public function start()
36+
{
37+
$this->clear();
38+
}
39+
40+
/**
41+
* Stop the proxy server
42+
*/
43+
public function stop()
44+
{
45+
// nothing to do
46+
}
47+
48+
/**
49+
* Clear all cached content from the proxy server
50+
*/
51+
public function clear()
52+
{
53+
if (is_dir($this->getCacheDir())) {
54+
$path = realpath($this->getCacheDir());
55+
if (!$this->getCacheDir() || '/' == $path) {
56+
throw new \Exception('Invalid test setup, the cache dir is ' . $path);
57+
}
58+
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
59+
system('DEL /S '.$path);
60+
} else {
61+
system('rm -r '.$path);
62+
}
63+
}
64+
}
65+
}

src/Test/SymfonyTestCase.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\HttpCache\Test;
13+
14+
use FOS\HttpCache\ProxyClient\Symfony;
15+
use FOS\HttpCache\Test\Proxy\SymfonyProxy;
16+
17+
/**
18+
* A phpunit base class to write functional tests with the symfony HttpCache.
19+
*
20+
* The webserver with symfony is to be started with the WebServerListener.
21+
*
22+
* You can define constants in your phpunit to control how this test behaves.
23+
*
24+
* To define constants in the phpunit file, use this syntax:
25+
* <php>
26+
* <const name="WEB_SERVER_PORT" value="/tmp/foo" />
27+
* </php>
28+
*
29+
* WEB_SERVER_PORT port the PHP webserver listens on (default 8080)
30+
*
31+
* Note that the SymfonyProxy also defines a SYMFONY_CACHE_DIR constant.
32+
*/
33+
abstract class SymfonyTestCase extends ProxyTestCase
34+
{
35+
/**
36+
* @var Symfony
37+
*/
38+
protected $proxyClient;
39+
40+
/**
41+
* @var SymfonyProxy
42+
*/
43+
protected $proxy;
44+
45+
/**
46+
* Get server port
47+
*
48+
* @return int
49+
*
50+
* @throws \Exception
51+
*/
52+
protected function getCachingProxyPort()
53+
{
54+
if (!defined('WEB_SERVER_PORT')) {
55+
throw new \Exception('Set WEB_SERVER_PORT in your phpunit.xml');
56+
}
57+
58+
return WEB_SERVER_PORT;
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
protected function getProxy()
65+
{
66+
if (null === $this->proxy) {
67+
$this->proxy = new SymfonyProxy();
68+
}
69+
70+
return $this->proxy;
71+
}
72+
73+
/**
74+
* Get Symfony proxy client
75+
*
76+
* We use a non-default method for PURGE because the built-in PHP webserver
77+
* does not allow arbitrary HTTP methods.
78+
* https://github.com/php/php-src/blob/PHP-5.4.1/sapi/cli/php_http_parser.c#L78-L102
79+
*
80+
* @return Symfony
81+
*/
82+
protected function getProxyClient()
83+
{
84+
if (null === $this->proxyClient) {
85+
$this->proxyClient = new Symfony(
86+
array('http://127.0.0.1:' . $this->getCachingProxyPort()),
87+
$this->getHostName() . ':' . $this->getCachingProxyPort(),
88+
null,
89+
array('purge_method' => 'NOTIFY')
90+
);
91+
}
92+
93+
return $this->proxyClient;
94+
}
95+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace FOS\HttpCache\Tests\Functional\Fixtures\Symfony;
4+
5+
use FOS\HttpCache\SymfonyCache\EventDispatchingHttpCache;
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpKernel\HttpKernelInterface;
8+
9+
class AppCache extends EventDispatchingHttpCache
10+
{
11+
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
12+
{
13+
$response = parent::handle($request, $type, $catch);
14+
15+
if ($response->headers->has('X-Symfony-Cache')) {
16+
if (false !== strpos($response->headers->get('X-Symfony-Cache'), 'miss')) {
17+
$state = 'MISS';
18+
} elseif (false !== strpos($response->headers->get('X-Symfony-Cache'), 'fresh')) {
19+
$state = 'HIT';
20+
} else {
21+
$state = 'UNDETERMINED';
22+
}
23+
$response->headers->set('X-Cache', $state);
24+
}
25+
26+
return $response;
27+
}
28+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace FOS\HttpCache\Tests\Functional\Fixtures\Symfony;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
use Symfony\Component\HttpFoundation\Response;
7+
use Symfony\Component\HttpKernel\HttpKernel;
8+
use Symfony\Component\HttpKernel\HttpKernelInterface;
9+
10+
class AppKernel extends HttpKernel
11+
{
12+
public function __construct() {}
13+
14+
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
15+
{
16+
switch ($request->getPathInfo()) {
17+
case '/cache':
18+
$response = new Response(microtime(true));
19+
$response->setCache(array('max_age' => 3600, 'public' => true));
20+
21+
return $response;
22+
case '/negotiation':
23+
$response = new Response(microtime(true));
24+
$response->setCache(array('max_age' => 3600, 'public' => true));
25+
$response->headers->set('Content-Type', $_SERVER['HTTP_ACCEPT']);
26+
$response->setVary('Accept');
27+
28+
return $response;
29+
}
30+
31+
return new Response('Unknown request '.$request->getPathInfo(), 404);
32+
}
33+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
use FOS\HttpCache\SymfonyCache\PurgeSubscriber;
4+
use FOS\HttpCache\SymfonyCache\RefreshSubscriber;
5+
use FOS\HttpCache\SymfonyCache\UserContextSubscriber;
6+
use FOS\HttpCache\Test\Proxy\SymfonyProxy;
7+
use FOS\HttpCache\Tests\Functional\Fixtures\Symfony\AppCache;
8+
use FOS\HttpCache\Tests\Functional\Fixtures\Symfony\AppKernel;
9+
use Symfony\Component\HttpFoundation\Request;
10+
use Symfony\Component\HttpKernel\HttpCache\Store;
11+
12+
$loader = require_once __DIR__.'/../../../../vendor/autoload.php';
13+
14+
$symfonyProxy = new SymfonyProxy();
15+
16+
$kernel = new AppKernel();
17+
$kernel = new AppCache($kernel, new Store($symfonyProxy->getCacheDir()), null, array('debug' => true));
18+
$kernel->addSubscriber(new PurgeSubscriber(array('purge_method' => 'NOTIFY')));
19+
$kernel->addSubscriber(new RefreshSubscriber());
20+
$kernel->addSubscriber(new UserContextSubscriber());
21+
$request = Request::createFromGlobals();
22+
$response = $kernel->handle($request);
23+
$response->send();
24+
$kernel->terminate($request, $response);
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\HttpCache\Tests\Functional;
13+
14+
use FOS\HttpCache\ProxyClient\Symfony;
15+
use FOS\HttpCache\Test\SymfonyTestCase;
16+
17+
/**
18+
* @group webserver
19+
* @group symfony
20+
*/
21+
class SymfonyProxyClientTest extends SymfonyTestCase
22+
{
23+
public function setUp()
24+
{
25+
/*
26+
* We did not figure out how to run this test with hhvm. Help welcome!
27+
* On travis, connection is closed after the headers are sent without providing
28+
* anything in either the hhvm or apache log.
29+
*/
30+
if (defined('HHVM_VERSION')) {
31+
$this->markTestSkipped('Test not working with hhvm as backend server');
32+
}
33+
34+
parent::setUp();
35+
}
36+
37+
public function testPurge()
38+
{
39+
$this->assertMiss($this->getResponse('/symfony.php/cache'));
40+
$this->assertHit($this->getResponse('/symfony.php/cache'));
41+
42+
$this->getProxyClient()->purge('/symfony.php/cache')->flush();
43+
$this->assertMiss($this->getResponse('/symfony.php/cache'));
44+
}
45+
46+
public function testPurgeContentType()
47+
{
48+
$json = array('Accept' => 'application/json');
49+
$html = array('Accept' => 'text/html');
50+
51+
$response = $this->getResponse('/symfony.php/negotiation', $json);
52+
$this->assertMiss($response);
53+
$this->assertEquals('application/json', $response->getContentType());
54+
$this->assertHit($this->getResponse('/symfony.php/negotiation', $json));
55+
56+
$response = $this->getResponse('/symfony.php/negotiation', $html);
57+
$this->assertContains('text/html', $response->getContentType());
58+
$this->assertMiss($response);
59+
$this->assertHit($this->getResponse('/symfony.php/negotiation', $html));
60+
61+
$this->getResponse('/symfony.php/negotiation');
62+
$this->getProxyClient()->purge('/symfony.php/negotiation')->flush();
63+
$this->assertMiss($this->getResponse('/symfony.php/negotiation', $json));
64+
$this->assertMiss($this->getResponse('/symfony.php/negotiation', $html));
65+
}
66+
67+
public function testPurgeHost()
68+
{
69+
$symfony = new Symfony(array('http://127.0.0.1:' . $this->getCachingProxyPort()), null, null, array('purge_method' => 'NOTIFY'));
70+
71+
$this->getResponse('/symfony.php/cache');
72+
73+
$symfony->purge('http://localhost:8080/symfony.php/cache')->flush();
74+
$this->assertMiss($this->getResponse('/symfony.php/cache'));
75+
}
76+
77+
public function testRefresh()
78+
{
79+
$this->assertMiss($this->getResponse('/symfony.php/cache'));
80+
$response = $this->getResponse('/symfony.php/cache');
81+
$this->assertHit($response);
82+
83+
$this->getProxyClient()->refresh('/symfony.php/cache')->flush();
84+
usleep(100);
85+
$refreshed = $this->getResponse('/symfony.php/cache');
86+
$this->assertGreaterThan((float) $response->getBody(true), (float) $refreshed->getBody(true));
87+
}
88+
89+
public function testRefreshContentType()
90+
{
91+
$json = array('Accept' => 'application/json');
92+
$html = array('Accept' => 'text/html');
93+
94+
$this->getProxyClient()->refresh('/symfony.php/negotiation', $json)->flush();
95+
96+
$this->assertHit($this->getResponse('/symfony.php/negotiation', $json));
97+
$this->assertMiss($this->getResponse('/symfony.php/negotiation', $html));
98+
}
99+
}

0 commit comments

Comments
 (0)