diff --git a/solutions/ess-maker-skills/scripts/flightcheck/checks/_da_connection_refs.py b/solutions/ess-maker-skills/scripts/flightcheck/checks/_da_connection_refs.py index 3e40167d..0c6d38ca 100644 --- a/solutions/ess-maker-skills/scripts/flightcheck/checks/_da_connection_refs.py +++ b/solutions/ess-maker-skills/scripts/flightcheck/checks/_da_connection_refs.py @@ -73,13 +73,26 @@ def _bot_connection_references(client, bot_id: str) -> list[dict[str, Any]]: refs: list[dict[str, Any]] = [] for change in changes: - item = ( - change.get("connectionReference") - if isinstance(change, dict) - else None - ) - if not isinstance(item, dict): + # Surface a malformed individual entry instead of silently skipping it + # (PR #304 review): a non-dict change, or a ``connectionReference`` that + # is present but not an object, no longer matches the validated + # contract, so raise and let the owning check degrade to a WARNING. An + # absent or null ``connectionReference`` is tolerated (a non-connection + # change) and skipped. + if not isinstance(change, dict): + raise ValueError( + "Component fetch returned a malformed " + "connectionReferenceChanges entry." + ) + if "connectionReference" not in change: continue + item = change.get("connectionReference") + if item is None: + continue + if not isinstance(item, dict): + raise ValueError( + "Component fetch returned a malformed connectionReference entry." + ) refs.append( { "botid": bot_id, diff --git a/tests/flightcheck/checks/test_da_connection_refs.py b/tests/flightcheck/checks/test_da_connection_refs.py index e3b4c42e..54f9034a 100644 --- a/tests/flightcheck/checks/test_da_connection_refs.py +++ b/tests/flightcheck/checks/test_da_connection_refs.py @@ -106,6 +106,41 @@ def test_read_active_malformed_change_set_raises(): reader.read_active_agent_connection_references(runner) +def test_read_active_non_dict_change_entry_raises(): + # An individual change that is not an object is surfaced, not skipped. + runner = _FakeRunner( + _FakeClient({"BOT": {"connectionReferenceChanges": ["oops"]}}), + {"agent": {"botId": "BOT"}}, + ) + with pytest.raises(ValueError): + reader.read_active_agent_connection_references(runner) + + +def test_read_active_malformed_individual_reference_raises(): + # connectionReference present but not an object -> surfaced, not "not found". + payload = { + "connectionReferenceChanges": [ + {"changeType": "Insert", "connectionReference": "not-an-object"} + ] + } + runner = _FakeRunner(_FakeClient({"BOT": payload}), {"agent": {"botId": "BOT"}}) + with pytest.raises(ValueError): + reader.read_active_agent_connection_references(runner) + + +def test_read_active_change_without_reference_is_tolerated(): + # A change carrying no (or null) connectionReference is a non-connection + # change: skipped, not raised. + payload = { + "connectionReferenceChanges": [ + {"changeType": "Delete"}, + {"changeType": "Insert", "connectionReference": None}, + ] + } + runner = _FakeRunner(_FakeClient({"BOT": payload}), {"agent": {"botId": "BOT"}}) + assert reader.read_active_agent_connection_references(runner) == [] + + # -------------------------------------------------------------------------- # read_all_agents_connection_references (ENV-004 surface: env-wide, de-duped) # --------------------------------------------------------------------------