diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc030b2..9cb1ae7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: - php: 8.1 # should use monolog v3 - php: 8.2 # should use monolog v3 - php: 8.3 # should use monolog v3 + # TODO matrix should support Both versions of Sentry SDK steps: - uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 06d6576..5f6be3b 100644 --- a/composer.json +++ b/composer.json @@ -37,14 +37,14 @@ "require": { "php": "^7.4 || ^8.0", "monolog/monolog": "^1.6 || ^2.0 || ^3.0", - "sentry/sentry": "^3.1" + "sentry/sentry": "^3.1 || ^4.0" }, "require-dev": { "coduo/php-matcher": "^6.0.8", - "friendsofphp/php-cs-fixer": "^3.16", + "friendsofphp/php-cs-fixer": "3.51.0", "jangregor/phpstan-prophecy": "1.0.0", - "phpstan/phpstan": "1.10.14", - "phpstan/phpstan-phpunit": "1.3.11", + "phpstan/phpstan": "1.10.59", + "phpstan/phpstan-phpunit": "1.3.16", "phpunit/phpunit": "^9.6.7", "symfony/phpunit-bridge": "^6.2.10", "symfony/var-dumper": ">4.3" diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d364861..3ce988c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -6,9 +6,10 @@ parameters: inferPrivatePropertyTypeFromConstructor: true tipsOfTheDay: false checkMissingIterableValueType: false + treatPhpDocTypesAsCertain: false level: 8 - paths: [ src, tests ] + paths: [ src ] ignoreErrors: - "#^Cannot assign offset 'formatted' to Monolog\\\\LogRecord\\.$#" @@ -20,12 +21,3 @@ parameters: - message: "#^Parameter \\#1 \\$record of method Monolog\\\\Formatter\\\\LineFormatter\\:\\:format\\(\\) expects Monolog\\\\LogRecord, array\\\\|Monolog\\\\LogRecord given\\.$#" path: src/SentryHandler.php - - - message: "#expects Monolog\\\\LogRecord, array\\\\|Monolog\\\\LogRecord given\\.$#" - path: tests - - - message: "#expects array\\, array\\\\|Monolog\\\\LogRecord\\> given\\.$#" - path: tests - - - message: "#^PHPDoc tag @param for parameter \\$level with type array\\\\|int is not subtype of native type int\\|null\\.$#" - path: tests diff --git a/tests/SentryHandlerTest.php b/tests/SentryHandlerTest.php index 27e6854..eb07851 100644 --- a/tests/SentryHandlerTest.php +++ b/tests/SentryHandlerTest.php @@ -4,28 +4,25 @@ namespace BGalati\MonologSentryHandler\Tests; -use BGalati\MonologSentryHandler\SentryHandler; use Coduo\PHPMatcher\PHPUnit\PHPMatcherAssertions; -use GuzzleHttp\Promise\FulfilledPromise; -use GuzzleHttp\Promise\PromiseInterface; use Monolog\Formatter\LineFormatter; use Monolog\Level; use Monolog\Logger; use Monolog\LogRecord; use PHPUnit\Framework\TestCase; use Sentry\Breadcrumb; +use Sentry\Client; use Sentry\ClientBuilder; -use Sentry\Event; use Sentry\Integration\EnvironmentIntegration; -use Sentry\Options; -use Sentry\Response; -use Sentry\ResponseStatus; use Sentry\SentrySdk; use Sentry\Severity; use Sentry\State\HubInterface; -use Sentry\State\Scope; -use Sentry\Transport\TransportFactoryInterface; -use Sentry\Transport\TransportInterface; + +if (defined(Client::class.'::SDK_VERSION') && version_compare(Client::SDK_VERSION, '4.0.0') >= 0) { + require __DIR__.'/SentryStubV4.php'; +} else { + require __DIR__.'/SentryStubV3.php'; +} final class SentryHandlerTest extends TestCase { @@ -47,7 +44,12 @@ protected function setUp(): void ], ] ); - $clientBuilder->setTransportFactory(new FakeTransportFactory($this->transport)); + + if (defined(Client::class.'::SDK_VERSION') && version_compare(Client::SDK_VERSION, '4.0.0') >= 0) { + $clientBuilder->setTransport($this->transport); + } else { + $clientBuilder->setTransportFactory(new FakeTransportFactory($this->transport)); + } $client = $clientBuilder->getClient(); @@ -492,7 +494,7 @@ private function assertCapturedEvent(Severity $severity, string $message, array if ($breadcrumbs) { $this->assertMatchesPattern( - json_encode($breadcrumbs), + json_encode($breadcrumbs, JSON_THROW_ON_ERROR), json_encode( array_map( static function (Breadcrumb $breadcrumb) { @@ -544,88 +546,3 @@ private function createSentryHandler(?int $level = null): SpySentryHandler return $handler; } } - -class SpySentryHandler extends SentryHandler -{ - /** - * @var bool - */ - public $afterWriteCalled = false; - - protected function processScope(Scope $scope, $record, Event $sentryEvent): void - { - $scope->setExtra('processScope', 'called'); - } - - protected function afterWrite(): void - { - $this->afterWriteCalled = true; - - parent::afterWrite(); - } - - public function resetSpy(): void - { - $this->afterWriteCalled = false; - } -} - -class SpyTransport implements TransportInterface -{ - /** - * @var Event|null - */ - public $spiedEvent; - - /** - * @var bool - */ - public $isFlushed = false; - - public function send(Event $event): PromiseInterface - { - $this->spiedEvent = $event; - - return new FulfilledPromise(new Response(ResponseStatus::skipped(), $event)); - } - - public function resetSpy(): void - { - $this->spiedEvent = null; - $this->isFlushed = false; - } - - public function getSpiedEvent(): Event - { - if (null === $this->spiedEvent) { - throw new \RuntimeException('No spied scope'); - } - - return $this->spiedEvent; - } - - public function close(?int $timeout = null): PromiseInterface - { - $this->isFlushed = true; - - return new FulfilledPromise(true); - } -} - -class FakeTransportFactory implements TransportFactoryInterface -{ - /** - * @var SpyTransport - */ - private $transport; - - public function __construct(SpyTransport $transport) - { - $this->transport = $transport; - } - - public function create(Options $options): TransportInterface - { - return $this->transport; - } -} diff --git a/tests/SentryStubV3.php b/tests/SentryStubV3.php new file mode 100644 index 0000000..456b734 --- /dev/null +++ b/tests/SentryStubV3.php @@ -0,0 +1,74 @@ +spiedEvent = $event; + + return new FulfilledPromise(new Response(ResponseStatus::skipped(), $event)); + } + + public function resetSpy(): void + { + $this->spiedEvent = null; + $this->isFlushed = false; + } + + public function getSpiedEvent(): Event + { + if (null === $this->spiedEvent) { + throw new \RuntimeException('No spied scope'); + } + + return $this->spiedEvent; + } + + public function close(?int $timeout = null): PromiseInterface + { + $this->isFlushed = true; + + return new FulfilledPromise(true); + } +} + +class FakeTransportFactory implements TransportFactoryInterface +{ + /** + * @var SpyTransport + */ + private $transport; + + public function __construct(SpyTransport $transport) + { + $this->transport = $transport; + } + + public function create(Options $options): TransportInterface + { + return $this->transport; + } +} diff --git a/tests/SentryStubV4.php b/tests/SentryStubV4.php new file mode 100644 index 0000000..1509930 --- /dev/null +++ b/tests/SentryStubV4.php @@ -0,0 +1,52 @@ +spiedEvent = $event; + + return new Result(ResultStatus::skipped(), $event); + } + + public function resetSpy(): void + { + $this->spiedEvent = null; + $this->isFlushed = false; + } + + public function getSpiedEvent(): Event + { + if (null === $this->spiedEvent) { + throw new \RuntimeException('No spied scope'); + } + + return $this->spiedEvent; + } + + public function close(?int $timeout = null): Result + { + $this->isFlushed = true; + + return new Result(ResultStatus::success()); + } +} diff --git a/tests/SpySentryHandler.php b/tests/SpySentryHandler.php new file mode 100644 index 0000000..805c15a --- /dev/null +++ b/tests/SpySentryHandler.php @@ -0,0 +1,34 @@ +setExtra('processScope', 'called'); + } + + protected function afterWrite(): void + { + $this->afterWriteCalled = true; + + parent::afterWrite(); + } + + public function resetSpy(): void + { + $this->afterWriteCalled = false; + } +}