Skip to content

Commit 02fbfed

Browse files
fix: return empty success instead of RESOURCE_NOT_FOUND for empty logbook (#710)
* fix: return empty success instead of RESOURCE_NOT_FOUND for empty logbook Empty logbook results are a valid query outcome, not an error. The HA API successfully executed the query — there just happen to be no entries for the requested period/entity. Raising RESOURCE_NOT_FOUND confused AI agents into unnecessary retries and caused flaky E2E failures on fresh CI containers. Remove the error-raising block and normalize falsy responses to an empty list, letting the existing pagination logic handle it naturally. Update E2E tests to assert success directly instead of using safe_call_tool workarounds. Closes #696 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review — remove redundant guard, fix assertion consistency - Remove `if not response` normalization block since `client.get_logbook` always returns `list[dict]` per its type signature - Use `data['entity_filter']` instead of `data.get('entity_filter')` in assertion message for consistency with the assertion itself Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent eec0f1b commit 02fbfed

2 files changed

Lines changed: 24 additions & 48 deletions

File tree

src/ha_mcp/tools/tools_utility.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,6 @@ async def ha_get_logbook(
136136
entity_id=entity_id, start_time=start_timestamp, end_time=end_time
137137
)
138138

139-
if not response:
140-
raise_tool_error(create_error_response(
141-
ErrorCode.RESOURCE_NOT_FOUND,
142-
"No logbook entries found",
143-
context={
144-
"period": f"{hours_back_int} hours back from {end_dt.isoformat()}",
145-
"entity_filter": entity_id,
146-
"total_entries": 0,
147-
"returned_entries": 0,
148-
"limit": effective_limit,
149-
"offset": offset_int,
150-
"has_more": False,
151-
},
152-
))
153-
154139
# Get total count before pagination
155140
total_entries = len(response) if isinstance(response, list) else 1
156141

tests/src/e2e/tools/test_logbook.py

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import pytest
88

9-
from ..utilities.assertions import assert_mcp_success, safe_call_tool
9+
from ..utilities.assertions import assert_mcp_success
1010

1111
logger = logging.getLogger(__name__)
1212

@@ -220,32 +220,26 @@ async def test_logbook_entity_filter(mcp_client):
220220
"""Test logbook filtering by entity_id."""
221221
logger.info("Testing logbook entity filter")
222222

223-
# Query for sun.sun which should always exist
224-
# Use safe_call_tool to handle ToolError (e.g. no entries found) as a dict
225-
raw_data = await safe_call_tool(
226-
mcp_client,
223+
result = await mcp_client.call_tool(
227224
"ha_get_logbook",
228225
{"hours_back": 24, "entity_id": "sun.sun", "limit": 50},
229226
)
227+
raw_data = assert_mcp_success(result, "Logbook entity filter")
230228
data = get_logbook_data(raw_data)
231229

232-
# Verify entity filter is recorded in response (should always be present)
233-
assert data.get("entity_filter") == "sun.sun", (
234-
f"Entity filter should be 'sun.sun', got: {data.get('entity_filter')}"
230+
# Verify entity filter is recorded in response
231+
assert data["entity_filter"] == "sun.sun", (
232+
f"Entity filter should be 'sun.sun', got: {data['entity_filter']}"
235233
)
236234

237235
# If there are entries, verify they are for the filtered entity
238-
if data.get("success"):
239-
entries = data.get("entries", [])
240-
for entry in entries:
241-
if "entity_id" in entry:
242-
assert entry["entity_id"] == "sun.sun", (
243-
f"Entry should be for sun.sun, got {entry['entity_id']}"
244-
)
245-
logger.info(f"Entity filter applied: {len(entries)} entries for sun.sun")
246-
else:
247-
# No entries case - also acceptable
248-
logger.info("No logbook entries for sun.sun in test period (expected in fresh container)")
236+
entries = data.get("entries", [])
237+
for entry in entries:
238+
if "entity_id" in entry:
239+
assert entry["entity_id"] == "sun.sun", (
240+
f"Entry should be for sun.sun, got {entry['entity_id']}"
241+
)
242+
logger.info(f"Entity filter applied: {len(entries)} entries for sun.sun")
249243

250244

251245
@pytest.mark.asyncio
@@ -297,12 +291,10 @@ async def test_logbook_response_metadata(mcp_client):
297291

298292
@pytest.mark.asyncio
299293
async def test_logbook_empty_result(mcp_client):
300-
"""Test logbook with non-existent entity returns appropriate error."""
294+
"""Test logbook with non-existent entity returns empty success."""
301295
logger.info("Testing logbook with non-existent entity")
302296

303-
# Use safe_call_tool to handle ToolError (e.g. no entries found) as a dict
304-
raw_data = await safe_call_tool(
305-
mcp_client,
297+
result = await mcp_client.call_tool(
306298
"ha_get_logbook",
307299
{
308300
"hours_back": 1,
@@ -311,16 +303,15 @@ async def test_logbook_empty_result(mcp_client):
311303
},
312304
)
313305

314-
# Parse result - may be success with no entries or error
306+
raw_data = assert_mcp_success(result, "Logbook empty result")
315307
data = get_logbook_data(raw_data)
316308

317-
# Either success with empty entries or explicit error is acceptable
318-
if data.get("success"):
319-
entries = data.get("entries", [])
320-
assert len(entries) == 0, "Should have no entries for non-existent entity"
321-
logger.info("Got success with empty entries for non-existent entity")
322-
else:
323-
# Error case is also acceptable
324-
logger.info(f"Got error for non-existent entity: {data.get('error')}")
309+
# Empty results are a valid success — not an error
310+
assert data["success"] is True, "Empty logbook should return success"
311+
entries = data.get("entries", [])
312+
assert len(entries) == 0, "Should have no entries for non-existent entity"
313+
assert data["total_entries"] == 0, "total_entries should be 0"
314+
assert data["returned_entries"] == 0, "returned_entries should be 0"
315+
assert data["has_more"] is False, "has_more should be False"
325316

326-
logger.info("Non-existent entity handling verified")
317+
logger.info("Empty logbook correctly returns success with no entries")

0 commit comments

Comments
 (0)