srr84/agent-data-layer
0
1"""Unit tests for result_envelope: the typed return-schema egress gate (M3 DoD, FR-3).2 3Covers: a ToolResult whose asserted_value is the tool's declared ReturnSchema dataclass4passes through unchanged; a schema mismatch (wrong dataclass for the tool) yields a typed5ValidationError(return) whose `got` is a typed summary (never a value echo); an6unregistered tool name at egress yields a typed ValidationError.7 8```text9spec traceability (audit map — safe to skip)10docs/09 §4 (M3 DoD: result_envelope return-schema)11LLD §1/§3/§6 (validate vs return_schema; ValidationError(return) owner = result_envelope)12TechSpec §3/§4 (per-tool result shapes; ValidationError wire; got = typed summary) · FuncSpec FR-313"""14 15from __future__ import annotations16 17import pytest18 19from agent_data_layer.contracts.envelopes import ToolResult20from agent_data_layer.contracts.errors import ValidationError21from agent_data_layer.contracts.provenance import EventPointer22from agent_data_layer.contracts.tools import (23 PriceResult,24 ReturnSchema,25 StockResult,26 ValidatedToolCall,27)28from agent_data_layer.attacher.provenance_attacher import attach29from agent_data_layer.executor.tool_executor import execute30from agent_data_layer.result_envelope.result_envelope import build_result_envelope31from agent_data_layer.store.event_index import EventIndex, build_index32from agent_data_layer.store.store_loader import load_store33 34_AS_OF = "2026-01-18T08:00:00Z"35 36 37@pytest.fixture38def index() -> EventIndex:39 return build_index(load_store())40 41 42def test_matching_schema_passes_through(index: EventIndex) -> None:43 """A ToolResult with the tool's declared ReturnSchema passes the gate unchanged."""44 executed = execute(ValidatedToolCall(tool_name="get_stock_state", params={"store_id": "st-001", "product_id": "pr-001", "as_of": _AS_OF}), index)45 result = attach(executed)46 out = build_result_envelope("get_stock_state", result)47 assert out is result # validated, returned as-is (FR-3)48 assert isinstance(out, ToolResult)49 assert isinstance(out.asserted_value, StockResult)50 51 52def test_schema_mismatch_yields_typed_validation_error() -> None:53 """A ToolResult whose asserted_value is the WRONG dataclass for the tool is rejected."""54 # get_stock_state declares StockResult; a PriceResult here is a wiring bug.55 wrong: ToolResult[ReturnSchema] = ToolResult(56 asserted_value=PriceResult(value=199),57 provenance=frozenset({EventPointer(event_id="evt-000001", store_id="st-001")}), # type: ignore[arg-type]58 observed_at=None,59 )60 out = build_result_envelope("get_stock_state", wrong)61 assert isinstance(out, ValidationError)62 assert out.field == "asserted_value"63 assert "StockResult" in out.expected64 # got is a typed summary (the type name), never a value echo (F6S-8).65 assert out.got == "a PriceResult"66 assert "199" not in out.got67 68 69def test_unregistered_tool_name_at_egress_yields_error() -> None:70 """An unregistered tool name at egress is a typed ValidationError (code-bug guard)."""71 from agent_data_layer.contracts.enums import ShelfState72 73 result: ToolResult[ReturnSchema] = ToolResult(74 asserted_value=StockResult(value=ShelfState.ON_SHELF),75 provenance=frozenset({EventPointer(event_id="evt-000001", store_id="st-001")}), # type: ignore[arg-type]76 observed_at=None,77 )78 out = build_result_envelope("no_such_tool", result)79 assert isinstance(out, ValidationError)80 assert out.field == "tool_name"81 82 83def test_each_tool_passes_its_own_schema(index: EventIndex) -> None:84 """Every tool's real ToolResult passes its own egress gate (FR-3 over all 6)."""85 cases = [86 ("get_stock_state", {"store_id": "st-001", "product_id": "pr-001", "as_of": _AS_OF}),87 ("get_price", {"store_id": "st-001", "product_id": "pr-001", "as_of": _AS_OF}),88 ("get_compliance_flag", {"store_id": "st-001", "product_id": "pr-001", "as_of": _AS_OF}),89 ("count_stores_with_condition", {"condition": "OUT_OF_STOCK", "as_of": _AS_OF}),90 ("list_products_in_state", {"store_id": "st-001", "state": "ON_SHELF", "as_of": _AS_OF}),91 ("count_events_in_window", {"store_id": "st-001", "state": "ON_SHELF", "start": "2026-01-05T08:00:00Z", "end": "2026-01-12T08:00:00Z"}),92 ]93 for name, params in cases:94 result = attach(execute(ValidatedToolCall(tool_name=name, params=params), index))95 out = build_result_envelope(name, result)96 assert isinstance(out, ToolResult), f"{name} failed its egress schema"97 