diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 578ffaaaa..92a1f90bc 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -21,6 +21,39 @@ $logger->pushHandler(new \Sentry\Monolog\LogsHandler()); To continue sending Monolog records to Sentry issues instead, use `Sentry\Monolog\LogToSentryIssueHandler` for log messages or `Sentry\Monolog\ExceptionToSentryIssueHandler` for exceptions. +### Hub APIs Removed + +The Hub API has been removed. This includes: + +- **Removed classes and interfaces:** + - `Sentry\State\Hub` + - `Sentry\State\HubAdapter` + - `Sentry\State\HubInterface` + +- **Removed methods:** + - `SentrySdk::getCurrentHub()` + - `SentrySdk::setCurrentHub()` + +The SDK now exposes runtime state directly through `SentrySdk`, global helpers, and `Scope`: + +```php +// Before (4.x) +\Sentry\SentrySdk::getCurrentHub()->configureScope(static function (\Sentry\State\Scope $scope): void { + $scope->setTag('feature', 'checkout'); +}); + +// After (5.0) +\Sentry\configureScope(static function (\Sentry\State\Scope $scope): void { + $scope->setTag('feature', 'checkout'); +}); +``` + +Use `SentrySdk::getClient()` for the active client, `SentrySdk::getGlobalScope()` for process-global data, and `SentrySdk::getIsolationScope()` for the current runtime isolation scope. Use the capture helpers such as `captureMessage()`, `captureException()`, and `captureEvent()` to send events. + +Use `configureScope()` to mutate the current isolation scope, `withIsolationScope()` to run code with a temporary forked scope, and `startTransaction()` to start manual tracing instrumentation. + +`SentrySdk::init()` no longer returns a Hub instance. It now returns `void`. + ### Metrics API Removed The entire Metrics API has been removed as it is no longer supported. This includes: diff --git a/analysis-baseline.toml b/analysis-baseline.toml index 2c30c8e02..bb9bc9876 100644 --- a/analysis-baseline.toml +++ b/analysis-baseline.toml @@ -1368,18 +1368,6 @@ code = "possibly-invalid-argument" message = '''Possible argument type mismatch for argument #1 of `Sentry\StacktraceBuilder::buildFromBacktrace`: expected `list, 'class'?: class-string, 'file'?: string, 'function'?: string, 'line'?: int, 'type'?: string}>`, but possibly received `array`.''' count = 1 -[[issues]] -file = "src/State/Hub.php" -code = "mixed-operand" -message = "Right operand in `<` comparison has `mixed` type." -count = 1 - -[[issues]] -file = "src/State/Hub.php" -code = "possibly-null-operand" -message = "Left operand in `<` comparison might be `null` (type `float|null`)." -count = 1 - [[issues]] file = "src/State/Scope.php" code = "impossible-condition" diff --git a/src/Integration/ModulesIntegration.php b/src/Integration/ModulesIntegration.php index f1234f355..9e5ee8c16 100644 --- a/src/Integration/ModulesIntegration.php +++ b/src/Integration/ModulesIntegration.php @@ -28,8 +28,6 @@ public function setupOnce(): void Scope::addGlobalEventProcessor(static function (Event $event): Event { $integration = SentrySdk::getClient()->getIntegration(self::class); - // The integration could be bound to a client that is not the one - // attached to the current hub. If this is the case, bail out if ($integration !== null) { $event->setModules(self::getComposerPackages()); } diff --git a/src/Integration/TransactionIntegration.php b/src/Integration/TransactionIntegration.php index 746584dab..13c7bfe51 100644 --- a/src/Integration/TransactionIntegration.php +++ b/src/Integration/TransactionIntegration.php @@ -26,8 +26,6 @@ public function setupOnce(): void Scope::addGlobalEventProcessor(static function (Event $event, EventHint $hint): Event { $integration = SentrySdk::getClient()->getIntegration(self::class); - // The client bound to the current hub, if any, could not have this - // integration enabled. If this is the case, bail out if ($integration === null) { return $event; } diff --git a/src/SentrySdk.php b/src/SentrySdk.php index 3822d4a28..d01434fec 100644 --- a/src/SentrySdk.php +++ b/src/SentrySdk.php @@ -6,8 +6,6 @@ use Sentry\Logs\Logs; use Sentry\Metrics\TraceMetrics; -use Sentry\State\HubAdapter; -use Sentry\State\HubInterface; use Sentry\State\RuntimeContext; use Sentry\State\RuntimeContextManager; use Sentry\State\Scope; @@ -37,38 +35,15 @@ private function __construct() } /** - * Initializes the SDK by binding the client to the global scope and reset + * Initializes the SDK by binding the client to the global scope and resetting * the current local runtime state. */ - public static function init(?ClientInterface $client = null): HubInterface + public static function init(?ClientInterface $client = null): void { if ($client !== null) { self::getGlobalScope()->setClient($client); } self::$runtimeContextManager = new RuntimeContextManager(); - - return self::getCurrentHub(); - } - - public static function getCurrentHub(): HubInterface - { - return HubAdapter::getInstance(); - } - - /** - * Sets the current hub. - * - * If called while an explicit runtime context is active, the hub update is - * scoped to that active context only. Otherwise, it updates the baseline - * hub used by the global fallback context and future contexts. - * - * @param HubInterface $hub The hub to set - */ - public static function setCurrentHub(HubInterface $hub): HubInterface - { - self::getGlobalScope()->setClient($hub->getClient()); - - return $hub; } public static function getGlobalScope(): Scope diff --git a/src/State/Hub.php b/src/State/Hub.php deleted file mode 100644 index 82597e79c..000000000 --- a/src/State/Hub.php +++ /dev/null @@ -1,283 +0,0 @@ -stack[] = new Layer($client, $scope ?? new Scope()); - } - - /** - * {@inheritdoc} - */ - public function getClient(): ClientInterface - { - return $this->getStackTop()->getClient(); - } - - /** - * {@inheritdoc} - */ - public function getLastEventId(): ?EventId - { - return $this->lastEventId; - } - - /** - * {@inheritdoc} - */ - public function pushScope(): Scope - { - $clonedScope = clone $this->getScope(); - - $this->stack[] = new Layer($this->getClient(), $clonedScope); - - return $clonedScope; - } - - /** - * {@inheritdoc} - */ - public function popScope(): bool - { - if (\count($this->stack) === 1) { - return false; - } - - return array_pop($this->stack) !== null; - } - - /** - * {@inheritdoc} - */ - public function withScope(callable $callback) - { - $scope = $this->pushScope(); - - try { - return $callback($scope); - } finally { - $this->popScope(); - } - } - - /** - * {@inheritdoc} - */ - public function configureScope(callable $callback): void - { - $callback($this->getScope()); - } - - /** - * {@inheritdoc} - */ - public function bindClient(ClientInterface $client): void - { - $layer = $this->getStackTop(); - $layer->setClient($client); - } - - /** - * {@inheritdoc} - */ - public function captureMessage(string $message, ?Severity $level = null, ?EventHint $hint = null): ?EventId - { - return $this->lastEventId = $this->getClient()->captureMessage($message, $level, $this->getScope(), $hint); - } - - /** - * {@inheritdoc} - */ - public function captureException(\Throwable $exception, ?EventHint $hint = null): ?EventId - { - return $this->lastEventId = $this->getClient()->captureException($exception, $this->getScope(), $hint); - } - - /** - * {@inheritdoc} - */ - public function captureEvent(Event $event, ?EventHint $hint = null): ?EventId - { - return $this->lastEventId = $this->getClient()->captureEvent($event, $hint, $this->getScope()); - } - - /** - * {@inheritdoc} - */ - public function captureLastError(?EventHint $hint = null): ?EventId - { - return $this->lastEventId = $this->getClient()->captureLastError($this->getScope(), $hint); - } - - /** - * {@inheritdoc} - * - * @param int|float|null $duration - */ - public function captureCheckIn(string $slug, CheckInStatus $status, $duration = null, ?MonitorConfig $monitorConfig = null, ?string $checkInId = null): ?string - { - $client = $this->getClient(); - - if ($client instanceof NoOpClient) { - return null; - } - - $options = $client->getOptions(); - $event = Event::createCheckIn(); - $checkIn = new CheckIn( - $slug, - $status, - $checkInId, - $options->getRelease(), - $options->getEnvironment(), - $duration, - $monitorConfig - ); - $event->setCheckIn($checkIn); - $this->captureEvent($event); - - return $checkIn->getId(); - } - - /** - * {@inheritdoc} - */ - public function addBreadcrumb(Breadcrumb $breadcrumb): bool - { - $client = $this->getClient(); - - // No point in storing breadcrumbs if the client will never send them - if ($client instanceof NoOpClient) { - return false; - } - - $options = $client->getOptions(); - $beforeBreadcrumbCallback = $options->getBeforeBreadcrumbCallback(); - $maxBreadcrumbs = $options->getMaxBreadcrumbs(); - - if ($maxBreadcrumbs <= 0) { - return false; - } - - $breadcrumb = $beforeBreadcrumbCallback($breadcrumb); - - if ($breadcrumb !== null) { - $this->getScope()->addBreadcrumb($breadcrumb, $maxBreadcrumbs); - } - - return $breadcrumb !== null; - } - - public function addAttachment(Attachment $attachment): bool - { - // No point in storing attachments if the client will never send them - if ($this->getClient() instanceof NoOpClient) { - return false; - } - - $this->getScope()->addAttachment($attachment); - - return true; - } - - /** - * {@inheritdoc} - */ - public function getIntegration(string $className): ?IntegrationInterface - { - return $this->getClient()->getIntegration($className); - } - - /** - * {@inheritdoc} - * - * @param array $customSamplingContext Additional context that will be passed to the {@see SamplingContext} - */ - public function startTransaction(TransactionContext $context, array $customSamplingContext = []): Transaction - { - return TransactionSampler::startTransaction($this->getClient()->getOptions(), $context, $customSamplingContext); - } - - /** - * {@inheritdoc} - */ - public function getTransaction(): ?Transaction - { - return $this->getScope()->getTransaction(); - } - - /** - * {@inheritdoc} - */ - public function setSpan(?Span $span): HubInterface - { - $this->getScope()->setSpan($span); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getSpan(): ?Span - { - return $this->getScope()->getSpan(); - } - - /** - * Gets the scope bound to the top of the stack. - */ - private function getScope(): Scope - { - return $this->getStackTop()->getScope(); - } - - /** - * Gets the topmost client/layer pair in the stack. - */ - private function getStackTop(): Layer - { - return $this->stack[\count($this->stack) - 1]; - } -} diff --git a/src/State/HubAdapter.php b/src/State/HubAdapter.php deleted file mode 100644 index b376b4cd7..000000000 --- a/src/State/HubAdapter.php +++ /dev/null @@ -1,226 +0,0 @@ -getLastEventId(); - } - - /** - * {@inheritdoc} - */ - public function withScope(callable $callback) - { - return \Sentry\withIsolationScope($callback); - } - - /** - * {@inheritdoc} - */ - public function configureScope(callable $callback): void - { - $callback(SentrySdk::getIsolationScope()); - } - - /** - * {@inheritdoc} - */ - public function bindClient(ClientInterface $client): void - { - SentrySdk::getGlobalScope()->setClient($client); - } - - /** - * {@inheritdoc} - */ - public function captureMessage(string $message, ?Severity $level = null, ?EventHint $hint = null): ?EventId - { - return EventCapturer::captureMessage($message, $level, $hint); - } - - /** - * {@inheritdoc} - */ - public function captureException(\Throwable $exception, ?EventHint $hint = null): ?EventId - { - return EventCapturer::captureException($exception, $hint); - } - - /** - * {@inheritdoc} - */ - public function captureEvent(Event $event, ?EventHint $hint = null): ?EventId - { - return EventCapturer::captureEvent($event, $hint); - } - - /** - * {@inheritdoc} - */ - public function captureLastError(?EventHint $hint = null): ?EventId - { - return EventCapturer::captureLastError($hint); - } - - /** - * {@inheritdoc} - * - * @param int|float|null $duration - */ - public function captureCheckIn(string $slug, CheckInStatus $status, $duration = null, ?MonitorConfig $monitorConfig = null, ?string $checkInId = null): ?string - { - return EventCapturer::captureCheckIn($slug, $status, $duration, $monitorConfig, $checkInId); - } - - /** - * {@inheritdoc} - */ - public function addBreadcrumb(Breadcrumb $breadcrumb): bool - { - $scope = SentrySdk::getIsolationScope(); - - return BreadcrumbRecorder::record(SentrySdk::getClient($scope), $scope, $breadcrumb); - } - - /** - * {@inheritDoc} - */ - public function addAttachment(Attachment $attachment): bool - { - if (SentrySdk::getClient() instanceof NoOpClient) { - return false; - } - - SentrySdk::getIsolationScope()->addAttachment($attachment); - - return true; - } - - /** - * {@inheritdoc} - */ - public function getIntegration(string $className): ?IntegrationInterface - { - return SentrySdk::getClient()->getIntegration($className); - } - - /** - * {@inheritdoc} - */ - public function startTransaction(TransactionContext $context, array $customSamplingContext = []): Transaction - { - return TransactionSampler::startTransaction(SentrySdk::getClient()->getOptions(), $context, $customSamplingContext); - } - - /** - * {@inheritdoc} - */ - public function getTransaction(): ?Transaction - { - return SentrySdk::getIsolationScope()->getTransaction(); - } - - /** - * {@inheritdoc} - */ - public function getSpan(): ?Span - { - return SentrySdk::getIsolationScope()->getSpan(); - } - - /** - * {@inheritdoc} - */ - public function setSpan(?Span $span): HubInterface - { - SentrySdk::getIsolationScope()->setSpan($span); - - return $this; - } - - /** - * @see https://www.php.net/manual/en/language.oop5.cloning.php#object.clone - */ - public function __clone() - { - throw new \BadMethodCallException('Cloning is forbidden.'); - } - - /** - * @see https://www.php.net/manual/en/language.oop5.magic.php#object.wakeup - */ - public function __wakeup(): void - { - throw new \BadMethodCallException('Unserializing instances of this class is forbidden.'); - } - - /** - * @see https://www.php.net/manual/en/language.oop5.magic.php#object.sleep - */ - public function __sleep() - { - throw new \BadMethodCallException('Serializing instances of this class is forbidden.'); - } -} diff --git a/src/State/HubInterface.php b/src/State/HubInterface.php deleted file mode 100644 index c34ab7e43..000000000 --- a/src/State/HubInterface.php +++ /dev/null @@ -1,145 +0,0 @@ - $className - * - * @phpstan-return T|null - */ - public function getIntegration(string $className): ?IntegrationInterface; - - /** - * Starts a new `Transaction` and returns it. This is the entry point to manual - * tracing instrumentation. - * - * A tree structure can be built by adding child spans to the transaction, and - * child spans to other spans. To start a new child span within the transaction - * or any span, call the respective `startChild()` method. - * - * Every child span must be finished before the transaction is finished, - * otherwise the unfinished spans are discarded. - * - * The transaction must be finished with a call to its `finish()` method, at - * which point the transaction with all its finished child spans will be sent to - * Sentry. - * - * @param array $customSamplingContext Additional context that will be passed to the {@see SamplingContext} - */ - public function startTransaction(TransactionContext $context, array $customSamplingContext = []): Transaction; - - /** - * Returns the transaction that is on the Hub. - */ - public function getTransaction(): ?Transaction; - - /** - * Returns the span that is on the Hub. - */ - public function getSpan(): ?Span; - - /** - * Sets the span on the Hub. - */ - public function setSpan(?Span $span): HubInterface; - - /** - * Records a new attachment that will be attached to error and transaction events. - */ - public function addAttachment(Attachment $attachment): bool; -} diff --git a/src/State/Layer.php b/src/State/Layer.php deleted file mode 100644 index 3befbd80a..000000000 --- a/src/State/Layer.php +++ /dev/null @@ -1,82 +0,0 @@ -client = $client; - $this->scope = $scope; - } - - /** - * Gets the client held by this layer. - */ - public function getClient(): ClientInterface - { - return $this->client; - } - - /** - * Sets the client held by this layer. - * - * @param ClientInterface $client The client instance - * - * @return $this - */ - public function setClient(ClientInterface $client): self - { - $this->client = $client; - - return $this; - } - - /** - * Gets the scope held by this layer. - */ - public function getScope(): Scope - { - return $this->scope; - } - - /** - * Sets the scope held by this layer. - * - * @param Scope $scope The scope instance - * - * @return $this - */ - public function setScope(Scope $scope): self - { - $this->scope = $scope; - - return $this; - } -} diff --git a/tests/SentrySdkTest.php b/tests/SentrySdkTest.php index dcb705292..d72be230f 100644 --- a/tests/SentrySdkTest.php +++ b/tests/SentrySdkTest.php @@ -11,7 +11,6 @@ use Sentry\NoOpClient; use Sentry\Options; use Sentry\SentrySdk; -use Sentry\State\Hub; use Sentry\Tracing\Span; use Sentry\Tracing\SpanContext; use Sentry\Tracing\TransactionContext; @@ -22,32 +21,21 @@ final class SentrySdkTest extends TestCase { - public function testInit(): void + public function testInitResetsRuntimeContext(): void { - $hub1 = SentrySdk::init(); - $hub2 = SentrySdk::getCurrentHub(); + $previousScope = SentrySdk::getIsolationScope(); + $previousScope->setTag('runtime', 'old'); - $this->assertSame($hub1, $hub2); - $this->assertSame(SentrySdk::init(), SentrySdk::init()); - } - - public function testGetCurrentHub(): void - { SentrySdk::init(); - $hub2 = SentrySdk::getCurrentHub(); - $hub3 = SentrySdk::getCurrentHub(); + $currentScope = SentrySdk::getIsolationScope(); - $this->assertSame($hub2, $hub3); - } + $this->assertNotSame($previousScope, $currentScope); - public function testSetCurrentHub(): void - { - $client = $this->createMock(ClientInterface::class); - $hub = new Hub($client); + $event = $currentScope->applyToEvent(Event::createEvent()); - $this->assertSame($hub, SentrySdk::setCurrentHub($hub)); - $this->assertSame($client, SentrySdk::getClient()); + $this->assertNotNull($event); + $this->assertSame([], $event->getTags()); } public function testGetGlobalScope(): void diff --git a/tests/State/HubAdapterTest.php b/tests/State/HubAdapterTest.php deleted file mode 100644 index 149ba203f..000000000 --- a/tests/State/HubAdapterTest.php +++ /dev/null @@ -1,303 +0,0 @@ -assertSame(HubAdapter::getInstance(), HubAdapter::getInstance()); - } - - public function testGetInstanceReturnsUncloneableInstance(): void - { - $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Cloning is forbidden.'); - - clone HubAdapter::getInstance(); - } - - public function testHubAdapterThrowsExceptionOnSerialization(): void - { - $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Serializing instances of this class is forbidden.'); - - serialize(HubAdapter::getInstance()); - } - - public function testHubAdapterThrowsExceptionOnUnserialization(): void - { - $this->expectException(\BadMethodCallException::class); - $this->expectExceptionMessage('Unserializing instances of this class is forbidden.'); - - unserialize('O:23:"Sentry\State\HubAdapter":0:{}'); - } - - public function testGetClient(): void - { - $client = $this->createMock(ClientInterface::class); - - HubAdapter::getInstance()->bindClient($client); - - $this->assertSame($client, HubAdapter::getInstance()->getClient()); - } - - public function testGetLastEventId(): void - { - $event = Event::createEvent(); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureEvent') - ->willReturn($event->getId()); - - SentrySdk::getGlobalScope()->setClient($client); - - HubAdapter::getInstance()->captureEvent($event); - - $this->assertSame($event->getId(), HubAdapter::getInstance()->getLastEventId()); - } - - public function testWithScope(): void - { - $baseScope = SentrySdk::getIsolationScope(); - - $returnValue = HubAdapter::getInstance()->withScope(static function (Scope $scope): string { - $scope->setTag('nested', 'yes'); - - return 'foobarbaz'; - }); - - $this->assertSame('foobarbaz', $returnValue); - $this->assertSame($baseScope, SentrySdk::getIsolationScope()); - - $event = $baseScope->applyToEvent(Event::createEvent()); - $this->assertNotNull($event); - $this->assertArrayNotHasKey('nested', $event->getTags()); - } - - public function testConfigureScope(): void - { - HubAdapter::getInstance()->configureScope(static function (Scope $scope): void { - $scope->setTag('foo', 'bar'); - }); - - $event = SentrySdk::getIsolationScope()->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertSame(['foo' => 'bar'], $event->getTags()); - } - - /** - * @dataProvider captureMessageDataProvider - */ - public function testCaptureMessage(array $expectedFunctionCallArgs): void - { - $eventId = EventId::generate(); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureMessage') - ->with($expectedFunctionCallArgs[0], $expectedFunctionCallArgs[1], $this->isInstanceOf(Scope::class), $expectedFunctionCallArgs[2] ?? null) - ->willReturn($eventId); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertSame($eventId, HubAdapter::getInstance()->captureMessage(...$expectedFunctionCallArgs)); - } - - public static function captureMessageDataProvider(): \Generator - { - yield [['foo', Severity::debug()]]; - yield [['foo', Severity::debug(), new EventHint()]]; - } - - /** - * @dataProvider captureExceptionDataProvider - */ - public function testCaptureException(array $expectedFunctionCallArgs): void - { - $eventId = EventId::generate(); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureException') - ->with($expectedFunctionCallArgs[0], $this->isInstanceOf(Scope::class), $expectedFunctionCallArgs[1] ?? null) - ->willReturn($eventId); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertSame($eventId, HubAdapter::getInstance()->captureException(...$expectedFunctionCallArgs)); - } - - public static function captureExceptionDataProvider(): \Generator - { - yield [[new \Exception('foo')]]; - yield [[new \Exception('foo'), new EventHint()]]; - } - - public function testCaptureEvent(): void - { - $event = Event::createEvent(); - $hint = EventHint::fromArray([]); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureEvent') - ->with($event, $hint, $this->isInstanceOf(Scope::class)) - ->willReturn($event->getId()); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertSame($event->getId(), HubAdapter::getInstance()->captureEvent($event, $hint)); - } - - /** - * @dataProvider captureLastErrorDataProvider - */ - public function testCaptureLastError(array $expectedFunctionCallArgs): void - { - $eventId = EventId::generate(); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureLastError') - ->with($this->isInstanceOf(Scope::class), $expectedFunctionCallArgs[0] ?? null) - ->willReturn($eventId); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertSame($eventId, HubAdapter::getInstance()->captureLastError(...$expectedFunctionCallArgs)); - } - - public static function captureLastErrorDataProvider(): \Generator - { - yield [[]]; - yield [[new EventHint()]]; - } - - public function testCaptureCheckIn(): void - { - $checkInId = SentryUid::generate(); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'environment' => Event::DEFAULT_ENVIRONMENT, - 'release' => '1.1.8', - ])); - $client->expects($this->once()) - ->method('captureEvent') - ->with($this->callback(static function (Event $event) use ($checkInId): bool { - $checkIn = $event->getCheckIn(); - - return $checkIn !== null && $checkIn->getId() === $checkInId; - }), null, $this->isInstanceOf(Scope::class)); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertSame($checkInId, HubAdapter::getInstance()->captureCheckIn( - 'test-crontab', - CheckInStatus::ok(), - 10, - new MonitorConfig( - MonitorSchedule::crontab('*/5 * * * *'), - 5, - 30, - 'UTC' - ), - $checkInId - )); - } - - public function testAddBreadcrumb(): void - { - $breadcrumb = new Breadcrumb(Breadcrumb::LEVEL_DEBUG, Breadcrumb::TYPE_ERROR, 'user'); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options()); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertTrue(HubAdapter::getInstance()->addBreadcrumb($breadcrumb)); - } - - public function testGetIntegration(): void - { - $integration = $this->createMock(IntegrationInterface::class); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getIntegration') - ->with(\get_class($integration)) - ->willReturn($integration); - - SentrySdk::getGlobalScope()->setClient($client); - - $this->assertSame($integration, HubAdapter::getInstance()->getIntegration(\get_class($integration))); - } - - public function testStartTransaction(): void - { - $transactionContext = new TransactionContext('test-transaction'); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options()); - - SentrySdk::getGlobalScope()->setClient($client); - - $transaction = HubAdapter::getInstance()->startTransaction($transactionContext); - - $this->assertSame('test-transaction', $transaction->getName()); - } - - public function testGetTransaction(): void - { - $transaction = HubAdapter::getInstance()->startTransaction(new TransactionContext()); - SentrySdk::getIsolationScope()->setSpan($transaction); - - $this->assertSame($transaction, HubAdapter::getInstance()->getTransaction()); - } - - public function testGetSpan(): void - { - $span = new Span(); - SentrySdk::getIsolationScope()->setSpan($span); - - $this->assertSame($span, HubAdapter::getInstance()->getSpan()); - } - - public function testSetSpan(): void - { - $span = new Span(); - - $this->assertSame(HubAdapter::getInstance(), HubAdapter::getInstance()->setSpan($span)); - $this->assertSame($span, SentrySdk::getIsolationScope()->getSpan()); - } -} diff --git a/tests/State/HubTest.php b/tests/State/HubTest.php deleted file mode 100644 index 5e9bd9837..000000000 --- a/tests/State/HubTest.php +++ /dev/null @@ -1,1055 +0,0 @@ -createMock(ClientInterface::class); - $hub = new Hub($client); - - $this->assertSame($client, $hub->getClient()); - } - - public function testGetScope(): void - { - $callbackInvoked = false; - $scope = new Scope(); - $hub = new Hub($this->createMock(ClientInterface::class), $scope); - - $hub->configureScope(function (Scope $scopeArg) use (&$callbackInvoked, $scope) { - $this->assertSame($scope, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testGetLastEventId(): void - { - $eventId = EventId::generate(); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureMessage') - ->willReturn($eventId); - - $hub = new Hub($client); - - $this->assertNull($hub->getLastEventId()); - $this->assertSame($hub->captureMessage('foo'), $hub->getLastEventId()); - $this->assertSame($eventId, $hub->getLastEventId()); - } - - public function testPushScope(): void - { - /** @var ClientInterface&MockObject $client1 */ - $client1 = $this->createMock(ClientInterface::class); - $scope1 = new Scope(); - $hub = new Hub($client1, $scope1); - - $this->assertSame($client1, $hub->getClient()); - - $scope2 = $hub->pushScope(); - - $this->assertSame($client1, $hub->getClient()); - $this->assertNotSame($scope1, $scope2); - - $hub->configureScope(function (Scope $scopeArg) use (&$callbackInvoked, $scope2): void { - $this->assertSame($scope2, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testPopScope(): void - { - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $scope1 = new Scope(); - $hub = new Hub($client, $scope1); - - $this->assertFalse($hub->popScope()); - - $scope2 = $hub->pushScope(); - - $callbackInvoked = false; - - $hub->configureScope(function (Scope $scopeArg) use ($scope2, &$callbackInvoked): void { - $this->assertSame($scope2, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - $this->assertSame($client, $hub->getClient()); - - $this->assertTrue($hub->popScope()); - - $callbackInvoked = false; - - $hub->configureScope(function (Scope $scopeArg) use ($scope1, &$callbackInvoked): void { - $this->assertSame($scope1, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - $this->assertSame($client, $hub->getClient()); - - $this->assertFalse($hub->popScope()); - - $callbackInvoked = false; - - $hub->configureScope(function (Scope $scopeArg) use ($scope1, &$callbackInvoked): void { - $this->assertSame($scope1, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - $this->assertSame($client, $hub->getClient()); - } - - public function testWithScope(): void - { - $scope = new Scope(); - $hub = new Hub(new NoOpClient(), $scope); - - $callbackReturn = $hub->withScope(function (Scope $scopeArg) use ($scope): string { - $this->assertNotSame($scope, $scopeArg); - - return 'foobarbaz'; - }); - - $this->assertSame('foobarbaz', $callbackReturn); - - $callbackInvoked = false; - - $hub->configureScope(function (Scope $scopeArg) use (&$callbackInvoked, $scope): void { - $this->assertSame($scope, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testWithScopeWhenExceptionIsThrown(): void - { - $scope = new Scope(); - $hub = new Hub($this->createMock(ClientInterface::class), $scope); - $callbackInvoked = false; - - try { - $hub->withScope(function (Scope $scopeArg) use ($scope, &$callbackInvoked): void { - $this->assertNotSame($scope, $scopeArg); - - $callbackInvoked = true; - - // We throw to test that the scope is correctly popped form the - // stack regardless - throw new \RuntimeException(); - }); - } catch (\RuntimeException $exception) { - // Do nothing, we catch this exception to not make the test fail - } - - $this->assertTrue($callbackInvoked); - - $callbackInvoked = false; - - $hub->configureScope(function (Scope $scopeArg) use (&$callbackInvoked, $scope): void { - $this->assertSame($scope, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testConfigureScope(): void - { - $scope = new Scope(); - $hub = new Hub(new NoOpClient(), $scope); - - $callbackInvoked = false; - - $hub->configureScope(function (Scope $scopeArg) use ($scope, &$callbackInvoked): void { - $this->assertSame($scope, $scopeArg); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testBindClient(): void - { - /** @var ClientInterface&MockObject $client1 */ - $client1 = $this->createMock(ClientInterface::class); - - /** @var ClientInterface&MockObject $client2 */ - $client2 = $this->createMock(ClientInterface::class); - - $hub = new Hub($client1); - - $this->assertSame($client1, $hub->getClient()); - - $hub->bindClient($client2); - - $this->assertSame($client2, $hub->getClient()); - } - - /** - * @dataProvider captureMessageDataProvider - */ - public function testCaptureMessage(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void - { - $eventId = EventId::generate(); - $hub = new Hub(new NoOpClient()); - - $hub->configureScope(static function (Scope $scope) use ($propagationContext): void { - $scope->setPropagationContext($propagationContext); - }); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureMessage') - ->with(...$expectedFunctionCallArgs) - ->willReturn($eventId); - - $this->assertNull($hub->captureMessage('foo')); - - $hub->bindClient($client); - - $this->assertSame($eventId, $hub->captureMessage(...$functionCallArgs)); - } - - public static function captureMessageDataProvider(): \Generator - { - $propagationContext = PropagationContext::fromDefaults(); - - yield [ - [ - 'foo', - Severity::debug(), - ], - [ - 'foo', - Severity::debug(), - new Scope($propagationContext), - ], - $propagationContext, - ]; - - yield [ - [ - 'foo', - Severity::debug(), - new EventHint(), - ], - [ - 'foo', - Severity::debug(), - new Scope($propagationContext), - new EventHint(), - ], - $propagationContext, - ]; - } - - /** - * @dataProvider captureExceptionDataProvider - */ - public function testCaptureException(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void - { - $eventId = EventId::generate(); - $hub = new Hub(new NoOpClient()); - - $hub->configureScope(static function (Scope $scope) use ($propagationContext): void { - $scope->setPropagationContext($propagationContext); - }); - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureException') - ->with(...$expectedFunctionCallArgs) - ->willReturn($eventId); - - $this->assertNull($hub->captureException(new \RuntimeException())); - - $hub->bindClient($client); - - $this->assertSame($eventId, $hub->captureException(...$functionCallArgs)); - } - - public static function captureExceptionDataProvider(): \Generator - { - $propagationContext = PropagationContext::fromDefaults(); - - yield [ - [ - new \Exception('foo'), - ], - [ - new \Exception('foo'), - new Scope($propagationContext), - null, - ], - $propagationContext, - ]; - - yield [ - [ - new \Exception('foo'), - new EventHint(), - ], - [ - new \Exception('foo'), - new Scope($propagationContext), - new EventHint(), - ], - $propagationContext, - ]; - } - - /** - * @dataProvider captureLastErrorDataProvider - */ - public function testCaptureLastError(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void - { - $eventId = EventId::generate(); - $hub = new Hub(new NoOpClient()); - - $hub->configureScope(static function (Scope $scope) use ($propagationContext): void { - $scope->setPropagationContext($propagationContext); - }); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureLastError') - ->with(...$expectedFunctionCallArgs) - ->willReturn($eventId); - - $this->assertNull($hub->captureLastError(...$functionCallArgs)); - - $hub->bindClient($client); - - $this->assertSame($eventId, $hub->captureLastError(...$functionCallArgs)); - } - - public static function captureLastErrorDataProvider(): \Generator - { - $propagationContext = PropagationContext::fromDefaults(); - - yield [ - [], - [ - new Scope($propagationContext), - null, - ], - $propagationContext, - ]; - - yield [ - [ - new EventHint(), - ], - [ - new Scope($propagationContext), - new EventHint(), - ], - $propagationContext, - ]; - } - - public function testCaptureCheckIn(): void - { - $expectedCheckIn = new CheckIn( - 'test-crontab', - CheckInStatus::ok(), - SentryUid::generate(), - '0.0.1-dev', - Event::DEFAULT_ENVIRONMENT, - 10, - new MonitorConfig( - MonitorSchedule::crontab('*/5 * * * *'), - 5, - 30, - 'UTC' - ) - ); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'environment' => Event::DEFAULT_ENVIRONMENT, - 'release' => '0.0.1-dev', - ])); - - $client->expects($this->once()) - ->method('captureEvent') - ->with($this->callback(static function (Event $event) use ($expectedCheckIn): bool { - return $event->getCheckIn() == $expectedCheckIn; - })); - - $hub = new Hub($client); - - $this->assertSame($expectedCheckIn->getId(), $hub->captureCheckIn( - $expectedCheckIn->getMonitorSlug(), - $expectedCheckIn->getStatus(), - $expectedCheckIn->getDuration(), - $expectedCheckIn->getMonitorConfig(), - $expectedCheckIn->getId() - )); - } - - public function testCaptureEvent(): void - { - $hub = new Hub(new NoOpClient()); - $event = Event::createEvent(); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('captureEvent') - ->with($event) - ->willReturn($event->getId()); - - $this->assertNull($hub->captureEvent($event)); - - $hub->bindClient($client); - - $this->assertSame($event->getId(), $hub->captureEvent($event)); - } - - public function testAddBreadcrumb(): void - { - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options()); - - $callbackInvoked = false; - $hub = new Hub(new NoOpClient()); - $breadcrumb = new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting'); - - $hub->addBreadcrumb($breadcrumb); - $hub->configureScope(function (Scope $scope): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertEmpty($event->getBreadcrumbs()); - }); - - $hub->bindClient($client); - $hub->addBreadcrumb($breadcrumb); - $hub->configureScope(function (Scope $scope) use (&$callbackInvoked, $breadcrumb): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertSame([$breadcrumb], $event->getBreadcrumbs()); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testAddBreadcrumbDoesNothingIfMaxBreadcrumbsLimitIsZero(): void - { - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options(['max_breadcrumbs' => 0])); - - $hub = new Hub($client); - - $hub->addBreadcrumb(new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting')); - $hub->configureScope(function (Scope $scope): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertEmpty($event->getBreadcrumbs()); - }); - } - - public function testAddBreadcrumbRespectsMaxBreadcrumbsLimit(): void - { - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->any()) - ->method('getOptions') - ->willReturn(new Options(['max_breadcrumbs' => 2])); - - $hub = new Hub($client); - $breadcrumb1 = new Breadcrumb(Breadcrumb::LEVEL_WARNING, Breadcrumb::TYPE_ERROR, 'error_reporting', 'foo'); - $breadcrumb2 = new Breadcrumb(Breadcrumb::LEVEL_WARNING, Breadcrumb::TYPE_ERROR, 'error_reporting', 'bar'); - $breadcrumb3 = new Breadcrumb(Breadcrumb::LEVEL_WARNING, Breadcrumb::TYPE_ERROR, 'error_reporting', 'baz'); - - $hub->addBreadcrumb($breadcrumb1); - $hub->addBreadcrumb($breadcrumb2); - - $hub->configureScope(function (Scope $scope) use ($breadcrumb1, $breadcrumb2): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertSame([$breadcrumb1, $breadcrumb2], $event->getBreadcrumbs()); - }); - - $hub->addBreadcrumb($breadcrumb3); - - $hub->configureScope(function (Scope $scope) use ($breadcrumb2, $breadcrumb3): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertSame([$breadcrumb2, $breadcrumb3], $event->getBreadcrumbs()); - }); - } - - public function testAddBreadcrumbDoesNothingWhenBeforeBreadcrumbCallbackReturnsNull(): void - { - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'before_breadcrumb' => static function () { - return null; - }, - ])); - - $hub = new Hub($client); - - $hub->addBreadcrumb(new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting')); - $hub->configureScope(function (Scope $scope): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertEmpty($event->getBreadcrumbs()); - }); - } - - public function testAddBreadcrumbStoresBreadcrumbReturnedByBeforeBreadcrumbCallback(): void - { - $callbackInvoked = false; - $breadcrumb1 = new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting'); - $breadcrumb2 = new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting'); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'before_breadcrumb' => static function () use ($breadcrumb2): Breadcrumb { - return $breadcrumb2; - }, - ])); - - $hub = new Hub($client); - - $hub->addBreadcrumb($breadcrumb1); - $hub->configureScope(function (Scope $scope) use (&$callbackInvoked, $breadcrumb2): void { - $event = $scope->applyToEvent(Event::createEvent()); - - $this->assertNotNull($event); - $this->assertSame([$breadcrumb2], $event->getBreadcrumbs()); - - $callbackInvoked = true; - }); - - $this->assertTrue($callbackInvoked); - } - - public function testGetIntegration(): void - { - /** @var IntegrationInterface&MockObject $integration */ - $integration = $this->createMock(IntegrationInterface::class); - - /** @var ClientInterface&MockObject $client */ - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getIntegration') - ->with('Foo\\Bar') - ->willReturn($integration); - - $hub = new Hub(new NoOpClient()); - - $this->assertNull($hub->getIntegration('Foo\\Bar')); - - $hub->bindClient($client); - - $this->assertSame($integration, $hub->getIntegration('Foo\\Bar')); - } - - /** - * @dataProvider startTransactionDataProvider - */ - public function testStartTransactionWithTracesSampler(Options $options, TransactionContext $transactionContext, bool $expectedSampled): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn($options); - - $hub = new Hub($client); - $transaction = $hub->startTransaction($transactionContext); - - $this->assertSame($expectedSampled, $transaction->getSampled()); - } - - public function testStartTransactionIgnoresBaggageSampleRateWithoutSentryTrace(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 0.0, - ])); - - $hub = new Hub($client); - $transactionContext = TransactionContext::fromHeaders('', 'sentry-sample_rate=1'); - $transaction = $hub->startTransaction($transactionContext); - - $this->assertFalse($transaction->getSampled()); - } - - public static function startTransactionDataProvider(): iterable - { - yield 'Acceptable float value returned from traces_sampler' => [ - new Options([ - 'traces_sampler' => static function (): float { - return 1.0; - }, - ]), - new TransactionContext(), - true, - ]; - - yield 'Acceptable but too low float value returned from traces_sampler' => [ - new Options([ - 'traces_sampler' => static function (): float { - return 0.0; - }, - ]), - new TransactionContext(), - false, - ]; - - yield 'Acceptable integer value returned from traces_sampler' => [ - new Options([ - 'traces_sampler' => static function (): int { - return 1; - }, - ]), - new TransactionContext(), - true, - ]; - - yield 'Acceptable but too low integer value returned from traces_sampler' => [ - new Options([ - 'traces_sampler' => static function (): int { - return 0; - }, - ]), - new TransactionContext(), - false, - ]; - - yield 'Acceptable float value returned from traces_sample_rate' => [ - new Options([ - 'traces_sample_rate' => 1.0, - ]), - new TransactionContext(), - true, - ]; - - yield 'Acceptable but too low float value returned from traces_sample_rate' => [ - new Options([ - 'traces_sample_rate' => 0.0, - ]), - new TransactionContext(), - false, - ]; - - yield 'Acceptable integer value returned from traces_sample_rate' => [ - new Options([ - 'traces_sample_rate' => 1, - ]), - new TransactionContext(), - true, - ]; - - yield 'Acceptable but too low integer value returned from traces_sample_rate' => [ - new Options([ - 'traces_sample_rate' => 0, - ]), - new TransactionContext(), - false, - ]; - - yield 'Acceptable but too low value returned from traces_sample_rate which is preferred over sample_rate' => [ - new Options([ - 'sample_rate' => 1.0, - 'traces_sample_rate' => 0.0, - ]), - new TransactionContext(), - false, - ]; - - yield 'Acceptable value returned from traces_sample_rate which is preferred over sample_rate' => [ - new Options([ - 'sample_rate' => 0.0, - 'traces_sample_rate' => 1.0, - ]), - new TransactionContext(), - true, - ]; - - yield 'Acceptable value returned from SamplingContext::getParentSampled() which is preferred over traces_sample_rate (x1)' => [ - new Options([ - 'traces_sample_rate' => 0.5, - ]), - new TransactionContext(TransactionContext::DEFAULT_NAME, true), - true, - ]; - - yield 'Acceptable value returned from SamplingContext::getParentSampled() which is preferred over traces_sample_rate (x2)' => [ - new Options([ - 'traces_sample_rate' => 1.0, - ]), - new TransactionContext(TransactionContext::DEFAULT_NAME, false), - false, - ]; - - yield 'Invalid incoming sample_rand is ignored' => [ - new Options([ - 'traces_sample_rate' => 1.0, - ]), - TransactionContext::fromHeaders( - '566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8', - 'sentry-sample_rand=2.0' - ), - true, - ]; - - yield 'Out of range sample rate returned from traces_sampler (lower than minimum)' => [ - new Options([ - 'traces_sampler' => static function (): float { - return -1.0; - }, - ]), - new TransactionContext(TransactionContext::DEFAULT_NAME, false), - false, - ]; - - yield 'Out of range sample rate returned from traces_sampler (greater than maximum)' => [ - new Options([ - 'traces_sampler' => static function (): float { - return 1.1; - }, - ]), - new TransactionContext(TransactionContext::DEFAULT_NAME, false), - false, - ]; - - yield 'Invalid type returned from traces_sampler' => [ - new Options([ - 'traces_sampler' => static function (): string { - return 'foo'; - }, - ]), - new TransactionContext(TransactionContext::DEFAULT_NAME, false), - false, - ]; - } - - public function testStartTransactionDoesNothingIfTracingIsNotEnabled(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options()); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext()); - - $this->assertFalse($transaction->getSampled()); - } - - public function testStartTransactionWithCustomSamplingContext(): void - { - $customSamplingContext = ['a' => 'b']; - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sampler' => function (SamplingContext $samplingContext) use ($customSamplingContext): float { - $this->assertSame($samplingContext->getAdditionalContext(), $customSamplingContext); - - return 1.0; - }, - ])); - - $hub = new Hub($client); - $hub->startTransaction(new TransactionContext(), $customSamplingContext); - } - - public function testStartTransactionStartsProfilerWithProfilesSampler(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 1.0, - 'profiles_sampler' => static function (): float { - return 1.0; - }, - ])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext()); - - $this->assertTrue($transaction->getSampled()); - $this->assertNotNull($transaction->getProfiler()); - } - - public function testStartTransactionDoesNotStartProfilerWhenProfilesSamplerReturnsZero(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 1.0, - 'profiles_sampler' => static function (): float { - return 0.0; - }, - ])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext()); - - $this->assertTrue($transaction->getSampled()); - $this->assertNull($transaction->getProfiler()); - } - - public function testStartTransactionPrefersProfilesSamplerOverProfilesSampleRate(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 1.0, - 'profiles_sample_rate' => 1.0, - 'profiles_sampler' => static function (): float { - return 0.0; - }, - ])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext()); - - $this->assertTrue($transaction->getSampled()); - $this->assertNull($transaction->getProfiler()); - } - - public function testStartTransactionWithProfilesSamplerReceivesCustomSamplingContext(): void - { - $customSamplingContext = ['a' => 'b']; - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 1.0, - 'profiles_sampler' => function (SamplingContext $samplingContext) use ($customSamplingContext): float { - $this->assertSame($samplingContext->getAdditionalContext(), $customSamplingContext); - - return 0.0; - }, - ])); - - $hub = new Hub($client); - $hub->startTransaction(new TransactionContext(), $customSamplingContext); - } - - public function testStartTransactionDoesNotStartProfilerWhenProfilesSamplerReturnsInvalidValue(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 1.0, - 'profiles_sampler' => static function (): string { - return 'foo'; - }, - ])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext()); - - $this->assertTrue($transaction->getSampled()); - $this->assertNull($transaction->getProfiler()); - } - - public function testStartTransactionDoesNotCallProfilesSamplerWhenTransactionIsNotSampled(): void - { - $profilesSamplerInvoked = false; - - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sample_rate' => 0.0, - 'profiles_sampler' => static function () use (&$profilesSamplerInvoked): float { - $profilesSamplerInvoked = true; - - return 1.0; - }, - ])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext()); - - $this->assertFalse($transaction->getSampled()); - $this->assertFalse($profilesSamplerInvoked); - $this->assertNull($transaction->getProfiler()); - } - - public function testStartTransactionUpdatesTheDscSampleRate(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options([ - 'traces_sampler' => static function (SamplingContext $samplingContext): float { - return 1.0; - }, - ])); - - $hub = new Hub($client); - - $dsc = DynamicSamplingContext::fromHeader('sentry-trace_id=d49d9bf66f13450b81f65bc51cf49c03,sentry-public_key=public'); - $transactionMetaData = new TransactionMetadata(null, $dsc); - $transactionContext = new TransactionContext(TransactionContext::DEFAULT_NAME, null, $transactionMetaData); - - $transaction = $hub->startTransaction($transactionContext); - $this->assertSame('1', $transaction->getMetadata()->getDynamicSamplingContext()->get('sample_rate')); - } - - public function testGetTransactionReturnsInstanceSetOnTheScopeIfTransactionIsNotSampled(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options(['traces_sample_rate' => 1])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext(TransactionContext::DEFAULT_NAME, false)); - - $hub->configureScope(static function (Scope $scope) use ($transaction): void { - $scope->setSpan($transaction); - }); - - $this->assertSame($transaction, $hub->getTransaction()); - } - - public function testGetTransactionReturnsInstanceSetOnTheScopeIfTransactionIsSampled(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options(['traces_sample_rate' => 1])); - - $hub = new Hub($client); - $transaction = $hub->startTransaction(new TransactionContext(TransactionContext::DEFAULT_NAME, true)); - - $hub->configureScope(static function (Scope $scope) use ($transaction): void { - $scope->setSpan($transaction); - }); - - $this->assertSame($transaction, $hub->getTransaction()); - } - - public function testGetTransactionReturnsNullIfNoTransactionIsSetOnTheScope(): void - { - $client = $this->createMock(ClientInterface::class); - $client->expects($this->once()) - ->method('getOptions') - ->willReturn(new Options(['traces_sample_rate' => 1])); - - $hub = new Hub($client); - $hub->startTransaction(new TransactionContext(TransactionContext::DEFAULT_NAME, true)); - - $this->assertNull($hub->getTransaction()); - } - - public function testEventTraceContextIsAlwaysFilled(): void - { - $hub = new Hub(new NoOpClient()); - - $event = Event::createEvent(); - - $hub->configureScope(function (Scope $scope) use ($event): void { - $event = $scope->applyToEvent($event); - - $this->assertNotEmpty($event->getContexts()['trace']); - }); - } - - public function testEventTraceContextIsNotOverridenWhenPresent(): void - { - $hub = new Hub(new NoOpClient()); - - $traceContext = ['foo' => 'bar']; - - $event = Event::createEvent(); - $event->setContext('trace', $traceContext); - - $hub->configureScope(function (Scope $scope) use ($event, $traceContext): void { - $event = $scope->applyToEvent($event); - - $this->assertEquals($event->getContexts()['trace'], $traceContext); - }); - } -} diff --git a/tests/State/LayerTest.php b/tests/State/LayerTest.php deleted file mode 100644 index 501f0e579..000000000 --- a/tests/State/LayerTest.php +++ /dev/null @@ -1,37 +0,0 @@ -createMock(ClientInterface::class); - - /** @var ClientInterface|MockObject $client2 */ - $client2 = $this->createMock(ClientInterface::class); - - $scope1 = new Scope(); - $scope2 = new Scope(); - - $layer = new Layer($client1, $scope1); - - $this->assertSame($client1, $layer->getClient()); - $this->assertSame($scope1, $layer->getScope()); - - $layer->setClient($client2); - $layer->setScope($scope2); - - $this->assertSame($client2, $layer->getClient()); - $this->assertSame($scope2, $layer->getScope()); - } -}