Skip to content

test: cover OTel replay and status mappings - #758

Open
zhongkechen wants to merge 34 commits into
mainfrom
test/otel-conformance-feasibility
Open

zhongkechen wants to merge 34 commits into
mainfrom
test/otel-conformance-feasibility

Conversation

@zhongkechen

@zhongkechen zhongkechen commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Add Python handlers and SAM mappings for OTel cases 21–26: completed-step replay, active context in user functions and callbacks, invocation retry status, callback failure without error details, and external callback completion followed by two later replays. Handlers use public durable APIs and ordinary implicit span parenting.

Case 26 creates a root callback, saves its result in a durable observation step, and uses two callback barriers to force four invocations. The shared driver delivers each callback after InvocationCompleted; raw S3 assertions require the first terminal export before the observation step, parented to the second Invocation in invocation view, and reject duplicate exports on later replays. Case 25 reuses the existing callback-failure handler with an omitted failure payload and a strict raw-history no-error-details precondition.

The local testing library reports callback success, failure and timeout through UpdatedOperationIds, and consumes versions already delivered by checkpoint responses while retaining newer updates. Core terminal notifications track actual delivery within each invocation, preserving first notifications, concurrent delivery and invocation reset behavior. The local tester canonicalizes only an exactly empty callback error container as absent, matching the existing service parser and filesystem behavior. Present fields, including empty strings and empty stack lists, retain their values; the caller failure is identical across stores. Detailed callback-failure history preserves the service’s empty Error.Payload object with Truncated: false when execution data is requested; SDK-facing state remains error=None. Typed history preserves the explicit empty payload through raw-to-typed-to-raw round trips; rebuilding callback state alone canonicalizes an exactly empty error as absent. Metadata-only history, nonempty errors and other terminal projections retain their existing behavior. Checkpoint token/watermark values are preserved.

The handler-context prerequisite isolates failed plugin setup and preserves token ownership, caller restoration and the no-plugin path. Handler scopes require explicit concrete-class opt-in, retaining compatibility with legacy helpers/properties/dynamic attributes. Both OTel views preserve same-trace parents and baggage and bind their durable span when the worker parent is absent or unrelated. Runtime dependency floors and persisted checkpoint formats remain unchanged.

Shared requirements and the reusable workflow are pinned to bdb4f1cd0f9252c1aaa978bb8b341b71f2b9d9dc in aws/aws-durable-execution-conformance-tests#131. The shared self-test fixture uses 12e760cf72dc14055ae0dec48bf0a20ad73451b2; its deployed core, OTel and handler source is identical to this PR's current revision. Each SDK CI run uses its own exact PR head. Cases 1–24 and the long-running scenarios remain in the matrix, with failed-plus-uncovered gating enabled.

Merge prerequisites: #756 and aws/aws-durable-execution-conformance-tests#131. The status-mapping prerequisite #752 is already merged.

Coverage boundary: case 24 verifies RETRY → RETRYING/UNSET. Case 25 verifies FAILED without error details → UNSET, including the typed empty raw-history payload precondition in both views. CANCELLED, TIMED_OUT and STOPPED without error details retain explicit unit coverage in both views; their cloud paths are not established. On cd81a3d, S1c cloud run 37716434728 verifies all four main backend/view reports at 26/26 and both long-running reports at 4/4, with zero failed/uncovered cases. The same head's JS examples CI passes all 148 tests without retries. The testing-library history round-trip follow-up in 2a100531 leaves the deployed fixture unchanged. Its remote CI is also green: OTel run 37719373329 passes all four 26-case and both 4-case reports, and JS examples run 37719372461 passes 148 tests.

Validation:

  • Core runtime: 1,848 tests plus 10 subtests, 98.28% coverage. The affected testing-library, OTel and handler suite passed 2,193 tests. Additional paired controls execute 28 real store/view/payload combinations and preserve caller failure, nonempty object identity and every explicitly present error field. Rejected-checkpoint/concurrent-delivery boundary tests also pass. The history projection also passes 78 direct factory checks. New history DTO/factory regressions pass 22 cases (four failed before the fix), and all 148 unchanged JS examples pass locally without retries on the latest revision. Workflow checks, type checks and Hatch lint/format pass.
  • Six early callback success/failure/timeout cases failed before the consumption fix on memory/file stores. Public tests retain saved results, first notifications, first-completion timing, correct parent/trace identity and two subsequent replays; raw export counts include equal-span-ID duplicates.
  • The handler/template wiring and replay suite passes 68 tests and pins all 52 mappings; the additional paired empty-error controls cover both stores and views. Existing resources, parameters and conditions are preserved. Exact core 2.0.0 and separate 2.0.x compatibility lanes remain configured and previously validated; installed capable-core testing requires an available core 2.1+ release. The runtime core floor remains 2.0.0.

@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 3, 2026 00:14 — with GitHub Actions Active
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 3, 2026 00:19 — with GitHub Actions Active
@github-actions

This comment has been minimized.

@zhongkechen zhongkechen changed the title test(otel): cover replay and callback contexts test: add OTel conformance handlers 21–24 Oct 3, 2026
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 3, 2026 00:57 — with GitHub Actions Active
@github-actions

This comment has been minimized.

@zhongkechen zhongkechen changed the title test: add OTel conformance handlers 21–24 test: add otel context and replay conformance Oct 3, 2026
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 3, 2026 02:08 — with GitHub Actions Active
@github-actions

This comment has been minimized.

@zhongkechen zhongkechen changed the title test: add otel context and replay conformance test: cover OTel replay and user callback contexts Oct 3, 2026
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 3, 2026 02:26 — with GitHub Actions Active
@github-actions

This comment has been minimized.

@zhongkechen
zhongkechen changed the base branch from fix/otel-handler-context-428 to main October 3, 2026 02:37
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 3, 2026 02:38 — with GitHub Actions Active
@github-actions

This comment has been minimized.

@zhongkechen

Copy link
Copy Markdown
Contributor Author

@hln33 Agreed. I’ve clarified the description in response to your coverage comment: case 24 verifies only the invocation RETRY → RETRYING / UNSET mapping. Including #752 as a runtime prerequisite does not mean cases 1–24 verify the non-success-operation/no-error → UNSET rule.

That second mapping remains unit-covered, with the conformance gap tracked in aws/aws-durable-execution-conformance-tests#131. A real callback-failure path is being validated for the follow-up requirement; no additional case 25 coverage is claimed here. The description clarification does not close that coverage gap.

@hln33

hln33 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Note on aws/aws-durable-execution-sdk-js#954 (merged 2026-10-07, fixes aws/aws-durable-execution-sdk-js#942).

aws/aws-durable-execution-sdk-js#954 fixed a defect in the JS local test runner only. LocalDurableTestRunner wraps each invocation event in DurableExecutionInvocationInputWithClient (invoke-handler.ts#L98-L101). Before the fix, the wrapper's constructor dropped UpdatedOperationIds (durable-execution-invocation-input.ts#L21-L29). As a result, the SDK treated the first external completion as a replay.

A deployed Lambda receives the event directly from the service. That path never builds the wrapper. So the conformance cases here don't depend on aws/aws-durable-execution-sdk-js#954, and they can't catch a regression of it.

The regression test for aws/aws-durable-execution-sdk-js#954 lives in JS: plugin-external-completion.integration.test.ts.

Python's local tester doesn't have this defect. Its in-process invoker also wraps the input in DurableExecutionInvocationInputWithClient and passes updated_operation_ids explicitly (invoker.py#L227-L242). That wrapper is a dataclass subclass of the base input, so it inherits the field (execution.py#L142-L161).

Record local callback success, failure, and timeout updates for the next
invocation. Deduplicate actual terminal update notifications within an
invocation while preserving first delivery and clearing state between
invocations. Keep checkpoint versions and error payload handling intact.

Exercise public callback completion through both memory and file stores,
including timeout, stored results, and two subsequent replays. Cover
duplicate checkpoints, first notification, reset, and concurrent updates.
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 00:25 — with GitHub Actions Active
end_timestamp=now if now is not None else real_now(),
callback_details=updated_callback_details,
)
self._record_updated_operation(operation.operation_id)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in 246cad8. Checkpoint delivery now consumes only callback update IDs covered by the existing delivery watermark, while retaining newer updates. Token/watermark values and error payloads are unchanged.

Six previously failing public-runner cases cover success, failure and actual timeout during a running submission step, then two later replays, on both memory and file stores. Rejected checkpoints, pure reads, cached/older deliveries and concurrent updates around delivery are covered as well. The affected testing/OTel/handler suite passed 2,149 tests; the additional boundary suite passed 98 tests.

@github-actions

This comment has been minimized.

Add the root callback completion/replay handler and both view mappings,
plus aliases of the existing callback-failure handler for case 25.
Exercise four real invocations, stored results, first-completion timing,
raw terminal export counts, parentage, and two subsequent replays.

Document the tester/core completion fixes and the remaining cloud
no-error-details precondition. Keep the workflow and test references
unchanged until the shared 26-case definitions are published together.
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 00:50 — with GitHub Actions Active
end_timestamp=now if now is not None else real_now(),
callback_details=updated_callback_details,
)
self._record_updated_operation(operation.operation_id)

This comment was marked as outdated.

@github-actions

This comment has been minimized.

Prune update IDs covered by a successful checkpoint delivery while
retaining later changes. Keep token and watermark values, pure reads,
rejected requests, cached responses, and error payloads unchanged.

Cover early success, failure and timeout during submission with real
public APIs and two later replays on both stores. Exercise rejection,
read, retry and concurrent update boundaries.

Pin the reusable workflow and conformance definitions to the coordinated
26-case shared revision fb95e20acb0b9b10f5fc032ac521a4fd45efdd73.
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 01:09 — with GitHub Actions Active
@zhongkechen zhongkechen changed the title test: cover OTel replay and user callback contexts test: cover OTel replay and status mappings Oct 8, 2026
end_timestamp=now if now is not None else real_now(),
callback_details=updated_callback_details,
)
self._record_updated_operation(operation.operation_id)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in 22004eb. The in-memory path retained an all-None ErrorObject, while the existing file-store/service parser already maps its empty serialized payload to None. The fix canonicalizes only that exactly empty payload; any present fields, including empty messages/types/data and StackTrace: [], retain their values and the nonempty in-memory error object retains its identity.

Four public regressions reproduced the discrepancy before the fix. Paired tests now cover 28 actual handler/store/view/payload runs and compare caller error, status and resumed failure behavior with the existing filesystem baseline. The full affected suite passed 2,168 tests; the final paired controls, workflow checks, types and formatting pass. Core/OTel/provider APIs and shared cloud assertions are unchanged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up in cd81a3d preserves the raw history contract as well: CallbackFailed with no error details and IncludeExecutionData=true retains Error.Payload={} and Truncated=false, matching the observed service response. SDK-facing state remains error=None and the leaf remains UNSET. Nonempty errors, metadata-only history and other terminal projections are unchanged.

The exact JS revision used by the failed CI (19ee19f) now passes all 148 unchanged example tests locally with zero retries. The full affected Python suite passes 2,166 tests, and 78 factory checks cover the narrow projection boundaries. Current-head remote CI is running.

@github-actions

This comment has been minimized.

@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 01:19 — with GitHub Actions Active
end_timestamp=now if now is not None else real_now(),
callback_details=updated_callback_details,
)
self._record_updated_operation(operation.operation_id)

This comment was marked as outdated.

@github-actions

This comment has been minimized.

Represent an exactly empty serialized callback error as absent, matching
CallbackDetails.from_dict without requiring a file-store round trip.
Preserve every present field, including empty strings and stack lists.

Compare real public handler failures across memory/file stores and both
OTel views, including caller error/status and replay behavior.

Pin the coordinated shared S1c revision with the strict empty-payload,
parent-occurrence and callback phase-gate assertions.
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 01:41 — with GitHub Actions Active
@github-actions

This comment has been minimized.

Match the observed service projection for a failed callback with no error
details when IncludeExecutionData is true: keep Payload {} and mark it
untruncated. SDK-facing state remains error=None, with an UNSET leaf and
the same caller failure. Other statuses, nonempty errors and metadata-only
history retain their existing representation.

Verify the raw public history shape, state/telemetry separation and direct
factory boundaries. The unchanged JS 19ee19f example suite passes all 148
tests without retries or assertion/selection changes.
@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 02:09 — with GitHub Actions Active
):
# Detailed service history retains an empty Error.Payload object.
# This projection must not turn the SDK-facing absent error into one.
event_error = EventError(payload=ErrorObject.from_dict({}), truncated=False)

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2a10053. History and callback state now keep their separate contracts: EventError.from_dict() preserves a present Payload: {} through raw → typed → raw conversion, while rebuilding callback operations maps only an exactly empty serialized error to None. Absent/null payloads retain their existing behavior; Truncated, rich/partial fields, explicit empty strings, and StackTrace: [] are preserved.

Added round-trip and DurableFunctionTestResult.from_execution_history coverage for both direct and decoded history. Actual public callback runs also check memory/filesystem parity, both OTel views, and history with/without execution data. Four focused regressions and four public-runner controls fail before the fix. Validation passes: 2,193 testing/OTel/conformance tests, 28 public runner executions, all 148 unchanged JS examples without retries, type checks, and formatting. Core, OTel, and deployed conformance handler code are unchanged.

@github-actions

This comment has been minimized.

@zhongkechen

zhongkechen commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

@hln33 Follow-up to your #752 coverage comment: this PR now adds case 25 for the missing non-success/no-error-details mapping, alongside the existing case 24 RETRYING/UNSET coverage.

Case 25 uses the public callback-failure handler and sends failure without Error after InvocationCompleted. The shared requirement checks the typed empty raw error payload and requires the FAILED callback leaf to remain UNSET in both views. Returning OK for that branch would now fail conformance. Case 26 also exercises a root callback’s first completion, its second-invocation parent, and two subsequent replays without duplicate terminal exports.

On current commit cd81a3d, S1c cloud run 37716434728 reports 26/26 passed in all four backend/view suites, with zero failed or uncovered cases; both long-running suites pass 4/4. The unchanged JS examples CI also passes 148 tests on this head.

FAILED, CANCELLED, TIMED_OUT and STOPPED without error details remain covered by the two-view unit matrix. The new real-service case exercises FAILED; no cloud reachability claim is made for the other three statuses. The separate new typed-history round-trip review is being investigated as testing-API maintenance; it does not change this deployed handler coverage.

@zhongkechen
zhongkechen deployed to ai-pr-review-runtime October 8, 2026 02:45 — with GitHub Actions Active
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Codex AI review

No actionable findings. Residual risk remains in cloud-only OTel conformance and service callback-history behavior.

Reviewed commit 2a100531ed4a8b631dc6a5844b95927b3d2d4f08. Workflow run

This branch was successfully deployed

1 active deployment
ai-pr-review-runtime — 2a100531 Deployed Oct 8, 2026 by zhongkechen via ai-pr-review / Codex review / Generate Codex review #1430
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants