diff --git a/src/Activity.php b/src/Activity.php index 9b9d069d..8bc210c8 100644 --- a/src/Activity.php +++ b/src/Activity.php @@ -17,7 +17,6 @@ use Temporal\DataConverter\Type; use Temporal\DataConverter\ValuesInterface; use Temporal\Exception\OutOfContextException; -use Temporal\Internal\Activity\ActivityContext; use Temporal\Internal\Support\Facade; final class Activity extends Facade @@ -29,10 +28,11 @@ final class Activity extends Facade public static function getCurrentContext(): ActivityContextInterface { $ctx = parent::getCurrentContext(); - /** @var ActivityContext $ctx */ - $ctx::class === ActivityContext::class or throw new OutOfContextException( - 'The Activity facade can only be used in the context of an activity execution.', - ); + if (!$ctx instanceof ActivityContextInterface) { + throw new OutOfContextException( + 'The Activity facade can only be used in the context of an activity execution.', + ); + } return $ctx; } diff --git a/src/Internal/Support/Facade.php b/src/Internal/Support/Facade.php index bf13a67e..07aaaa04 100644 --- a/src/Internal/Support/Facade.php +++ b/src/Internal/Support/Facade.php @@ -20,8 +20,7 @@ abstract class Facade */ private const ERROR_NO_CONTEXT = 'Calling facade methods can only be made ' . - 'from the currently running process' - ; + 'from the currently running process'; private static ?object $ctx = null; @@ -41,15 +40,8 @@ public static function setCurrentContext(?object $ctx): void self::$ctx = $ctx; } - /** - * @throws OutOfContextException - */ - public static function getCurrentContext(): object + public static function getCurrentContext(): ?object { - if (self::$ctx === null) { - throw new \RuntimeException(self::ERROR_NO_CONTEXT); - } - return self::$ctx; } @@ -58,11 +50,12 @@ public static function getCurrentContext(): object */ public static function getContextId(): int { - if (self::$ctx === null) { + $context = static::getCurrentContext(); + if ($context === null) { throw new \RuntimeException(self::ERROR_NO_CONTEXT); } - return \spl_object_id(self::$ctx); + return \spl_object_id($context); } /** diff --git a/src/Workflow.php b/src/Workflow.php index 06bbb4be..7ecff887 100644 --- a/src/Workflow.php +++ b/src/Workflow.php @@ -59,9 +59,11 @@ final class Workflow extends Facade public static function getCurrentContext(): WorkflowContextInterface { $ctx = parent::getCurrentContext(); - $ctx instanceof WorkflowContextInterface or throw new OutOfContextException( - 'The Workflow facade can be used only inside workflow code.', - ); + if (!$ctx instanceof WorkflowContextInterface) { + throw new OutOfContextException( + 'The Workflow facade can be used only inside workflow code.', + ); + } return $ctx; } diff --git a/tests/Unit/Internal/Support/ActivityFacadeTest.php b/tests/Unit/Internal/Support/ActivityFacadeTest.php new file mode 100644 index 00000000..6c45f029 --- /dev/null +++ b/tests/Unit/Internal/Support/ActivityFacadeTest.php @@ -0,0 +1,66 @@ + + */ + public static function outOfContextMethods(): iterable + { + yield 'getCurrentContext' => [ + static fn() => Activity::getCurrentContext(), + ]; + + yield 'getInfo' => [ + static fn() => Activity::getInfo(), + ]; + + yield 'getInput' => [ + static fn() => Activity::getInput(), + ]; + + yield 'hasHeartbeatDetails' => [ + static fn() => Activity::hasHeartbeatDetails(), + ]; + + yield 'getHeartbeatDetails' => [ + static fn() => Activity::getHeartbeatDetails(), + ]; + + yield 'getCancellationDetails' => [ + static fn() => Activity::getCancellationDetails(), + ]; + + yield 'doNotCompleteOnReturn' => [ + static fn() => Activity::doNotCompleteOnReturn(), + ]; + + yield 'heartbeat' => [ + static fn() => Activity::heartbeat('test'), + ]; + + yield 'getInstance' => [ + static fn() => Activity::getInstance(), + ]; + } + + #[Test] + #[DataProvider('outOfContextMethods')] + public function throwsOutOfContextException(callable $method): void + { + $this->expectException(OutOfContextException::class); + $this->expectExceptionMessage('The Activity facade can only be used in the context of an activity execution.'); + + $method(); + } +} diff --git a/tests/Unit/Internal/Support/WorkflowFacadeTest.php b/tests/Unit/Internal/Support/WorkflowFacadeTest.php new file mode 100644 index 00000000..259cdd09 --- /dev/null +++ b/tests/Unit/Internal/Support/WorkflowFacadeTest.php @@ -0,0 +1,193 @@ + + */ + public static function outOfContextMethods(): iterable + { + yield 'getCurrentContext' => [ + static fn() => Workflow::getCurrentContext(), + ]; + + yield 'now' => [ + static fn() => Workflow::now(), + ]; + + yield 'getInfo' => [ + static fn() => Workflow::getInfo(), + ]; + + yield 'getInput' => [ + static fn() => Workflow::getInput(), + ]; + + yield 'await' => [ + static fn() => Workflow::await(static fn() => true), + ]; + + yield 'awaitWithTimeout' => [ + static fn() => Workflow::awaitWithTimeout(1, static fn() => true), + ]; + + yield 'isReplaying' => [ + static fn() => Workflow::isReplaying(), + ]; + + yield 'getVersion' => [ + static fn() => Workflow::getVersion('test', Workflow::DEFAULT_VERSION, 1), + ]; + + yield 'timer' => [ + static fn() => Workflow::timer(1), + ]; + + yield 'executeChildWorkflow' => [ + static fn() => Workflow::executeChildWorkflow('Test'), + ]; + + yield 'newChildWorkflowStub' => [ + static fn() => Workflow::newChildWorkflowStub(\stdClass::class), + ]; + + yield 'newExternalWorkflowStub' => [ + static fn() => Workflow::newExternalWorkflowStub('test', new Workflow\WorkflowExecution()), + ]; + + yield 'continueAsNew' => [ + static fn() => Workflow::continueAsNew(''), + ]; + + yield 'async' => [ + static fn() => Workflow::async(static fn() => yield), + ]; + + yield 'asyncDetached' => [ + static fn() => Workflow::asyncDetached(static fn() => yield), + ]; + + yield 'newActivityStub' => [ + static fn() => Workflow::newActivityStub(\stdClass::class), + ]; + + yield 'newUntypedActivityStub' => [ + static fn() => Workflow::newUntypedActivityStub(), + ]; + + yield 'executeActivity' => [ + static fn() => Workflow::executeActivity('test'), + ]; + + yield 'getStackTrace' => [ + static fn() => Workflow::getStackTrace(), + ]; + + yield 'allHandlersFinished' => [ + static fn() => Workflow::allHandlersFinished(), + ]; + + yield 'upsertMemo' => [ + static fn() => Workflow::upsertMemo(['key' => 'value']), + ]; + + yield 'upsertSearchAttributes' => [ + static fn() => Workflow::upsertSearchAttributes(['key' => 'value']), + ]; + + yield 'uuid' => [ + static fn() => Workflow::uuid(), + ]; + + yield 'uuid4' => [ + static fn() => Workflow::uuid4(), + ]; + + yield 'uuid7' => [ + static fn() => Workflow::uuid7(), + ]; + + yield 'runLocked' => [ + static fn() => Workflow::runLocked(new \Temporal\Workflow\Mutex('test'), static fn() => yield), + ]; + + yield 'getLogger' => [ + static fn() => Workflow::getLogger(), + ]; + yield 'getInstance' => [ + static fn() => Workflow::getInstance(), + ]; + + yield 'getUpdateContext' => [ + static fn() => Workflow::getUpdateContext(), + ]; + + yield 'getLastCompletionResult' => [ + static fn() => Workflow::getLastCompletionResult(), + ]; + + yield 'registerQuery' => [ + static fn() => Workflow::registerQuery('test', static fn() => null), + ]; + + yield 'registerSignal' => [ + static fn() => Workflow::registerSignal('test', static fn() => null), + ]; + + yield 'registerDynamicSignal' => [ + static fn() => Workflow::registerDynamicSignal(static fn() => null), + ]; + + yield 'registerDynamicQuery' => [ + static fn() => Workflow::registerDynamicQuery(static fn() => null), + ]; + + yield 'registerDynamicUpdate' => [ + static fn() => Workflow::registerDynamicUpdate(static fn() => null), + ]; + + yield 'registerUpdate' => [ + static fn() => Workflow::registerUpdate('test', static fn() => null), + ]; + + yield 'sideEffect' => [ + static fn() => Workflow::sideEffect(static fn() => null), + ]; + + yield 'newContinueAsNewStub' => [ + static fn() => Workflow::newContinueAsNewStub(\stdClass::class), + ]; + + yield 'newUntypedChildWorkflowStub' => [ + static fn() => Workflow::newUntypedChildWorkflowStub('test'), + ]; + + yield 'newUntypedExternalWorkflowStub' => [ + static fn() => Workflow::newUntypedExternalWorkflowStub(new Workflow\WorkflowExecution()), + ]; + + yield 'upsertTypedSearchAttributes' => [ + static fn() => Workflow::upsertTypedSearchAttributes(), + ]; + } + + #[Test] + #[DataProvider('outOfContextMethods')] + public function throwsOutOfContextException(callable $method): void + { + $this->expectException(OutOfContextException::class); + $this->expectExceptionMessage('The Workflow facade can be used only inside workflow code.'); + + $method(); + } +}