mirror of
https://github.com/FriendsOfSymfony/FOSHttpCache.git
synced 2026-03-24 15:02:27 +01:00
263 lines
9.3 KiB
PHP
263 lines
9.3 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the FOSHttpCache package.
|
|
*
|
|
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace FOS\HttpCache\Tests\Unit;
|
|
|
|
use FOS\HttpCache\CacheInvalidator;
|
|
use FOS\HttpCache\EventListener\LogListener;
|
|
use FOS\HttpCache\Exception\ExceptionCollection;
|
|
use FOS\HttpCache\Exception\ProxyResponseException;
|
|
use FOS\HttpCache\Exception\ProxyUnreachableException;
|
|
use FOS\HttpCache\Exception\UnsupportedProxyOperationException;
|
|
use FOS\HttpCache\ProxyClient\Invalidation\BanCapable;
|
|
use FOS\HttpCache\ProxyClient\Invalidation\PurgeCapable;
|
|
use FOS\HttpCache\ProxyClient\Invalidation\RefreshCapable;
|
|
use FOS\HttpCache\ProxyClient\Invalidation\TagCapable;
|
|
use FOS\HttpCache\ProxyClient\ProxyClient;
|
|
use FOS\HttpCache\ProxyClient\Varnish;
|
|
use Http\Client\Exception\HttpException;
|
|
use Http\Client\Exception\NetworkException;
|
|
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
|
|
use Mockery\MockInterface;
|
|
use PHPUnit\Framework\Attributes as PHPUnit;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Http\Message\RequestInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
|
|
|
class CacheInvalidatorTest extends TestCase
|
|
{
|
|
use MockeryPHPUnitIntegration;
|
|
|
|
public function testSupportsTrue(): void
|
|
{
|
|
/** @var MockInterface&Varnish $proxyClient */
|
|
$proxyClient = \Mockery::mock(Varnish::class);
|
|
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
|
|
$this->assertTrue($cacheInvalidator->supports(CacheInvalidator::PATH));
|
|
$this->assertTrue($cacheInvalidator->supports(CacheInvalidator::REFRESH));
|
|
$this->assertTrue($cacheInvalidator->supports(CacheInvalidator::INVALIDATE));
|
|
$this->assertTrue($cacheInvalidator->supports(CacheInvalidator::TAGS));
|
|
}
|
|
|
|
public function testSupportsFalse(): void
|
|
{
|
|
/** @var MockInterface&ProxyClient $proxyClient */
|
|
$proxyClient = \Mockery::mock(ProxyClient::class);
|
|
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
|
|
$this->assertFalse($cacheInvalidator->supports(CacheInvalidator::PATH));
|
|
$this->assertFalse($cacheInvalidator->supports(CacheInvalidator::REFRESH));
|
|
$this->assertFalse($cacheInvalidator->supports(CacheInvalidator::INVALIDATE));
|
|
$this->assertFalse($cacheInvalidator->supports(CacheInvalidator::TAGS));
|
|
}
|
|
|
|
public function testSupportsInvalid(): void
|
|
{
|
|
/** @var MockInterface&ProxyClient $proxyClient */
|
|
$proxyClient = \Mockery::mock(ProxyClient::class);
|
|
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
$cacheInvalidator->supports('garbage');
|
|
}
|
|
|
|
public function testInvalidatePath(): void
|
|
{
|
|
/** @var MockInterface&PurgeCapable $purge */
|
|
$purge = \Mockery::mock(PurgeCapable::class)
|
|
->shouldReceive('purge')->once()->with('/my/route', [])
|
|
// https://github.com/phpstan/phpstan-mockery/issues/8
|
|
/* @phpstan-ignore-next-line */
|
|
->shouldReceive('purge')->once()->with('/my/route', ['X-Test-Header' => 'xyz'])
|
|
->shouldReceive('flush')->once()
|
|
->getMock();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($purge);
|
|
|
|
$cacheInvalidator
|
|
->invalidatePath('/my/route')
|
|
->invalidatePath('/my/route', ['X-Test-Header' => 'xyz'])
|
|
->flush()
|
|
;
|
|
}
|
|
|
|
public function testRefreshPath(): void
|
|
{
|
|
$headers = ['X' => 'Y'];
|
|
/** @var MockInterface&RefreshCapable $refresh */
|
|
$refresh = \Mockery::mock(RefreshCapable::class)
|
|
->shouldReceive('refresh')->once()->with('/my/route', $headers)
|
|
// https://github.com/phpstan/phpstan-mockery/issues/8
|
|
/* @phpstan-ignore-next-line */
|
|
->shouldReceive('flush')->never()
|
|
->getMock();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($refresh);
|
|
|
|
$cacheInvalidator
|
|
->refreshPath('/my/route', $headers)
|
|
;
|
|
}
|
|
|
|
public function testInvalidate(): void
|
|
{
|
|
$headers = [
|
|
'X-Header' => '^value.*$',
|
|
'Other-Header' => '^a|b|c$',
|
|
];
|
|
|
|
/** @var MockInterface&BanCapable $ban */
|
|
$ban = \Mockery::mock(BanCapable::class)
|
|
->shouldReceive('ban')
|
|
->with($headers)
|
|
->once()
|
|
->getMock();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($ban);
|
|
$cacheInvalidator->invalidate($headers);
|
|
}
|
|
|
|
public function testInvalidateTags(): void
|
|
{
|
|
$tags = [
|
|
'post-8',
|
|
'post-type-2',
|
|
];
|
|
|
|
/** @var MockInterface&TagCapable $tagHandler */
|
|
$tagHandler = \Mockery::mock(TagCapable::class)
|
|
->shouldReceive('invalidateTags')
|
|
->with($tags)
|
|
->once()
|
|
->getMock();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($tagHandler);
|
|
$cacheInvalidator->invalidateTags($tags);
|
|
}
|
|
|
|
public function testInvalidateRegex(): void
|
|
{
|
|
/** @var MockInterface&BanCapable $ban */
|
|
$ban = \Mockery::mock(BanCapable::class)
|
|
->shouldReceive('banPath')
|
|
->with('/a', 'b', ['example.com'])
|
|
->once()
|
|
->getMock();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($ban);
|
|
$cacheInvalidator->invalidateRegex('/a', 'b', ['example.com']);
|
|
}
|
|
|
|
public static function provideOperations(): iterable
|
|
{
|
|
yield from [
|
|
['invalidatePath', '/'],
|
|
['refreshPath', '/'],
|
|
['invalidate', []],
|
|
['invalidateRegex', '/'],
|
|
['invalidateTags', []],
|
|
];
|
|
}
|
|
|
|
#[PHPUnit\DataProvider('provideOperations')]
|
|
public function testMethodException(string $method, $arg): void
|
|
{
|
|
/** @var MockInterface&ProxyClient $proxyClient */
|
|
$proxyClient = \Mockery::mock(ProxyClient::class);
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
|
|
$this->expectException(UnsupportedProxyOperationException::class);
|
|
$cacheInvalidator->$method($arg);
|
|
}
|
|
|
|
public function testProxyClientExceptionsAreLogged(): void
|
|
{
|
|
/** @var MockInterface&RequestInterface $failedRequest */
|
|
$failedRequest = \Mockery::mock(RequestInterface::class)
|
|
->shouldReceive('getHeaderLine')->with('Host')->andReturn('127.0.0.1')
|
|
->getMock();
|
|
$clientException = new NetworkException('Couldn\'t connect to host', $failedRequest);
|
|
|
|
$unreachableException = ProxyUnreachableException::proxyUnreachable($clientException);
|
|
|
|
$response = \Mockery::mock(ResponseInterface::class)
|
|
->shouldReceive('getStatusCode')->andReturn(403)
|
|
// https://github.com/phpstan/phpstan-mockery/issues/8
|
|
/* @phpstan-ignore-next-line */
|
|
->shouldReceive('getReasonPhrase')->andReturn('Forbidden')
|
|
->getMock();
|
|
$responseException = ProxyResponseException::proxyResponse(new HttpException('test', $failedRequest, $response));
|
|
|
|
$exceptions = new ExceptionCollection();
|
|
$exceptions->add($unreachableException)->add($responseException);
|
|
|
|
/** @var MockInterface&ProxyClient $proxyClient */
|
|
$proxyClient = \Mockery::mock(ProxyClient::class)
|
|
->shouldReceive('flush')->once()->andThrow($exceptions)
|
|
->getMock();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
|
|
$logger = \Mockery::mock(LoggerInterface::class)
|
|
->shouldReceive('log')->once()
|
|
->with(
|
|
'critical',
|
|
'Request to caching proxy at 127.0.0.1 failed with message "Couldn\'t connect to host"',
|
|
['exception' => $unreachableException]
|
|
)
|
|
// https://github.com/phpstan/phpstan-mockery/issues/8
|
|
/* @phpstan-ignore-next-line */
|
|
->shouldReceive('log')->once()
|
|
->with(
|
|
'critical',
|
|
'403 error response "Forbidden" from caching proxy',
|
|
['exception' => $responseException]
|
|
)
|
|
->getMock();
|
|
|
|
$cacheInvalidator->getEventDispatcher()->addSubscriber(new LogListener($logger));
|
|
|
|
$this->expectException(ExceptionCollection::class);
|
|
$cacheInvalidator
|
|
->flush()
|
|
;
|
|
}
|
|
|
|
public function testEventDispatcher(): void
|
|
{
|
|
/** @var MockInterface&Varnish $proxyClient */
|
|
$proxyClient = \Mockery::mock(Varnish::class);
|
|
$eventDispatcher = new EventDispatcher();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
$cacheInvalidator->setEventDispatcher($eventDispatcher);
|
|
$this->assertSame($eventDispatcher, $cacheInvalidator->getEventDispatcher());
|
|
}
|
|
|
|
public function testEventDispatcherImmutable(): void
|
|
{
|
|
/** @var MockInterface&Varnish $proxyClient */
|
|
$proxyClient = \Mockery::mock(Varnish::class);
|
|
$eventDispatcher = new EventDispatcher();
|
|
|
|
$cacheInvalidator = new CacheInvalidator($proxyClient);
|
|
$cacheInvalidator->setEventDispatcher($eventDispatcher);
|
|
$this->expectException(\Exception::class);
|
|
$cacheInvalidator->setEventDispatcher($eventDispatcher);
|
|
}
|
|
}
|