diff --git a/resources/client.meta-storm.xml b/resources/client.meta-storm.xml
index a3a54236a..b51398e0c 100644
--- a/resources/client.meta-storm.xml
+++ b/resources/client.meta-storm.xml
@@ -29,7 +29,24 @@
argument="0"
>
+
+
+
+
+
+
+
+
+
diff --git a/tests/.meta-storm.xml b/tests/.meta-storm.xml
new file mode 100644
index 000000000..b1000d2e3
--- /dev/null
+++ b/tests/.meta-storm.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/tests/Acceptance/App/Attribute/RetryOptions.php b/tests/Acceptance/App/Attribute/RetryOptions.php
index ea5ace526..a13f01450 100644
--- a/tests/Acceptance/App/Attribute/RetryOptions.php
+++ b/tests/Acceptance/App/Attribute/RetryOptions.php
@@ -25,10 +25,25 @@ class RetryOptions
* @param int<0, max> $maximumAttempts
*/
public function __construct(
+ /**
+ * @see CommonOptions::withInitialInterval()
+ */
public ?string $initialInterval = CommonOptions::DEFAULT_INITIAL_INTERVAL,
+ /**
+ * @see CommonOptions::withBackoffCoefficient()
+ */
public float $backoffCoefficient = CommonOptions::DEFAULT_BACKOFF_COEFFICIENT,
+ /**
+ * @see CommonOptions::withMaximumInterval()
+ */
public ?string $maximumInterval = CommonOptions::DEFAULT_MAXIMUM_INTERVAL,
+ /**
+ * @see CommonOptions::withMaximumAttempts()
+ */
public int $maximumAttempts = CommonOptions::DEFAULT_MAXIMUM_ATTEMPTS,
+ /**
+ * @see CommonOptions::withNonRetryableExceptions()
+ */
public array $nonRetryableExceptions = CommonOptions::DEFAULT_NON_RETRYABLE_EXCEPTIONS,
) {}
diff --git a/tests/Acceptance/App/Attribute/Stub.php b/tests/Acceptance/App/Attribute/Stub.php
index 1fc2aa295..602467f58 100644
--- a/tests/Acceptance/App/Attribute/Stub.php
+++ b/tests/Acceptance/App/Attribute/Stub.php
@@ -23,11 +23,26 @@ final class Stub
*/
public function __construct(
public string $type,
+ /**
+ * @see WorkflowOptions::withEagerStart()
+ */
public bool $eagerStart = false,
+ /**
+ * @see WorkflowOptions::withWorkflowId()
+ */
public ?string $workflowId = null,
+ /**
+ * @see WorkflowOptions::withWorkflowExecutionTimeout()
+ */
public ?string $executionTimeout = null,
public array $args = [],
+ /**
+ * @see WorkflowOptions::withMemo()
+ */
public array $memo = [],
+ /**
+ * @see WorkflowOptions::withRetryOptions()
+ */
?RetryOptions $retryOptions = null,
) {
$this->retryOptions = $retryOptions?->toRetryOptions();
diff --git a/tests/Acceptance/Harness/Activity/BasicTest.php b/tests/Acceptance/Harness/Activity/BasicTest.php
index dfbd947a6..0bc166157 100644
--- a/tests/Acceptance/Harness/Activity/BasicTest.php
+++ b/tests/Acceptance/Harness/Activity/BasicTest.php
@@ -15,26 +15,25 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-/*
-
-# Basic activity
-
-The most basic workflow which just runs an activity and returns its result.
-Importantly, without setting a workflow execution timeout.
-
-# Detailed spec
-
-It's important that the workflow execution timeout is not set here, because server will propagate that to all un-set
-activity timeouts. We had a bug where TS would crash (after proto changes from gogo to google) because it was expecting
-timeouts to be set to zero rather than null.
-
-*/
-
+/**
+ * # Basic activity
+ *
+ * The most basic workflow which just runs an activity and returns its result.
+ * Importantly, without setting a workflow execution timeout.
+ *
+ * # Detailed spec
+ *
+ * It's important that the workflow execution timeout is not set here, because server will propagate that to all un-set
+ * activity timeouts. We had a bug where TS would crash (after proto changes from gogo to google) because it was expecting
+ * timeouts to be set to zero rather than null.
+ */
class BasicTest extends TestCase
{
#[Test]
- public static function check(#[Stub('Harness_Activity_Basic')]WorkflowStubInterface $stub): void
- {
+ public static function check(
+ #[Stub('Harness_Activity_Basic')]
+ WorkflowStubInterface $stub,
+ ): void {
self::assertSame('echo', $stub->getResult());
}
}
diff --git a/tests/Acceptance/Harness/Activity/CancelTryCancelTest.php b/tests/Acceptance/Harness/Activity/CancelTryCancelTest.php
index 1f729401d..853aa9a1c 100644
--- a/tests/Acceptance/Harness/Activity/CancelTryCancelTest.php
+++ b/tests/Acceptance/Harness/Activity/CancelTryCancelTest.php
@@ -5,7 +5,6 @@
namespace Temporal\Tests\Acceptance\Harness\Activity\CancelTryCancel;
use PHPUnit\Framework\Attributes\Test;
-use React\Promise\PromiseInterface;
use Temporal\Activity;
use Temporal\Activity\ActivityInterface;
use Temporal\Activity\ActivityMethod;
@@ -21,41 +20,41 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-/*
-
-# Activity cancellation - Try Cancel mode
-Activities may be cancelled in three different ways, this feature spec covers the
-Try Cancel mode.
-
-Each feature workflow in this folder should start an activity and cancel it
-using the Try Cancel mode. The implementation should demonstrate that the activity
-keeps receives a cancel request after the workflow has issued it, but the workflow
-immediately should proceed with the activity result being cancelled.
-
-## Detailed spec
-
-* When the SDK issues the activity cancel request command, server will write an
- activity cancel requested event to history
-* The workflow immediately resolves the activity with its result being cancelled
-* Server will notify the activity cancellation has been requested via a response
- to activity heartbeating
-* The activity may ignore the cancellation request if it explicitly chooses to
-
-## Feature implementation
-
-* Execute activity that heartbeats and checks cancellation
- * If a minute passes without cancellation, send workflow a signal that it timed out
- * If cancellation is received, send workflow a signal that it was cancelled
-* Cancel activity and confirm cancellation error is returned
-* Check in the workflow that the signal sent from the activity is showing it was cancelled
-
-*/
-
+/**
+ * # Activity cancellation - Try Cancel mode
+ *
+ * Activities may be cancelled in three different ways, this feature spec covers the
+ * Try Cancel mode.
+ *
+ * Each feature workflow in this folder should start an activity and cancel it
+ * using the Try Cancel mode. The implementation should demonstrate that the activity
+ * keeps receives a cancel request after the workflow has issued it, but the workflow
+ * immediately should proceed with the activity result being cancelled.
+ *
+ * ## Detailed spec
+ *
+ * When the SDK issues the activity cancel request command, server will write an
+ * activity cancel requested event to history
+ * The workflow immediately resolves the activity with its result being cancelled
+ * Server will notify the activity cancellation has been requested via a response
+ * to activity heartbeating
+ * The activity may ignore the cancellation request if it explicitly chooses to
+ *
+ * ## Feature implementation
+ *
+ * Execute activity that heartbeats and checks cancellation
+ * If a minute passes without cancellation, send workflow a signal that it timed out
+ * If cancellation is received, send workflow a signal that it was cancelled
+ * Cancel activity and confirm cancellation error is returned
+ * Check in the workflow that the signal sent from the activity is showing it was cancelled
+ */
class CancelTryCancelTest extends TestCase
{
#[Test]
- public static function check(#[Stub('Harness_Activity_CancelTryCancel')]WorkflowStubInterface $stub): void
- {
+ public static function check(
+ #[Stub('Harness_Activity_CancelTryCancel')]
+ WorkflowStubInterface $stub,
+ ): void {
self::assertSame('cancelled', $stub->getResult(timeout: 10));
}
}
@@ -76,7 +75,7 @@ public function run()
->withHeartbeatTimeout('5 seconds')
# Disable retry
->withRetryOptions(RetryOptions::new()->withMaximumAttempts(1))
- ->withCancellationType(Activity\ActivityCancellationType::TryCancel)
+ ->withCancellationType(Activity\ActivityCancellationType::TryCancel),
);
$scope = Workflow::async(static fn() => $activity->cancellableActivity());
@@ -98,7 +97,7 @@ public function run()
}
#[Workflow\SignalMethod('activity_result')]
- public function activityResult(string $result)
+ public function activityResult(string $result): void
{
$this->result = $result;
}
@@ -109,14 +108,10 @@ class FeatureActivity
{
public function __construct(
private readonly WorkflowClientInterface $client,
- ) {
- }
+ ) {}
- /**
- * @return PromiseInterface
- */
#[ActivityMethod('cancellable_activity')]
- public function cancellableActivity()
+ public function cancellableActivity(): void
{
# Heartbeat every second for a minute
$result = 'timeout';
diff --git a/tests/Acceptance/Harness/Activity/RetryOnErrorTest.php b/tests/Acceptance/Harness/Activity/RetryOnErrorTest.php
index 9c6f514b5..574649007 100644
--- a/tests/Acceptance/Harness/Activity/RetryOnErrorTest.php
+++ b/tests/Acceptance/Harness/Activity/RetryOnErrorTest.php
@@ -20,37 +20,38 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-/*
-
-# Retrying activities on error
-
-Failed activities can retry in a number of ways. This is configurable by retry policies that govern if and
-how a failed activity may retry.
-
-## Feature implementation
-
-* Workflow executes activity with 5 max attempts and low backoff
-* Activity errors every time with the attempt that failed
-* Workflow waits on activity and re-bubbles its same error
-* Confirm the right attempt error message is present
-
-*/
-
+/**
+ * # Retrying activities on error
+ *
+ * Failed activities can retry in a number of ways. This is configurable by retry policies that govern if and
+ * how a failed activity may retry.
+ *
+ * ## Feature implementation
+ *
+ * Workflow executes activity with 5 max attempts and low backoff
+ * Activity errors every time with the attempt that failed
+ * Workflow waits on activity and re-bubbles its same error
+ * Confirm the right attempt error message is present
+ */
class RetryOnErrorTest extends TestCase
{
#[Test]
- public static function check(#[Stub('Harness_Activity_CancelTryCancel')]WorkflowStubInterface $stub): void
- {
+ public static function check(
+ #[Stub('Harness_Activity_CancelTryCancel')]
+ WorkflowStubInterface $stub,
+ ): void {
try {
$stub->getResult();
- throw new \Exception('Expected WorkflowFailedException');
} catch (WorkflowFailedException $e) {
self::assertInstanceOf(ActivityFailure::class, $e->getPrevious());
/** @var ActivityFailure $failure */
$failure = $e->getPrevious()->getPrevious();
self::assertInstanceOf(ApplicationFailure::class, $failure);
self::assertStringContainsStringIgnoringCase('activity attempt 5 failed', $failure->getOriginalMessage());
+ return;
}
+
+ throw new \Exception('Expected getResult() produced WorkflowFailedException');
}
}
@@ -58,7 +59,7 @@ public static function check(#[Stub('Harness_Activity_CancelTryCancel')]Workflow
class FeatureWorkflow
{
#[WorkflowMethod('Harness_Activity_CancelTryCancel')]
- public function run()
+ public function run(): iterable
{
# Allow 4 retries with basically no backoff
yield Workflow::newActivityStub(
@@ -71,7 +72,7 @@ public function run()
# Do not increase retry backoff each time
->withBackoffCoefficient(1)
# 5 total maximum attempts
- ->withMaximumAttempts(5)
+ ->withMaximumAttempts(5),
),
)->alwaysFailActivity();
}
diff --git a/tests/Acceptance/Harness/ContinueAsNew/ContinueAsSameTest.php b/tests/Acceptance/Harness/ContinueAsNew/ContinueAsSameTest.php
index f9b8e60da..2c2acc2be 100644
--- a/tests/Acceptance/Harness/ContinueAsNew/ContinueAsSameTest.php
+++ b/tests/Acceptance/Harness/ContinueAsNew/ContinueAsSameTest.php
@@ -12,29 +12,33 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-\define('INPUT_DATA', 'InputData');
-\define('MEMO_KEY', 'MemoKey');
-\define('MEMO_VALUE', 'MemoValue');
-\define('WORKFLOW_ID', 'TestID');
-
+/**
+ * # Continues workflow execution
+ */
class ContinueAsSameTest extends TestCase
{
+ private const INPUT_DATA = 'InputData';
+ private const MEMO_KEY = 'MemoKey';
+ private const MEMO_VALUE = 'MemoValue';
+ private const WORKFLOW_ID = 'TestID';
+
#[Test]
public static function check(
#[Stub(
type: 'Harness_ContinueAsNew_ContinueAsSame',
- workflowId: WORKFLOW_ID,
- args: [INPUT_DATA],
- memo: [MEMO_KEY => MEMO_VALUE],
+ workflowId: self::WORKFLOW_ID,
+ args: [self::INPUT_DATA],
+ memo: [self::MEMO_KEY => self::MEMO_VALUE],
)]
WorkflowStubInterface $stub,
): void {
- self::assertSame(INPUT_DATA, $stub->getResult());
+ self::assertSame(self::INPUT_DATA, $stub->getResult());
# Workflow ID does not change after continue as new
- self::assertSame(WORKFLOW_ID, $stub->getExecution()->getID());
+ self::assertSame(self::WORKFLOW_ID, $stub->getExecution()->getID());
# Memos do not change after continue as new
$description = $stub->describe();
- self::assertSame([MEMO_KEY => MEMO_VALUE], $description->info->memo->getValues());
+ self::assertSame(5, $description->info->historyLength);
+ self::assertSame([self::MEMO_KEY => self::MEMO_VALUE], $description->info->memo->getValues());
}
}
@@ -42,7 +46,7 @@ public static function check(
class FeatureWorkflow
{
#[WorkflowMethod('Harness_ContinueAsNew_ContinueAsSame')]
- public function run(string $input)
+ public function run(string $input): iterable
{
if (!empty(Workflow::getInfo()->continuedExecutionRunId)) {
return $input;
diff --git a/tests/Acceptance/Harness/DataConverter/BinaryProtobufTest.php b/tests/Acceptance/Harness/DataConverter/BinaryProtobufTest.php
index a186088e7..fcea23ea4 100644
--- a/tests/Acceptance/Harness/DataConverter/BinaryProtobufTest.php
+++ b/tests/Acceptance/Harness/DataConverter/BinaryProtobufTest.php
@@ -20,41 +20,33 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-const EXPECTED_RESULT = 0xDEADBEEF;
-\define(__NAMESPACE__ . '\INPUT', (new DataBlob())->setData(EXPECTED_RESULT));
-
+/**
+ * # Binary Protobuf Payload Encoding
+ *
+ * Test that binary protobuf payload encoding works well.
+ */
class BinaryProtobufTest extends TestCase
{
- private GrpcCallInterceptor $interceptor;
+ private const EXPECTED_RESULT = 0xDEADBEEF;
- protected function setUp(): void
- {
- $this->interceptor = new GrpcCallInterceptor();
- parent::setUp();
- }
-
- public function pipelineProvider(): PipelineProvider
- {
- return new SimplePipelineProvider([$this->interceptor]);
- }
+ private GrpcCallInterceptor $interceptor;
#[Test]
public function check(
- #[Stub('Harness_DataConverter_BinaryProtobuf', args: [INPUT])]
+ #[Stub('Harness_DataConverter_BinaryProtobuf', args: [new DataBlob(['data' => self::EXPECTED_RESULT])])]
#[Client(
pipelineProvider: [self::class, 'pipelineProvider'],
payloadConverters: [ProtoConverter::class],
)]
WorkflowStubInterface $stub,
): void {
- /** @var DataBlob $result */
$result = $stub->getResult(DataBlob::class);
# Check that binary protobuf message was decoded in the Workflow and sent back.
# But we don't check the result Payload encoding, because we can't configure different Payload encoders
# on the server side for different Harness features.
# There `json/protobuf` converter is used for protobuf messages by default on the server side.
- self::assertEquals(EXPECTED_RESULT, $result->getData());
+ self::assertEquals(self::EXPECTED_RESULT, $result->getData());
# Check arguments
self::assertNotNull($this->interceptor->startRequest);
@@ -65,13 +57,24 @@ public function check(
self::assertSame('binary/protobuf', $payload->getMetadata()['encoding']);
self::assertSame('temporal.api.common.v1.DataBlob', $payload->getMetadata()['messageType']);
}
+
+ public function pipelineProvider(): PipelineProvider
+ {
+ return new SimplePipelineProvider([$this->interceptor]);
+ }
+
+ protected function setUp(): void
+ {
+ $this->interceptor = new GrpcCallInterceptor();
+ parent::setUp();
+ }
}
#[WorkflowInterface]
class FeatureWorkflow
{
#[WorkflowMethod('Harness_DataConverter_BinaryProtobuf')]
- public function run(DataBlob $data)
+ public function run(DataBlob $data): DataBlob
{
return $data;
}
diff --git a/tests/Acceptance/Harness/DataConverter/BinaryTest.php b/tests/Acceptance/Harness/DataConverter/BinaryTest.php
index 2324e57bc..974f32035 100644
--- a/tests/Acceptance/Harness/DataConverter/BinaryTest.php
+++ b/tests/Acceptance/Harness/DataConverter/BinaryTest.php
@@ -23,39 +23,32 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-/*
-# Binary payload converter
-
-Binary values can be converted to and from `binary/plain` Payloads.
-
-Steps:
-
-- run a echo workflow that accepts and returns binary value `0xdeadbeef`
-- verify client result is binary `0xdeadbeef`
-- get result payload of WorkflowExecutionCompleted event from workflow history
-- load JSON payload from `./payload.json` and compare it to result payload
-- get argument payload of WorkflowExecutionStarted event from workflow history
-- verify that argument and result payloads are the same
-
-
-# Detailed spec
-
-`metadata.encoding = toBinary("binary/plain")`
-*/
-
-const CODEC_ENCODING = 'binary/plain';
-\define(__NAMESPACE__ . '\EXPECTED_RESULT', (string)0xDEADBEEF);
-\define(__NAMESPACE__ . '\INPUT', new Bytes(EXPECTED_RESULT));
+/**
+ * # Binary payload converter
+ *
+ * Binary values can be converted to and from `binary/plain` Payloads.
+ *
+ * Steps:
+ *
+ * - run a echo workflow that accepts and returns binary value `0xdeadbeef`
+ * - verify client result is binary `0xdeadbeef`
+ * - get result payload of WorkflowExecutionCompleted event from workflow history
+ * - load JSON payload from `./payload.json` and compare it to result payload
+ * - get argument payload of WorkflowExecutionStarted event from workflow history
+ * - verify that argument and result payloads are the same
+ *
+ *
+ * # Detailed spec
+ *
+ * `metadata.encoding = toBinary("binary/plain")`
+ */
class BinaryTest extends TestCase
{
- private Interceptor $interceptor;
+ private const EXPECTED_RESULT = "" . 0xDEADBEEF;
+ private const CODEC_ENCODING = 'binary/plain';
- protected function setUp(): void
- {
- $this->interceptor = new Interceptor();
- parent::setUp();
- }
+ private Interceptor $interceptor;
public function pipelineProvider(): PipelineProvider
{
@@ -64,14 +57,14 @@ public function pipelineProvider(): PipelineProvider
#[Test]
public function check(
- #[Stub('Harness_DataConverter_Binary', args: [INPUT])]
+ #[Stub('Harness_DataConverter_Binary', args: [new Bytes(self::EXPECTED_RESULT)])]
#[Client(pipelineProvider: [self::class, 'pipelineProvider'])]
WorkflowStubInterface $stub,
): void {
/** @var Bytes $result */
$result = $stub->getResult(Bytes::class);
- self::assertEquals(EXPECTED_RESULT, $result->getData());
+ self::assertEquals(self::EXPECTED_RESULT, $result->getData());
# Check arguments
self::assertNotNull($this->interceptor->startRequest);
@@ -81,12 +74,18 @@ public function check(
$payload = $this->interceptor->startRequest->getInput()?->getPayloads()[0] ?? null;
self::assertNotNull($payload);
- self::assertSame(CODEC_ENCODING, $payload->getMetadata()['encoding']);
+ self::assertSame(self::CODEC_ENCODING, $payload->getMetadata()['encoding']);
// Check result value from interceptor
/** @var Payload $resultPayload */
$resultPayload = $this->interceptor->result->toPayloads()->getPayloads()[0];
- self::assertSame(CODEC_ENCODING, $resultPayload->getMetadata()['encoding']);
+ self::assertSame(self::CODEC_ENCODING, $resultPayload->getMetadata()['encoding']);
+ }
+
+ protected function setUp(): void
+ {
+ $this->interceptor = new Interceptor();
+ parent::setUp();
}
}
@@ -94,7 +93,7 @@ public function check(
class FeatureWorkflow
{
#[WorkflowMethod('Harness_DataConverter_Binary')]
- public function run(Bytes $data)
+ public function run(Bytes $data): Bytes
{
return $data;
}
diff --git a/tests/Acceptance/Harness/DataConverter/CodecTest.php b/tests/Acceptance/Harness/DataConverter/CodecTest.php
index a934c00c3..1ef30c76d 100644
--- a/tests/Acceptance/Harness/DataConverter/CodecTest.php
+++ b/tests/Acceptance/Harness/DataConverter/CodecTest.php
@@ -23,19 +23,17 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-const CODEC_ENCODING = 'my-encoding';
const EXPECTED_RESULT = new DTO(spec: true);
+/**
+ * # Codec Payload Encoding
+ *
+ * Test custom codec payload encoding.
+ */
class CodecTest extends TestCase
{
private ResultInterceptor $interceptor;
- protected function setUp(): void
- {
- $this->interceptor = new ResultInterceptor();
- parent::setUp();
- }
-
public function pipelineProvider(): PipelineProvider
{
return new SimplePipelineProvider([$this->interceptor]);
@@ -46,30 +44,36 @@ public function check(
#[Stub('Harness_DataConverter_Codec', args: [EXPECTED_RESULT])]
#[Client(
pipelineProvider: [self::class, 'pipelineProvider'],
- payloadConverters: [Base64PayloadCodec::class]),
- ]
+ payloadConverters: [Base64PayloadCodec::class],
+ )]
WorkflowStubInterface $stub,
): void {
$result = $stub->getResult();
self::assertEquals(EXPECTED_RESULT, $result);
- $result = $this->interceptor->result;
+ // Check arguments from interceptor
$input = $this->interceptor->start;
- self::assertNotNull($result);
self::assertNotNull($input);
+ /** @var Payload $inputPayload */
+ $inputPayload = $input->toPayloads()->getPayloads()[0];
+ self::assertSame(Base64PayloadCodec::CODEC_ENCODING, $inputPayload->getMetadata()['encoding']);
+ self::assertSame(\base64_encode('{"spec":true}'), $inputPayload->getData());
// Check result value from interceptor
+ $result = $this->interceptor->result;
+ self::assertNotNull($result);
/** @var Payload $resultPayload */
$resultPayload = $result->toPayloads()->getPayloads()[0];
- self::assertSame(CODEC_ENCODING, $resultPayload->getMetadata()['encoding']);
+ self::assertSame(Base64PayloadCodec::CODEC_ENCODING, $resultPayload->getMetadata()['encoding']);
self::assertSame(\base64_encode('{"spec":true}'), $resultPayload->getData());
- // Check arguments from interceptor
- /** @var Payload $inputPayload */
- $inputPayload = $input->toPayloads()->getPayloads()[0];
- self::assertSame(CODEC_ENCODING, $inputPayload->getMetadata()['encoding']);
- self::assertSame(\base64_encode('{"spec":true}'), $inputPayload->getData());
+ }
+
+ protected function setUp(): void
+ {
+ $this->interceptor = new ResultInterceptor();
+ parent::setUp();
}
}
@@ -89,8 +93,10 @@ public function run(mixed $data)
class ResultInterceptor implements WorkflowClientCallsInterceptor
{
use WorkflowClientCallsInterceptorTrait;
+
public ?EncodedValues $result = null;
public ?EncodedValues $start = null;
+
public function getResult(GetResultInput $input, callable $next): ?EncodedValues
{
return $this->result = $next($input);
@@ -116,9 +122,11 @@ public function __construct(...$args)
class Base64PayloadCodec implements PayloadConverterInterface
{
+ public const CODEC_ENCODING = 'my-encoding';
+
public function getEncodingType(): string
{
- return CODEC_ENCODING;
+ return self::CODEC_ENCODING;
}
public function toPayload($value): ?Payload
@@ -126,7 +134,7 @@ public function toPayload($value): ?Payload
return $value instanceof DTO
? (new Payload())
->setData(\base64_encode(\json_encode($value, flags: \JSON_THROW_ON_ERROR)))
- ->setMetadata(['encoding' => CODEC_ENCODING])
+ ->setMetadata(['encoding' => self::CODEC_ENCODING])
: null;
}
diff --git a/tests/Acceptance/Harness/DataConverter/JsonProtobufTest.php b/tests/Acceptance/Harness/DataConverter/JsonProtobufTest.php
index 781c03ca5..d1524cfb5 100644
--- a/tests/Acceptance/Harness/DataConverter/JsonProtobufTest.php
+++ b/tests/Acceptance/Harness/DataConverter/JsonProtobufTest.php
@@ -19,18 +19,16 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-const EXPECTED_RESULT = 0xDEADBEEF;
-\define(__NAMESPACE__ . '\INPUT', (new DataBlob())->setData(EXPECTED_RESULT));
-
+/**
+ * # JSON Protobuf Payload Encoding
+ *
+ * Test that JSON contains encoded protobuf payload which contains serialized `DataBlob` object.
+ */
class JsonProtobufTest extends TestCase
{
- private ResultInterceptor $interceptor;
+ private const EXPECTED_RESULT = 0xDEADBEEF;
- protected function setUp(): void
- {
- $this->interceptor = new ResultInterceptor();
- parent::setUp();
- }
+ private ResultInterceptor $interceptor;
public function pipelineProvider(): PipelineProvider
{
@@ -39,14 +37,14 @@ public function pipelineProvider(): PipelineProvider
#[Test]
public function check(
- #[Stub('Harness_DataConverter_JsonProtobuf', args: [INPUT])]
+ #[Stub('Harness_DataConverter_JsonProtobuf', args: [new DataBlob(['data' => self::EXPECTED_RESULT])])]
#[Client(pipelineProvider: [self::class, 'pipelineProvider'])]
WorkflowStubInterface $stub,
): void {
/** @var DataBlob $result */
$result = $stub->getResult(DataBlob::class);
- self::assertEquals(EXPECTED_RESULT, $result->getData());
+ self::assertEquals(self::EXPECTED_RESULT, $result->getData());
$result = $this->interceptor->result;
self::assertNotNull($result);
@@ -59,6 +57,12 @@ public function check(
self::assertSame('temporal.api.common.v1.DataBlob', $payload->getMetadata()['messageType']);
self::assertSame('{"data":"MzczNTkyODU1OQ=="}', $payload->getData());
}
+
+ protected function setUp(): void
+ {
+ $this->interceptor = new ResultInterceptor();
+ parent::setUp();
+ }
}
#[WorkflowInterface]
diff --git a/tests/Acceptance/Harness/DataConverter/JsonTest.php b/tests/Acceptance/Harness/DataConverter/JsonTest.php
index 7ff4ea22c..37b078566 100644
--- a/tests/Acceptance/Harness/DataConverter/JsonTest.php
+++ b/tests/Acceptance/Harness/DataConverter/JsonTest.php
@@ -18,18 +18,17 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-\define(__NAMESPACE__ . '\EXPECTED_RESULT', (object)['spec' => true]);
+\define(__NAMESPACE__ . '\EXPECTED_RESULT', (object) ['spec' => true]);
+/**
+ * # JSON Payload Encoding
+ *
+ * Test that regular PHP structures like plain objects are encoded as JSON payloads.
+ */
class JsonTest extends TestCase
{
private ResultInterceptor $interceptor;
- protected function setUp(): void
- {
- $this->interceptor = new ResultInterceptor();
- parent::setUp();
- }
-
public function pipelineProvider(): PipelineProvider
{
return new SimplePipelineProvider([$this->interceptor]);
@@ -55,6 +54,12 @@ public function check(
self::assertSame('json/plain', $payload->getMetadata()['encoding']);
self::assertSame('{"spec":true}', $payload->getData());
}
+
+ protected function setUp(): void
+ {
+ $this->interceptor = new ResultInterceptor();
+ parent::setUp();
+ }
}
#[WorkflowInterface]
diff --git a/tests/Acceptance/Harness/DataConverter/EmptyTest.php b/tests/Acceptance/Harness/DataConverter/NullTest.php
similarity index 81%
rename from tests/Acceptance/Harness/DataConverter/EmptyTest.php
rename to tests/Acceptance/Harness/DataConverter/NullTest.php
index 904d988a2..1de5f1674 100644
--- a/tests/Acceptance/Harness/DataConverter/EmptyTest.php
+++ b/tests/Acceptance/Harness/DataConverter/NullTest.php
@@ -21,7 +21,13 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-class EmptyTest extends TestCase
+/**
+ * # Null Payload Encoding
+ *
+ * Activities with `void` return type actually returns `null` as a result.
+ * Workflow check that acitivity's returning value is `null` and the history event contains `null` payload.
+ */
+class NullTest extends TestCase
{
#[Test]
public function check(
@@ -49,8 +55,11 @@ public function check(
self::assertInstanceOf(Payload::class, $payload);
\assert($payload instanceof Payload);
- $decoded = \json_decode('{ "metadata": { "encoding": "YmluYXJ5L251bGw=" } }', true, 512, JSON_THROW_ON_ERROR);
- self::assertEquals($decoded, \json_decode($payload->serializeToJsonString(), true, 512, JSON_THROW_ON_ERROR));
+ self::assertEquals([
+ 'metadata' => [
+ 'encoding' => 'YmluYXJ5L251bGw=', // \base64_encode('binary/null'),
+ ],
+ ], \json_decode($payload->serializeToJsonString(), true, 512, JSON_THROW_ON_ERROR));
}
}
@@ -58,7 +67,7 @@ public function check(
class FeatureWorkflow
{
#[WorkflowMethod('Harness_DataConverter_Empty')]
- public function run()
+ public function run(): iterable
{
yield Workflow::newActivityStub(
EmptyActivity::class,
diff --git a/tests/Acceptance/Harness/EagerWorkflow/SuccessfulStartTest.php b/tests/Acceptance/Harness/EagerWorkflow/SuccessfulStartTest.php
index 2b74b820b..0d74fb5e0 100644
--- a/tests/Acceptance/Harness/EagerWorkflow/SuccessfulStartTest.php
+++ b/tests/Acceptance/Harness/EagerWorkflow/SuccessfulStartTest.php
@@ -17,17 +17,9 @@
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;
-\define('EXPECTED_RESULT', 'Hello World');
-
class SuccessfulStartTest extends TestCase
{
- private grpcCallInterceptor $interceptor;
-
- protected function setUp(): void
- {
- $this->interceptor = new grpcCallInterceptor();
- parent::setUp();
- }
+ private CatchStartWorkflowExecutionResponseInterceptor $interceptor;
public function pipelineProvider(): PipelineProvider
{
@@ -36,31 +28,37 @@ public function pipelineProvider(): PipelineProvider
#[Test]
public function start(
- #[Stub('Harness_EagerWorkflow_SuccessfulStart', eagerStart: true,)]
+ #[Stub('Harness_EagerWorkflow_SuccessfulStart', eagerStart: true)]
#[Client(timeout: 30, pipelineProvider: [self::class, 'pipelineProvider'])]
WorkflowStubInterface $stub,
): void {
// Check the result and the eager workflow proof
- self::assertSame(EXPECTED_RESULT, $stub->getResult());
+ self::assertSame('ok', $stub->getResult());
self::assertNotNull($this->interceptor->lastResponse);
self::assertNotNull($this->interceptor->lastResponse->getEagerWorkflowTask());
}
+
+ protected function setUp(): void
+ {
+ $this->interceptor = new CatchStartWorkflowExecutionResponseInterceptor();
+ parent::setUp();
+ }
}
#[WorkflowInterface]
class FeatureWorkflow
{
#[WorkflowMethod('Harness_EagerWorkflow_SuccessfulStart')]
- public function run()
+ public function run(): string
{
- return EXPECTED_RESULT;
+ return 'ok';
}
}
/**
* Catches {@see StartWorkflowExecutionResponse} from the gRPC calls.
*/
-class grpcCallInterceptor implements GrpcClientInterceptor
+class CatchStartWorkflowExecutionResponseInterceptor implements GrpcClientInterceptor
{
public ?StartWorkflowExecutionResponse $lastResponse = null;
diff --git a/tests/Acceptance/Harness/Query/SuccessfulQueryTest.php b/tests/Acceptance/Harness/Query/SuccessfulQueryTest.php
index 3c2bf0dd3..0232c1cbf 100644
--- a/tests/Acceptance/Harness/Query/SuccessfulQueryTest.php
+++ b/tests/Acceptance/Harness/Query/SuccessfulQueryTest.php
@@ -17,8 +17,10 @@
class SuccessfulQueryTest extends TestCase
{
#[Test]
- public static function check(#[Stub('Harness_Query_SuccessfulQuery')]WorkflowStubInterface $stub): void
- {
+ public static function check(
+ #[Stub('Harness_Query_SuccessfulQuery')]
+ WorkflowStubInterface $stub,
+ ): void {
self::assertSame(0, $stub->query('get_counter')?->getValue(0));
$stub->signal('inc_counter');
diff --git a/tests/Acceptance/Harness/Query/UnexpectedArgumentsTest.php b/tests/Acceptance/Harness/Query/UnexpectedArgumentsTest.php
index 157f95f19..35d533141 100644
--- a/tests/Acceptance/Harness/Query/UnexpectedArgumentsTest.php
+++ b/tests/Acceptance/Harness/Query/UnexpectedArgumentsTest.php
@@ -19,12 +19,13 @@ class UnexpectedArgumentsTest extends TestCase
{
#[Test]
public static function check(
- #[Stub('Harness_Query_UnexpectedArguments')]WorkflowStubInterface $stub,
+ #[Stub('Harness_Query_UnexpectedArguments')]
+ WorkflowStubInterface $stub,
): void {
self::assertSame($stub->query('the_query', 42)?->getValue(0), 'got 42');
try {
- $stub->query('the_query', true)?->getValue(0);
+ $stub->query('the_query', true);
throw new \Exception('Query must fail due to unexpected argument type');
} catch (WorkflowQueryException $e) {
self::assertStringContainsString(
@@ -38,7 +39,7 @@ public static function check(
# Not enough arg
try {
- $stub->query('the_query')?->getValue(0);
+ $stub->query('the_query');
throw new \Exception('Query must fail due to missing argument');
} catch (WorkflowQueryException $e) {
self::assertStringContainsString('0 passed and exactly 1 expected', $e->getPrevious()->getMessage());
diff --git a/tests/Acceptance/Harness/Query/UnexpectedQueryTypeNameTest.php b/tests/Acceptance/Harness/Query/UnexpectedQueryTypeNameTest.php
index 5dd0127ed..fa1ed130d 100644
--- a/tests/Acceptance/Harness/Query/UnexpectedQueryTypeNameTest.php
+++ b/tests/Acceptance/Harness/Query/UnexpectedQueryTypeNameTest.php
@@ -18,7 +18,8 @@ class UnexpectedQueryTypeNameTest extends TestCase
{
#[Test]
public static function check(
- #[Stub('Harness_Query_UnexpectedQueryTypeName')]WorkflowStubInterface $stub,
+ #[Stub('Harness_Query_UnexpectedQueryTypeName')]
+ WorkflowStubInterface $stub,
): void {
try {
$stub->query('nonexistent');
diff --git a/tests/Acceptance/Harness/Query/UnexpectedReturnTypeTest.php b/tests/Acceptance/Harness/Query/UnexpectedReturnTypeTest.php
index e4f9f4c5a..444495474 100644
--- a/tests/Acceptance/Harness/Query/UnexpectedReturnTypeTest.php
+++ b/tests/Acceptance/Harness/Query/UnexpectedReturnTypeTest.php
@@ -19,7 +19,8 @@ class UnexpectedReturnTypeTest extends TestCase
{
#[Test]
public static function check(
- #[Stub('Harness_Query_UnexpectedReturnType')]WorkflowStubInterface $stub,
+ #[Stub('Harness_Query_UnexpectedReturnType')]
+ WorkflowStubInterface $stub,
): void {
try {
$stub->query('the_query')?->getValue(0, 'int');
diff --git a/tests/Acceptance/Harness/Schedule/BackfillTest.php b/tests/Acceptance/Harness/Schedule/BackfillTest.php
index cc96b4126..0d9377537 100644
--- a/tests/Acceptance/Harness/Schedule/BackfillTest.php
+++ b/tests/Acceptance/Harness/Schedule/BackfillTest.php
@@ -39,13 +39,13 @@ public static function check(
StartWorkflowAction::new('Harness_Schedule_Backfill')
->withWorkflowId($workflowId)
->withTaskQueue($feature->taskQueue)
- ->withInput(['arg1'])
+ ->withInput(['arg1']),
)->withSpec(
ScheduleSpec::new()
- ->withIntervalList(CarbonInterval::minute(1))
+ ->withIntervalList(CarbonInterval::minute(1)),
)->withState(
ScheduleState::new()
- ->withPaused(true)
+ ->withPaused(true),
),
options: ScheduleOptions::new()
// todo: should namespace be inherited from Service Client options by default?