Skip to content

Commit f731bef

Browse files
Wire 008 fan-out-instance error-status case
Per a PR #197 review: _run_fixture_008_case silently skipped the detached fan-out-instance error-status case via a bare return, so the run reported it as passing without exercising it. Proposal 0061 is implemented, so this was a harness-wiring gap, not a not-yet deferral. Set expect_raise for the case and assert ERROR status + the exception event + the error category on both the parent fan-out node span and the raising instance's detached invocation span, mirroring the detached-subgraph error-status case. The case passes.
1 parent 6fd75aa commit f731bef

1 file changed

Lines changed: 47 additions & 10 deletions

File tree

tests/conformance/test_observability.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,16 +2541,10 @@ def _has_exception_event(span: Any) -> bool:
25412541

25422542
async def _run_fixture_008_case(case: Mapping[str, Any]) -> None:
25432543
case_name = case["name"]
2544-
# v0.73.1 added the detached fan-out INSTANCE error-status case
2545-
# (proposal 0061 §4.2, the fan-out-instance counterpart to the
2546-
# subgraph case). Proposal 0061 IS implemented (the instance raise
2547-
# propagates exactly like the subgraph raise), but this runner does
2548-
# not yet wire the fan-out-instance raise path (expect_raise + the
2549-
# both-spans error-status assertions); defer the single case until the
2550-
# harness wires it, keeping the other 008 cases running.
2551-
if case_name == "detached_fan_out_instance_raises_error_status_on_both_spans":
2552-
return
2553-
expect_raise = case_name == "detached_subgraph_raises_error_status_on_both_spans"
2544+
expect_raise = case_name in (
2545+
"detached_subgraph_raises_error_status_on_both_spans",
2546+
"detached_fan_out_instance_raises_error_status_on_both_spans",
2547+
)
25542548
spans = await _run_detached_case_graph(case, expect_raise=expect_raise)
25552549

25562550
if case_name == "detached_subgraph_two_traces_one_link":
@@ -2683,6 +2677,49 @@ async def _run_fixture_008_case(case: Mapping[str, Any]) -> None:
26832677
)
26842678
return
26852679

2680+
if case_name == "detached_fan_out_instance_raises_error_status_on_both_spans":
2681+
# Proposal 0061 §4.2, fan-out-instance variant: a raising detached
2682+
# fan-out instance surfaces ERROR on BOTH the parent's fan-out node
2683+
# span (parent trace, carrying the Link) and the per-instance detached
2684+
# invocation span (its own trace), each with the §4 category + an OTel
2685+
# exception event, sharing the parent invocation_id. The single-element
2686+
# fan-out (items [1]) means exactly one instance runs and raises.
2687+
fan_out_node_spans = [s for s in spans if s.name == "per_document_scoring"]
2688+
parent_fan_out = next((s for s in fan_out_node_spans if s.links), None)
2689+
assert parent_fan_out is not None, "expected a fan-out node span with a Link in the parent trace"
2690+
assert parent_fan_out.status.status_code.name == "ERROR", (
2691+
"parent fan-out node span MUST carry ERROR for a raising detached instance"
2692+
)
2693+
assert _has_exception_event(parent_fan_out), "parent fan-out node span MUST record the exception"
2694+
assert dict(parent_fan_out.attributes or {}).get("openarmature.error.category") == "node_exception"
2695+
assert len(parent_fan_out.links) == 1, (
2696+
f"fan-out node span MUST carry exactly one Link; got {len(parent_fan_out.links)}"
2697+
)
2698+
parent_trace_id = cast("Any", parent_fan_out.context).trace_id
2699+
detached_trace_id = parent_fan_out.links[0].context.trace_id
2700+
assert detached_trace_id != parent_trace_id, "detached instance + parent traces MUST be distinct"
2701+
inv_spans = [s for s in spans if s.name == "openarmature.invocation"]
2702+
detached_inv = next(
2703+
(s for s in inv_spans if cast("Any", s.context).trace_id == detached_trace_id), None
2704+
)
2705+
parent_inv = next((s for s in inv_spans if cast("Any", s.context).trace_id == parent_trace_id), None)
2706+
assert detached_inv is not None, (
2707+
"detached instance trace MUST root in an openarmature.invocation span"
2708+
)
2709+
assert parent_inv is not None
2710+
assert detached_inv.status.status_code.name == "ERROR", (
2711+
"detached instance invocation span MUST carry the instance's ERROR status (§4.2)"
2712+
)
2713+
assert _has_exception_event(detached_inv), (
2714+
"detached instance invocation span MUST record the exception"
2715+
)
2716+
assert dict(detached_inv.attributes or {}).get("openarmature.error.category") == "node_exception"
2717+
parent_iid = _invocation_id_of(parent_inv)
2718+
assert parent_iid is not None and _invocation_id_of(detached_inv) == parent_iid, (
2719+
"detached instance invocation span MUST share the parent's invocation_id"
2720+
)
2721+
return
2722+
26862723
raise AssertionError(f"unknown sub-case {case_name!r}")
26872724

26882725

0 commit comments

Comments
 (0)