Files
archived-FOSHttpCache/tests/Unit/CacheInvalidatorTest.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);
}
}