diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e96bdf9a..f63dca9a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -108,7 +108,7 @@ jobs: restore-keys: ${{ runner.os }}-${{ matrix.php }}-composer-${{ matrix.dependencies }}- - name: Remove optional packages - run: composer remove doctrine/dbal doctrine/doctrine-bundle symfony/messenger symfony/twig-bundle symfony/cache symfony/cache-contracts --dev --no-update + run: composer remove doctrine/dbal doctrine/doctrine-bundle symfony/messenger symfony/twig-bundle symfony/cache --dev --no-update - name: Install highest dependencies run: composer update --no-progress --no-interaction --prefer-dist diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ca8c89a3..2aa559cd 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,12 +1,12 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder\\:\\:root\\(\\)\\.$#" + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:booleanNode\\(\\)\\.$#" count: 1 path: src/DependencyInjection/Configuration.php - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:booleanNode\\(\\)\\.$#" + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder\\:\\:root\\(\\)\\.$#" count: 1 path: src/DependencyInjection/Configuration.php @@ -16,42 +16,42 @@ parameters: path: src/EventListener/ErrorListener.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$driverOptions with no value type specified in iterable type array\\.$#" + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:connect\\(\\)\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$password with no typehint specified\\.$#" - count: 1 + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:getDatabasePlatform\\(\\)\\.$#" + count: 2 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$username with no typehint specified\\.$#" + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:getSchemaManager\\(\\)\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:connect\\(\\)\\.$#" + message: "#^Call to an undefined method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:convertException\\(\\)\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:getDatabasePlatform\\(\\)\\.$#" - count: 2 + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$driverOptions with no value type specified in iterable type array\\.$#" + count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:getSchemaManager\\(\\)\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$password with no typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:convertException\\(\\) has parameter \\$message with no typehint specified\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$username with no typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Call to an undefined method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:convertException\\(\\)\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:convertException\\(\\) has parameter \\$message with no typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriver.php @@ -61,57 +61,57 @@ parameters: path: src/Tracing/Doctrine/DBAL/TracingDriver.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:prepare\\(\\) has parameter \\$sql with no typehint specified\\.$#" + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\\\Connection\\:\\:errorCode\\(\\)\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:query\\(\\) has parameter \\$args with no typehint specified\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorCode\\(\\) has no return typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:exec\\(\\) has parameter \\$sql with no typehint specified\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorInfo\\(\\) has no return typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorCode\\(\\) has no return typehint specified\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:exec\\(\\) has parameter \\$sql with no typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php - - message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\\\Connection\\:\\:errorCode\\(\\)\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:prepare\\(\\) has parameter \\$sql with no typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php - - message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorInfo\\(\\) has no return typehint specified\\.$#" + message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:query\\(\\) has parameter \\$args with no typehint specified\\.$#" count: 1 path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php - - message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent not found\\.$#" + message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#" count: 1 path: src/aliases.php - - message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent not found\\.$#" - count: 2 + message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterResponseEvent not found\\.$#" + count: 1 path: src/aliases.php - - message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterResponseEvent not found\\.$#" - count: 1 + message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent not found\\.$#" + count: 2 path: src/aliases.php - - message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\PostResponseEvent not found\\.$#" + message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent not found\\.$#" count: 1 path: src/aliases.php - - message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#" + message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\PostResponseEvent not found\\.$#" count: 1 path: src/aliases.php @@ -121,12 +121,12 @@ parameters: path: tests/End2End/End2EndTest.php - - message: "#^Parameter \\$event of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\ErrorListenerTest\\:\\:testHandleExceptionEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent\\.$#" + message: "#^Call to method getException\\(\\) on an unknown class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent\\.$#" count: 1 path: tests/EventListener/ErrorListenerTest.php - - message: "#^Call to method getException\\(\\) on an unknown class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent\\.$#" + message: "#^Instantiated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent not found\\.$#" count: 1 path: tests/EventListener/ErrorListenerTest.php @@ -136,28 +136,23 @@ parameters: path: tests/EventListener/ErrorListenerTest.php - - message: "#^Instantiated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent not found\\.$#" + message: "#^Parameter \\$event of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\ErrorListenerTest\\:\\:testHandleExceptionEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent\\.$#" count: 1 path: tests/EventListener/ErrorListenerTest.php - - - message: "#^If condition is always false\\.$#" - count: 1 - path: tests/EventListener/MessengerListenerTest.php - - message: "#^Class Symfony\\\\Component\\\\Messenger\\\\Event\\\\WorkerMessageFailedEvent constructor invoked with 4 parameters, 3 required\\.$#" count: 1 path: tests/EventListener/MessengerListenerTest.php - - message: "#^Parameter \\$requestEvent of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\RequestListenerTest\\:\\:testHandleKernelRequestEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\.$#" + message: "#^If condition is always false\\.$#" count: 1 - path: tests/EventListener/RequestListenerTest.php + path: tests/EventListener/MessengerListenerTest.php - - message: "#^Parameter \\#1 \\$event of method Sentry\\\\SentryBundle\\\\EventListener\\\\RequestListener\\:\\:handleKernelRequestEvent\\(\\) expects Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\RequestEvent, Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\|Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\RequestEvent given\\.$#" - count: 1 + message: "#^Instantiated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#" + count: 3 path: tests/EventListener/RequestListenerTest.php - @@ -166,49 +161,74 @@ parameters: path: tests/EventListener/RequestListenerTest.php - - message: "#^Parameter \\$controllerEvent of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\RequestListenerTest\\:\\:testHandleKernelControllerEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent\\.$#" + message: "#^Parameter \\#1 \\$event of method Sentry\\\\SentryBundle\\\\EventListener\\\\RequestListener\\:\\:handleKernelControllerEvent\\(\\) expects Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\ControllerEvent, Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\ControllerEvent\\|Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent given\\.$#" count: 1 path: tests/EventListener/RequestListenerTest.php - - message: "#^Parameter \\#1 \\$event of method Sentry\\\\SentryBundle\\\\EventListener\\\\RequestListener\\:\\:handleKernelControllerEvent\\(\\) expects Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\ControllerEvent, Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\ControllerEvent\\|Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent given\\.$#" + message: "#^Parameter \\#1 \\$event of method Sentry\\\\SentryBundle\\\\EventListener\\\\RequestListener\\:\\:handleKernelRequestEvent\\(\\) expects Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\RequestEvent, Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\|Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\RequestEvent given\\.$#" count: 1 path: tests/EventListener/RequestListenerTest.php - - message: "#^Instantiated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#" - count: 3 + message: "#^Parameter \\$controllerEvent of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\RequestListenerTest\\:\\:testHandleKernelControllerEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent\\.$#" + count: 1 path: tests/EventListener/RequestListenerTest.php - - message: "#^Parameter \\$event of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\SubRequestListenerTest\\:\\:testHandleKernelRequestEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\.$#" + message: "#^Parameter \\$requestEvent of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\RequestListenerTest\\:\\:testHandleKernelRequestEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\.$#" count: 1 - path: tests/EventListener/SubRequestListenerTest.php + path: tests/EventListener/RequestListenerTest.php - message: "#^Call to method isMasterRequest\\(\\) on an unknown class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\.$#" count: 1 path: tests/EventListener/SubRequestListenerTest.php + - + message: "#^Instantiated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent not found\\.$#" + count: 2 + path: tests/EventListener/SubRequestListenerTest.php + - message: "#^Parameter \\#1 \\$event of method Sentry\\\\SentryBundle\\\\EventListener\\\\SubRequestListener\\:\\:handleKernelRequestEvent\\(\\) expects Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\RequestEvent, Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\|Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\RequestEvent given\\.$#" count: 1 path: tests/EventListener/SubRequestListenerTest.php - - message: "#^Instantiated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent not found\\.$#" - count: 2 + message: "#^Parameter \\$event of method Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\SubRequestListenerTest\\:\\:testHandleKernelRequestEvent\\(\\) has invalid typehint type Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent\\.$#" + count: 1 path: tests/EventListener/SubRequestListenerTest.php - - message: "#^Trying to mock an undefined method getName\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\.$#" + message: "#^Call to an undefined method TCacheAdapter of Symfony\\\\Component\\\\Cache\\\\Adapter\\\\AdapterInterface\\:\\:delete\\(\\)\\.$#" + count: 2 + path: tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php + + - + message: "#^Call to an undefined method TCacheAdapter of Symfony\\\\Component\\\\Cache\\\\Adapter\\\\AdapterInterface\\:\\:get\\(\\)\\.$#" + count: 2 + path: tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php + + - + message: "#^Call to an undefined method TCacheAdapter of Symfony\\\\Component\\\\Cache\\\\Adapter\\\\AdapterInterface\\:\\:prune\\(\\)\\.$#" + count: 2 + path: tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php + + - + message: "#^Call to an undefined method TCacheAdapter of Symfony\\\\Component\\\\Cache\\\\Adapter\\\\AdapterInterface\\:\\:reset\\(\\)\\.$#" + count: 2 + path: tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php + + - + message: "#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with array\\(Symfony\\\\Component\\\\Cache\\\\CacheItem\\) and iterable\\&Traversable will always evaluate to false\\.$#" count: 1 - path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php + path: tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php - - message: "#^Trying to mock an undefined method getDatabase\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\.$#" + message: "#^Parameter \\#2 \\$decoratedAdapter of class Sentry\\\\SentryBundle\\\\Tracing\\\\Cache\\\\TraceableTagAwareCacheAdapter constructor expects Symfony\\\\Component\\\\Cache\\\\Adapter\\\\TagAwareAdapterInterface, Symfony\\\\Component\\\\Cache\\\\Adapter\\\\AdapterInterface given\\.$#" count: 1 - path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php + path: tests/Tracing/Cache/TraceableTagAwareCacheAdapterTest.php - message: "#^Parameter \\#1 \\$driverException of class Doctrine\\\\DBAL\\\\Exception\\\\DriverException constructor expects Doctrine\\\\DBAL\\\\Driver\\\\Exception, string given\\.$#" @@ -224,3 +244,14 @@ parameters: message: "#^Trying to mock an undefined method convertException\\(\\) on class Sentry\\\\SentryBundle\\\\Tests\\\\Tracing\\\\Doctrine\\\\DBAL\\\\StubExceptionConverterDriverInterface\\.$#" count: 1 path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php + + - + message: "#^Trying to mock an undefined method getDatabase\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\.$#" + count: 1 + path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php + + - + message: "#^Trying to mock an undefined method getName\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\.$#" + count: 1 + path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php + diff --git a/src/Tracing/Cache/TraceableCacheAdapterTrait.php b/src/Tracing/Cache/TraceableCacheAdapterTrait.php index f96a9506..09bde35a 100644 --- a/src/Tracing/Cache/TraceableCacheAdapterTrait.php +++ b/src/Tracing/Cache/TraceableCacheAdapterTrait.php @@ -71,7 +71,7 @@ public function get(string $key, callable $callback, float $beta = null, array & { return $this->traceFunction('cache.get_item', function () use ($key, $callback, $beta, &$metadata) { if (!$this->decoratedAdapter instanceof CacheInterface) { - throw new \BadMethodCallException(sprintf('The %s() method is not supported on an adapter that does not implement the "%s" interface.', __FUNCTION__, CacheInterface::class)); + throw new \BadMethodCallException(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class)); } return $this->decoratedAdapter->get($key, $callback, $beta, $metadata); @@ -85,7 +85,7 @@ public function delete(string $key): bool { return $this->traceFunction('cache.delete_item', function () use ($key) { if (!$this->decoratedAdapter instanceof CacheInterface) { - throw new \BadMethodCallException(sprintf('The %s() method is not supported on an adapter that does not implement the "%s" interface.', __FUNCTION__, CacheInterface::class)); + throw new \BadMethodCallException(sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class)); } return $this->decoratedAdapter->delete($key); @@ -159,7 +159,7 @@ public function prune(): bool { return $this->traceFunction('cache.prune', function (): bool { if (!$this->decoratedAdapter instanceof PruneableInterface) { - throw new \BadMethodCallException(sprintf('The %s() method is not supported on an adapter that does not implement the "%s" interface.', __FUNCTION__, PruneableInterface::class)); + throw new \BadMethodCallException(sprintf('The %s::prune() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, PruneableInterface::class)); } return $this->decoratedAdapter->prune(); @@ -172,7 +172,7 @@ public function prune(): bool public function reset(): void { if (!$this->decoratedAdapter instanceof ResettableInterface) { - throw new \BadMethodCallException(sprintf('The %s() method is not supported on an adapter that does not implement the "%s" interface.', __FUNCTION__, ResettableInterface::class)); + throw new \BadMethodCallException(sprintf('The %s::reset() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, ResettableInterface::class)); } $this->decoratedAdapter->reset(); diff --git a/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php b/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php new file mode 100644 index 00000000..b1ce91b5 --- /dev/null +++ b/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php @@ -0,0 +1,433 @@ +hub = $this->createMock(HubInterface::class); + } + + public function testGetItem(): void + { + $cacheItem = new CacheItem(); + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('getItem') + ->with('foo') + ->willReturn($cacheItem); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertSame($cacheItem, $adapter->getItem('foo')); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.get_item', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testGetItems(): void + { + $cacheItems = [new CacheItem()]; + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('getItems') + ->with(['foo']) + ->willReturn($cacheItems); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertSame($cacheItems, $adapter->getItems(['foo'])); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.get_items', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testClear(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('clear') + ->with('foo') + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->clear('foo')); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.clear', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testGet(): void + { + $callback = static function () {}; + $metadata = []; + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(CacheInterface::class); + $decoratedAdapter->expects($this->once()) + ->method('get') + ->with('foo', $callback, 1.0, $metadata) + ->willReturn('bar'); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertSame('bar', $adapter->get('foo', $callback, 1.0, $metadata)); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.get_item', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testGetThrowsExceptionIfDecoratedAdapterDoesNotImplementTheCacheInterface(): void + { + $adapter = $this->createCacheAdapter($this->createMock(static::getAdapterClassFqcn())); + + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "Symfony\\Contracts\\Cache\\CacheInterface" interface.', \get_class($adapter))); + + $adapter->get('foo', static function () {}); + } + + public function testDelete(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(CacheInterface::class); + $decoratedAdapter->expects($this->once()) + ->method('delete') + ->with('foo') + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->delete('foo')); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.delete_item', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testDeleteThrowsExceptionIfDecoratedAdapterDoesNotImplementTheCacheInterface(): void + { + $adapter = $this->createCacheAdapter($this->createMock(static::getAdapterClassFqcn())); + + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage(sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "Symfony\\Contracts\\Cache\\CacheInterface" interface.', \get_class($adapter))); + + $adapter->delete('foo'); + } + + public function testHasItem(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('hasItem') + ->with('foo') + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->hasItem('foo')); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.has_item', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testDeleteItem(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('deleteItem') + ->with('foo') + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->deleteItem('foo')); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.delete_item', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testDeleteItems(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('deleteItems') + ->with(['foo']) + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->deleteItems(['foo'])); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.delete_items', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testSave(): void + { + $cacheItem = new CacheItem(); + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('save') + ->with($cacheItem) + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->save($cacheItem)); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.save', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testSaveDeferred(): void + { + $cacheItem = new CacheItem(); + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('saveDeferred') + ->with($cacheItem) + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->saveDeferred($cacheItem)); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.save_deferred', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testCommit(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $decoratedAdapter = $this->createMock(static::getAdapterClassFqcn()); + $decoratedAdapter->expects($this->once()) + ->method('commit') + ->willReturn(true); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->commit()); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.commit', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testPrune(): void + { + $transaction = new Transaction(new TransactionContext(), $this->hub); + $transaction->initSpanRecorder(); + + $decoratedAdapter = $this->createMock(PruneableCacheAdapterInterface::class); + $decoratedAdapter->expects($this->once()) + ->method('prune') + ->willReturn(true); + + $this->hub->expects($this->once()) + ->method('getSpan') + ->willReturn($transaction); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + + $this->assertTrue($adapter->prune()); + + $spans = $transaction->getSpanRecorder()->getSpans(); + + $this->assertCount(2, $spans); + $this->assertSame('cache.prune', $spans[1]->getOp()); + $this->assertNotNull($spans[1]->getEndTimestamp()); + } + + public function testPruneThrowsExceptionIfDecoratedAdapterIsNotPruneable(): void + { + $adapter = $this->createCacheAdapter($this->createMock(static::getAdapterClassFqcn())); + + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage(sprintf('The %s::prune() method is not supported because the decorated adapter does not implement the "Symfony\\Component\\Cache\\PruneableInterface" interface.', \get_class($adapter))); + + $adapter->prune(); + } + + public function testReset(): void + { + $decoratedAdapter = $this->createMock(ResettableCacheAdapterInterface::class); + $decoratedAdapter->expects($this->once()) + ->method('reset'); + + $adapter = $this->createCacheAdapter($decoratedAdapter); + $adapter->reset(); + } + + public function testResetThrowsExceptionIfDecoratedAdapterIsNotResettable(): void + { + $adapter = $this->createCacheAdapter($this->createMock(static::getAdapterClassFqcn())); + + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage(sprintf('The %s::reset() method is not supported because the decorated adapter does not implement the "Symfony\\Component\\Cache\\ResettableInterface" interface.', \get_class($adapter))); + + $adapter->reset(); + } + + private static function isCachePackageInstalled(): bool + { + return interface_exists(BaseCacheInterface::class); + } + + /** + * @phpstan-return TCacheAdapter + */ + abstract protected function createCacheAdapter(AdapterInterface $decoratedAdapter): AdapterInterface; + + /** + * @return class-string + */ + abstract protected static function getAdapterClassFqcn(): string; +} + +interface ResettableCacheAdapterInterface extends ResettableInterface, TagAwareAdapterInterface +{ +} + +interface PruneableCacheAdapterInterface extends PruneableInterface, TagAwareAdapterInterface +{ +} + +interface CacheInterface extends BaseCacheInterface, TagAwareAdapterInterface +{ +} diff --git a/tests/Tracing/Cache/TraceableCacheAdapterTest.php b/tests/Tracing/Cache/TraceableCacheAdapterTest.php new file mode 100644 index 00000000..87a46931 --- /dev/null +++ b/tests/Tracing/Cache/TraceableCacheAdapterTest.php @@ -0,0 +1,30 @@ + + */ +final class TraceableCacheAdapterTest extends AbstractTraceableCacheAdapterTest +{ + /** + * {@inheritdoc} + */ + protected function createCacheAdapter(AdapterInterface $decoratedAdapter): AdapterInterface + { + return new TraceableCacheAdapter($this->hub, $decoratedAdapter); + } + + /** + * {@inheritdoc} + */ + protected static function getAdapterClassFqcn(): string + { + return AdapterInterface::class; + } +} diff --git a/tests/Tracing/Cache/TraceableTagAwareCacheAdapterTest.php b/tests/Tracing/Cache/TraceableTagAwareCacheAdapterTest.php new file mode 100644 index 00000000..539e422c --- /dev/null +++ b/tests/Tracing/Cache/TraceableTagAwareCacheAdapterTest.php @@ -0,0 +1,31 @@ + + */ +final class TraceableTagAwareCacheAdapterTest extends AbstractTraceableCacheAdapterTest +{ + /** + * {@inheritdoc} + */ + protected function createCacheAdapter(AdapterInterface $decoratedAdapter): AdapterInterface + { + return new TraceableTagAwareCacheAdapter($this->hub, $decoratedAdapter); + } + + /** + * {@inheritdoc} + */ + protected static function getAdapterClassFqcn(): string + { + return TagAwareAdapterInterface::class; + } +}