Durabletesting2#2428
Draft
GarrettBeatty wants to merge 9 commits into
Draft
Conversation
Introduce an internal IDurableServiceClient interface that LambdaDurableServiceClient now implements. Refactor WrapAsyncCore to accept this interface instead of IAmazonLambda, and add an internal WrapAsync overload the testing package can call directly. Add InternalsVisibleTo for Amazon.Lambda.DurableExecution.Testing.
Create Amazon.Lambda.DurableExecution.Testing package with the full public API surface: IDurableTestRunner<TIn,TOut>, TestResult<TOut>, TestStep, TestRunnerOptions, CloudTestRunnerOptions, OperationKind, OperationStatus, and exception types (TestExecutionFailedException, TestExecutionLimitException, UnregisteredSiblingFunctionException, CloudTestException). Add Amazon.Lambda.DurableExecution.Testing.Tests with unit tests for TestStep (kind mapping, typed accessors, GetResult<T> deserialization), TestResult (step lookup, parent-child linking, EnsureSucceeded), options validation, and exception formatting.
…erviceClient The core state machine for the local test runner: - InMemoryOperationStore: per-execution operation storage with token tracking - CheckpointProcessor: maps OperationUpdate actions (START/SUCCEED/FAIL/RETRY) to Operation status transitions, mints callback IDs, applies time skipping - InMemoryDurableServiceClient: implements IDurableServiceClient by delegating to the processor and store Includes unit tests for both store (isolation, ordering, upsert) and processor (all action types, time skipping, callback ID minting, WaitForCondition retry).
Implements registration and invocation dispatch for InvokeAsync calls: - RegisterPlain<TPayload, TResult>: plain Lambda handlers - RegisterDurable<TPayload, TResult>: durable handlers (placeholder for nested runner, wired in commit 4) - Name matching: exact match first, then ARN-extracted name fallback - Errors from handlers returned as ErrorObject (not re-thrown) - UnregisteredSiblingFunctionException for unknown functions
The core local test runner that drives workflows to completion: - ExecutionOrchestrator: seeds the EXECUTION op with serialized input, then loops DurableFunction.WrapAsync (internal overload) with the in-memory service client until terminal or MaxInvocations exceeded - DurableTestRunner<TInput, TOutput>: public entry point with RunAsync, RegisterFunction, RegisterDurableFunction, IAsyncDisposable Integration tests verify: single step, multi-step inspection, workflow failure, EnsureSucceeded, WaitAsync with time skipping, MaxInvocations limit, timeout, null results, and custom ARN propagation.
…, WaitForResultAsync
Implements the two-call pattern for callback workflows:
- StartAsync: drives workflow until it suspends on a callback, returns ARN
- WaitForCallbackAsync: finds the pending callback ID in the store
(matches by name or "{name}-callback" convention from the runtime)
- SendCallbackSuccessAsync/FailureAsync: mutates the callback operation
- SendCallbackHeartbeatAsync: validates callback exists (no-op locally)
- WaitForResultAsync: re-drives the workflow to terminal after callback
resolution, caches completed results
Also adds DriveUntilSuspendedAsync to ExecutionOrchestrator for the
Start→Suspend pattern.
…unctions Implements the cloud test runner that invokes real Lambda functions: - StartAsync: invokes the function, extracts DurableExecutionArn from response - WaitForResultAsync: polls GetDurableExecutionState until terminal, builds TestResult - WaitForCallbackAsync: polls until a CALLBACK STARTED operation appears - SendCallbackSuccessAsync/FailureAsync/HeartbeatAsync: calls real Lambda APIs - BuildTestResult: reconstructs TestResult<TOutput> from polled operations Tests use a mock AmazonLambdaClient to verify ARN extraction, polling behavior, timeout handling, callback discovery, and API delegation.
Add the testing package to .autover/autover.json and set version to 0.0.1. Preview label will be added manually on first release.
…itForCondition Wire up and correct the Amazon.Lambda.DurableExecution.Testing package so its headline features work end-to-end, with integration coverage for each. Local runner: - Wire RegisterFunction/RegisterDurableFunction into execution: CheckpointProcessor captures CHAINED_INVOKE STARTs and the orchestrator resolves them through the FunctionRegistry between invocations, stamping ChainedInvokeDetails.Result/Error. Unregistered siblings throw UnregisteredSiblingFunctionException instead of looping to TestExecutionLimitException. - Implement durable siblings via a nested DurableTestRunner (was NotImplementedException). - Persist WaitForCondition poll state (RETRY Payload -> StepDetails.Result) and advance its attempt counter on RETRY (it emits START only once); remove the dead Type==Wait WaitForCondition branches (runtime emits Type=STEP). - Accumulate InvocationCount across StartAsync + WaitForResultAsync; make SeedExecutionOperation idempotent so re-drives don't reset the EXECUTION op. - RunAsync throws an actionable error when a workflow suspends on a callback. Cloud runner: - Determine terminal state and the typed result/error from GetDurableExecution (Status/Result/Error) instead of scanning the EXECUTION op in the state stream, which never reaches a terminal status. - Use the typed InvokeResponse.DurableExecutionArn; treat TIMED_OUT/STOPPED as terminal; map all four error fields on SendCallbackFailureAsync. Hardening: - InMemoryOperationStore is lock-guarded and returns snapshot copies. - OperationStatus constants alias the runtime OperationStatuses (compile-time linked). - TestResult gains IsSucceeded/IsFailed and GetStepsByStatus. - Mark the package preview (0.0.1-preview + autover PrereleaseLabel) to match the runtime. Tests: +15 (sibling invoke incl. durable + unregistered + failure, WaitForCondition end-to-end, callback-via-RunAsync error, accumulated invocation count, step retry attempt 1->2->3, cloud typed result/error/terminal-status, store snapshot/concurrency). 106 -> 121 passing on net8.0 and net10.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Amazon.Lambda.DurableExecution.Testing— a new NuGet package for testing durable workflows locally and against deployed functionsDurableTestRunner<TIn,TOut>): drives workflows to completion in-process using the real runtime engine with an in-memory service backend. Supports time skipping, sibling function registration, and the full callback flowCloudDurableTestRunner<TIn,TOut>): invokes deployed Lambda functions, polls for results, and provides the sameTestResult<TOutput>inspection APIIDurableTestRunner<TIn,TOut>so tests can swap backends without code changesIDurableServiceClientinternal interface to the runtime package as the injection seam (no public API change)Key types
DurableTestRunner<TIn,TOut>/CloudDurableTestRunner<TIn,TOut>— the runnersTestResult<TOutput>/TestStep— step-level inspection (GetStep, GetResult, Children)TestRunnerOptions/CloudTestRunnerOptions— configurationTestExecutionFailedException,TestExecutionLimitException,UnregisteredSiblingFunctionException,CloudTestExceptionCommits (each self-contained with tests)
Test plan