From 3b0e51cb5c3842c28b4a9f904641a6e2d23304eb Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 4 Oct 2023 23:39:46 +0400 Subject: [PATCH 1/2] Add test case where workflow fails with react/promise v3 --- .../Workflow/CancelSignaledChildWorkflow.php | 66 +++++++++++++++++++ .../src/Workflow/SimpleSignaledWorkflow.php | 37 +++++++++++ tests/Functional/SimpleWorkflowTestCase.php | 39 +++++++++++ 3 files changed, 142 insertions(+) create mode 100644 tests/Fixtures/src/Workflow/CancelSignaledChildWorkflow.php create mode 100644 tests/Fixtures/src/Workflow/SimpleSignaledWorkflow.php diff --git a/tests/Fixtures/src/Workflow/CancelSignaledChildWorkflow.php b/tests/Fixtures/src/Workflow/CancelSignaledChildWorkflow.php new file mode 100644 index 00000000..9b6df12f --- /dev/null +++ b/tests/Fixtures/src/Workflow/CancelSignaledChildWorkflow.php @@ -0,0 +1,66 @@ +status; + } + + #[WorkflowMethod(name: 'CancelSignaledChildWorkflow')] + public function handler() + { + // typed stub + $simple = Workflow::newChildWorkflowStub(SimpleSignaledWorkflow::class); + + $waitSignaled = new Deferred(); + + $this->status[] = 'start'; + + // start execution + $scope = Workflow::async( + function () use ($simple, $waitSignaled) { + $call = $simple->handler(); + $this->status[] = 'child started'; + + yield $simple->add(8); + $this->status[] = 'child signaled'; + $waitSignaled->resolve(); + + return yield $call; + } + ); + + // only cancel scope when signal dispatched + yield $waitSignaled; + $scope->cancel(); + $this->status[] = 'scope canceled'; + + try { + return yield $scope; + } catch (\Throwable $e) { + $this->status[] = 'process done'; + + return 'canceled ok'; + } + } +} diff --git a/tests/Fixtures/src/Workflow/SimpleSignaledWorkflow.php b/tests/Fixtures/src/Workflow/SimpleSignaledWorkflow.php new file mode 100644 index 00000000..0e7a9586 --- /dev/null +++ b/tests/Fixtures/src/Workflow/SimpleSignaledWorkflow.php @@ -0,0 +1,37 @@ +counter += $value; + } + + #[WorkflowMethod(name: 'SimpleSignaledWorkflow')] + public function handler(): iterable + { + // collect signals during one second + yield Workflow::timer(1); + + return $this->counter; + } +} diff --git a/tests/Functional/SimpleWorkflowTestCase.php b/tests/Functional/SimpleWorkflowTestCase.php index 64faf138..c32a7890 100644 --- a/tests/Functional/SimpleWorkflowTestCase.php +++ b/tests/Functional/SimpleWorkflowTestCase.php @@ -4,12 +4,14 @@ namespace Temporal\Tests\Functional; +use Temporal\Api\Enums\V1\EventType; use Temporal\Client\GRPC\ServiceClient; use Temporal\Client\WorkflowClient; use Temporal\Client\WorkflowOptions; use Temporal\Testing\ActivityMocker; use Temporal\Tests\TestCase; use Temporal\Tests\Workflow\SimpleWorkflow; +use Temporal\Workflow\WorkflowExecution; final class SimpleWorkflowTestCase extends TestCase { @@ -47,4 +49,41 @@ public function testEagerStartDoesntFail(): void $run = $this->workflowClient->start($workflow, 'hello'); $this->assertSame('HELLO', $run->getResult('string')); } + + public function testCancelSignaledChildWorkflow(): void + { + $workflow = $this->workflowClient + ->newWorkflowStub(\Temporal\Tests\Workflow\CancelSignaledChildWorkflow::class); + $run = $this->workflowClient->start($workflow); + + $this->assertSame('canceled ok', $run->getResult('string', 10)); + + $status = $workflow->getStatus(); + $this->assertSame([ + "start", + "child started", + "child signaled", + "scope canceled", + "process done", + ], $status); + + $this->assertContainsEvent( + $run->getExecution(), + EventType::EVENT_TYPE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED, + ); + } + + private function assertContainsEvent(WorkflowExecution $execution, int $event): void + { + $history = $this->workflowClient->getWorkflowHistory( + $execution, + ); + foreach ($history as $item) { + if ($item->getEventType() === $event) { + return; + } + } + + throw new \Exception(\sprintf('Event %s not found', EventType::value($event))); + } } From 20b0212d4496d25df547e9d144336336f2486197 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 4 Oct 2023 23:51:41 +0400 Subject: [PATCH 2/2] Add more tests from roadrunner-temporal package --- tests/Functional/Client/TypedStubTestCase.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/Functional/Client/TypedStubTestCase.php b/tests/Functional/Client/TypedStubTestCase.php index d1a9366d..cfe166a6 100644 --- a/tests/Functional/Client/TypedStubTestCase.php +++ b/tests/Functional/Client/TypedStubTestCase.php @@ -11,6 +11,7 @@ namespace Temporal\Tests\Functional\Client; +use Temporal\Exception\Client\WorkflowQueryException; use Temporal\Exception\InvalidArgumentException; use Temporal\Tests\DTO\Message; use Temporal\Tests\DTO\User; @@ -90,6 +91,26 @@ public function testQueryWorkflow() $this->assertSame(88, $e->getResult()); } + public function testQueryNotExistingMethod() + { + $client = $this->createClient(); + $simple = $client->newUntypedWorkflowStub('QueryWorkflow'); + + + $e = $client->start($simple, 'Hello World'); + $this->assertNotEmpty($e->getExecution()->getID()); + $this->assertNotEmpty($e->getExecution()->getRunID()); + + try { + $simple->query('error', -1); + } catch (WorkflowQueryException $e) { + $this->assertStringContainsString('KnownQueryTypes=[get]', $e->getPrevious()->getMessage()); + return; + } + + $this->fail('Expected exception to be thrown'); + } + public function testGetDTOResult() { $w = $this->createClient();