Skip to content

Commit bd48061

Browse files
Patch76claude
andcommitted
test(tools_integrations): pin diagnostic-hint wording on raise branches
Addresses kp13's second review-pass on PR homeassistant-ai#1424: - Item 1 (Ruff format on test_tools_integrations.py): already addressed upstream in 98f6ed2 — no action in this commit. - Item 2: adds the missing `"already_deleted" not in json.dumps(err)` assertion to test_remove_helpers_integrations_subentry_other_error_ surfaces_service_call_failed in test_config_subentries_folded.py. Symmetric with the string-form sibling test below that already pins the no-mislabeling rule. - "While you're in there": adds diagnostic-hint substring assertions to all six confirmed-absent raise-branch tests so the user-facing wording (the "May indicate ... typo" framing + the matching ha_search_entities() / ha_get_integration() tool suggestion, or the TOCTOU-specific "concurrent removal" hint) can't silently degrade on future edits. Test additions per branch: - test_direct_path_entry_not_found_raises (Path 3): pin "May indicate" + "ha_get_integration" - test_simple_path_state_gone_raises_entity_not_found (Path 1): pin "May indicate" + "ha_search_entities" - test_simple_path_404_on_state_check_raises_entity_not_found (Path 1 via 404): same pins as state-gone sibling - test_flow_path_entity_not_in_registry_raises (Path 2 step 1): pin "May indicate" + "ha_search_entities" - test_flow_path_entry_not_found_at_delete_raises (Path 2 TOCTOU): pin "concurrent removal" (distinct from the typo-framed hint — semantically the entry WAS resolvable at step 1 and only vanished before step 3) - test_remove_helpers_integrations_subentry_not_found_raises (Path 4): pin "May indicate" + "ha_get_integration" 76 unit tests pass locally. Ruff format clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 98f6ed2 commit bd48061

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

tests/src/unit/test_config_subentries_folded.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,9 @@ async def test_remove_helpers_integrations_subentry_not_found_raises(
529529
assert error_data["success"] is False
530530
assert error_data["error"]["code"] == "RESOURCE_NOT_FOUND"
531531
assert "ghost-subentry" in error_data["error"]["message"]
532+
# Pin the diagnostic-hint wording (same rationale as Paths 1/3).
533+
assert "May indicate" in error_data["error"]["message"]
534+
assert "ha_get_integration" in error_data["error"]["message"]
532535
assert "already_deleted" not in json.dumps(error_data)
533536

534537

@@ -559,6 +562,10 @@ async def test_remove_helpers_integrations_subentry_other_error_surfaces_service
559562
error_data = json.loads(str(exc_info.value))
560563
assert error_data["error"]["code"] == "SERVICE_CALL_FAILED"
561564
assert "Insufficient permissions" in error_data["error"]["message"]
565+
# Symmetric with the string-form sibling test below: a regression
566+
# that re-routes a non-not_found error into the already_deleted
567+
# success shape must not pass this test.
568+
assert "already_deleted" not in json.dumps(error_data)
562569

563570

564571
async def test_remove_helpers_integrations_subentry_string_error_surfaces_service_call_failed(

tests/src/unit/test_tools_integrations.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,13 @@ async def test_direct_path_entry_not_found_raises(self, tools, mock_client):
209209
assert err["success"] is False
210210
assert err["error"]["code"] == "RESOURCE_NOT_FOUND"
211211
assert "ghost_entry" in err["error"]["message"]
212+
# Pin the diagnostic hint so the caller-facing wording can't
213+
# silently degrade — both the "may indicate" framing and the
214+
# ha_get_integration tool reference were part of the review-cycle
215+
# outcome and removing either would regress UX without
216+
# otherwise failing this test.
217+
assert "May indicate" in err["error"]["message"]
218+
assert "ha_get_integration" in err["error"]["message"]
212219
assert "already_deleted" not in json.dumps(err)
213220

214221
async def test_direct_path_non_404_apierror_surfaces_structured_error(
@@ -358,6 +365,9 @@ async def test_simple_path_state_gone_raises_entity_not_found(
358365
assert err["success"] is False
359366
assert err["error"]["code"] == "ENTITY_NOT_FOUND"
360367
assert "my_button" in err["error"]["message"]
368+
# Pin the diagnostic-hint wording (same rationale as Path 3).
369+
assert "May indicate" in err["error"]["message"]
370+
assert "ha_search_entities" in err["error"]["message"]
361371
assert "already_deleted" not in json.dumps(err)
362372

363373
async def test_simple_path_404_on_state_check_raises_entity_not_found(
@@ -395,6 +405,10 @@ async def test_simple_path_404_on_state_check_raises_entity_not_found(
395405
assert err["success"] is False
396406
assert err["error"]["code"] == "ENTITY_NOT_FOUND"
397407
assert "never_existed_button" in err["error"]["message"]
408+
# Same diagnostic hint as the state-gone branch — both sub-paths
409+
# of Path 1 confirmed-absent route to the same raise.
410+
assert "May indicate" in err["error"]["message"]
411+
assert "ha_search_entities" in err["error"]["message"]
398412
assert "already_deleted" not in json.dumps(err)
399413

400414
async def test_simple_path_non_404_apierror_propagates(self, tools, mock_client):
@@ -643,6 +657,9 @@ async def test_flow_path_entity_not_in_registry_raises(self, tools, mock_client)
643657
assert err["success"] is False
644658
assert err["error"]["code"] == "ENTITY_NOT_FOUND"
645659
assert "template.ghost" in err["error"]["message"]
660+
# Pin the diagnostic-hint wording (same rationale as Path 1).
661+
assert "May indicate" in err["error"]["message"]
662+
assert "ha_search_entities" in err["error"]["message"]
646663
assert "already_deleted" not in json.dumps(err)
647664

648665
async def test_flow_path_lookup_failed_maps_to_websocket_disconnected(
@@ -723,6 +740,11 @@ async def test_flow_path_entry_not_found_at_delete_raises(self, tools, mock_clie
723740
assert err["success"] is False
724741
assert err["error"]["code"] == "RESOURCE_NOT_FOUND"
725742
assert "stale_entry" in err["error"]["message"]
743+
# Pin the TOCTOU-specific diagnostic hint ("concurrent removal"
744+
# framing) — distinct from the typo-framed hint on other paths
745+
# because the entry WAS resolvable at step 1 and only vanished
746+
# before step 3 reached HA.
747+
assert "concurrent removal" in err["error"]["message"]
726748
assert "already_deleted" not in json.dumps(err)
727749

728750
async def test_flow_path_require_restart_propagated(self, tools, mock_client):

0 commit comments

Comments
 (0)