CoolFace
Apppublic

srr84/agent-data-layer

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
test_tool_registry.py204 linesDownload Raw Back to tests
1"""Unit tests for tool_registry: the closed menu of T1-T6 + name -> ToolSpec lookup.2 3Covers: each of the six tools registers with the exact docs/07 §3 param set (names,4types, declaration order) and its concrete return-schema type + provenance kind; lookup5of each registered name returns its ToolSpec; an unregistered / hallucinated name returns6the typed UnregisteredTool; every registered param type is a member of the M0 closed7ParamType set (no free-form / expression param anywhere — INV-C1); the T4 condition param8uses the TechSpec §3 5-key by_condition set (R-B-1); and the registry's routing keys (tool9names) are distinct and well-defined.10 11```text12spec traceability (audit map — safe to skip)13LLD §1 (M-tool_registry DoD: name -> spec; unknown -> UnregisteredTool), §2/§2a (ToolSpec/ParamSpec; six tools)14TechSpec §3 (the six tool/param schemas; T4 by_condition 5-key set)15FuncSpec FR-1 (registered tools only), FR-6/FR-7 (no raw-query param), FR-8.1 (scalar T1-T3 / set T4-T6)16Charter INV-C1 (closed typed/enum params, no free-form), INV-C3 (typed params)  ·  R-B-1 (T4 condition keys)17```18"""19 20from __future__ import annotations21 22from typing import get_args23 24import pytest25 26from agent_data_layer.contracts.enums import ComplianceFlag, ShelfState27from agent_data_layer.contracts.errors import UnregisteredTool28from agent_data_layer.contracts.tools import (29    ComplianceResult,30    ConditionCountResult,31    ParamType,32    PriceResult,33    ProductListResult,34    StockResult,35    ToolSpec,36    WindowCountResult,37)38from agent_data_layer.registry.tool_registry import (39    TOOL_SPECS,40    get_tool_spec,41    registered_tool_names,42)43 44# The M0 closed ParamType set — the ONLY types a registered param may carry (INV-C1).45_CLOSED_PARAM_TYPES = frozenset(get_args(ParamType))46 47# The TechSpec §3 5-key by_condition set the T4 `condition` param must use (R-B-1).48_T4_CONDITION_KEYS = frozenset(49    {"OUT_OF_STOCK", "LOW_STOCK", "PRICE_MISMATCH", "EXPIRED", "PLANOGRAM_VIOLATION"}50)51 52# The expected per-tool schema, transcribed from docs/07 §3 / docs/05 §2a. Each entry:53# tool name -> (ordered (param_name, param_type) tuples, return_schema class, provenance_kind).54_EXPECTED = {55    "get_stock_state": (56        (("store_id", "StoreId"), ("product_id", "ProductId"), ("as_of", "Timestamp")),57        StockResult,58        "scalar",59    ),60    "get_price": (61        (("store_id", "StoreId"), ("product_id", "ProductId"), ("as_of", "Timestamp")),62        PriceResult,63        "scalar",64    ),65    "get_compliance_flag": (66        (("store_id", "StoreId"), ("product_id", "ProductId"), ("as_of", "Timestamp")),67        ComplianceResult,68        "scalar",69    ),70    "count_stores_with_condition": (71        (("condition", "StoreCondition"), ("as_of", "Timestamp")),72        ConditionCountResult,73        "set",74    ),75    "list_products_in_state": (76        (("store_id", "StoreId"), ("state", "ShelfState"), ("as_of", "Timestamp")),77        ProductListResult,78        "set",79    ),80    "count_events_in_window": (81        (82            ("store_id", "StoreId"),83            ("state", "ShelfState"),84            ("start", "Timestamp"),85            ("end", "Timestamp"),86        ),87        WindowCountResult,88        "set",89    ),90}91 92 93def test_exactly_six_tools_registered() -> None:94    """The closed menu holds exactly the six tools T1-T6 (FR-1, no more/fewer)."""95    assert set(TOOL_SPECS) == set(_EXPECTED)96    assert len(TOOL_SPECS) == 697 98 99@pytest.mark.parametrize("name", list(_EXPECTED))100def test_each_tool_registers_exact_param_set(name: str) -> None:101    """Each tool registers with its exact docs/07 §3 param set: names, types, order."""102    expected_params, expected_return, expected_kind = _EXPECTED[name]103    spec = TOOL_SPECS[name]104    assert spec.name == name105    actual_params = tuple((p.name, p.type) for p in spec.params)106    assert actual_params == expected_params107    assert spec.return_schema is expected_return108    assert spec.provenance_kind == expected_kind109 110 111@pytest.mark.parametrize("name", list(_EXPECTED))112def test_lookup_returns_tool_spec(name: str) -> None:113    """get_tool_spec returns the registered ToolSpec for each registered name."""114    spec = get_tool_spec(name)115    assert isinstance(spec, ToolSpec)116    assert spec is TOOL_SPECS[name]117 118 119@pytest.mark.parametrize(120    "name",121    [122        "get_inventory",  # plausible-but-unregistered123        "GET_STOCK_STATE",  # wrong case is a different name124        "get_stock_state ",  # trailing space125        "",  # empty126        "select * from events",  # raw-query-shaped string is just an unknown name127        "drop_table",128    ],129)130def test_unregistered_or_hallucinated_name_returns_typed_error(name: str) -> None:131    """An unknown/hallucinated tool name returns the typed UnregisteredTool (never raises,132    never coerced to OutOfCoverage) — LLD §6, FR-1."""133    result = get_tool_spec(name)134    assert isinstance(result, UnregisteredTool)135    assert result.name == name136 137 138def test_every_registered_param_type_is_in_the_closed_set() -> None:139    """INV-C1: every registered param's type is a member of the M0 closed ParamType set —140    a bounded typed scalar or a closed enum; no free-form / expression / predicate param."""141    for spec in TOOL_SPECS.values():142        for param in spec.params:143            assert param.type in _CLOSED_PARAM_TYPES, (spec.name, param.name, param.type)144 145 146def test_no_free_form_param_name_or_open_type() -> None:147    """No registered param is a free-form passthrough: every param has a concrete name and148    a closed type, and no param is named like a raw query/expression slot (FR-6/FR-7)."""149    forbidden_names = {"query", "expression", "predicate", "filter", "raw", "sql"}150    for spec in TOOL_SPECS.values():151        for param in spec.params:152            assert param.name not in forbidden_names, (spec.name, param.name)153            assert param.name  # non-empty154            assert param.type  # non-empty closed type155 156 157def test_enum_params_carry_enum_domain_scalars_do_not() -> None:158    """An enum param pins enum_domain to its allowed value set; a typed scalar carries159    enum_domain=None (it has a format validator, not an enum membership set) — M0 ParamSpec."""160    enum_types = {"ShelfState", "ComplianceFlag", "StoreCondition"}161    for spec in TOOL_SPECS.values():162        for param in spec.params:163            if param.type in enum_types:164                assert param.enum_domain is not None and len(param.enum_domain) > 0, (165                    spec.name,166                    param.name,167                )168            else:169                assert param.enum_domain is None, (spec.name, param.name)170 171 172def test_t4_condition_uses_techspec_by_condition_5key_set() -> None:173    """R-B-1: the T4 condition param enum is the TechSpec §3 5-key by_condition set174    (OUT_OF_STOCK|LOW_STOCK|PRICE_MISMATCH|EXPIRED|PLANOGRAM_VIOLATION), consistent with175    the existing event_index + gold — NOT the M0 StoreCondition HAS_* names."""176    t4 = TOOL_SPECS["count_stores_with_condition"]177    (condition_param,) = [p for p in t4.params if p.name == "condition"]178    assert condition_param.enum_domain == _T4_CONDITION_KEYS179 180 181def test_state_enum_domains_match_m0_enums() -> None:182    """The ShelfState / ComplianceFlag param domains equal the M0 enum member-value sets183    (the registry pins the closed domain to the contract enum, not a hand-typed subset)."""184    shelf_domain = frozenset(m.value for m in ShelfState)185    compliance_domain = frozenset(m.value for m in ComplianceFlag)186    for spec in TOOL_SPECS.values():187        for param in spec.params:188            if param.type == "ShelfState":189                assert param.enum_domain == shelf_domain, (spec.name, param.name)190            if param.type == "ComplianceFlag":191                assert param.enum_domain == compliance_domain, (spec.name, param.name)192 193 194def test_routing_keys_are_disjoint_and_well_defined() -> None:195    """The registry's routing keys (tool names) are distinct and exhaustively cover the196    menu: each ToolSpec.name is unique, non-empty, and equals its dict key, so name-based197    routing is unambiguous (one name -> at most one spec). docs/05 specifies no separate198    routing-edge predicate, so disjointness here is the name-key uniqueness contract."""199    names = [spec.name for spec in TOOL_SPECS.values()]200    assert len(names) == len(set(names))  # no duplicate routing key201    assert set(names) == registered_tool_names()202    for key, spec in TOOL_SPECS.items():203        assert key == spec.name  # dict key is the canonical routing key204