Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/ha_mcp/client/rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,18 +823,20 @@ async def _supervisor_logs_get(self, path: str, lines: int | None = None) -> str
)

try:
async with make_supervisor_httpx_client(
timeout=httpx.Timeout(self.timeout),
verify=self.verify_ssl,
) as client:
response = await client.get(
relative_path,
headers={"Accept": "text/plain"},
params={"lines": lines} if lines is not None else None,
)
except httpx.TimeoutException as e:
async with asyncio.timeout(self.timeout):
async with make_supervisor_httpx_client(
timeout=httpx.Timeout(self.timeout),
verify=self.verify_ssl,
) as client:
response = await client.get(
relative_path,
headers={"Accept": "text/plain"},
params={"lines": lines} if lines is not None else None,
)
except (TimeoutError, httpx.TimeoutException) as e:
raise HomeAssistantConnectionError(
f"Timeout fetching /{path}/logs from Supervisor: {e}"
f"Timeout fetching /{path}/logs from Supervisor after "
f"{self.timeout}s: {str(e) or type(e).__name__}"
) from e
except httpx.HTTPError as e:
raise HomeAssistantConnectionError(
Expand Down
21 changes: 21 additions & 0 deletions tests/src/unit/test_tools_utility_supervisor_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
error translation.
"""

import asyncio
import json
import re
from pathlib import Path
Expand Down Expand Up @@ -435,6 +436,26 @@ async def test_raises_connection_error_on_timeout_with_distinct_message(

assert "Timeout" in str(exc_info.value)

@pytest.mark.asyncio
@pytest.mark.timeout(10)
async def test_supervisor_log_fetch_has_overall_deadline(
self, mock_client, addon_install, mock_async_client_class
):
"""A stalled Supervisor log body must not wait forever on per-chunk IO."""
inner_client, _ = mock_async_client_class
mock_client.timeout = 0.01

async def _hang_forever(*_args, **_kwargs):
"""Model a Supervisor response that never completes."""
await asyncio.Event().wait()

inner_client.get.side_effect = _hang_forever

with pytest.raises(HomeAssistantConnectionError) as exc_info:
await mock_client.get_addon_logs("core_mosquitto")

assert "after 0.01s: TimeoutError" in str(exc_info.value)

@pytest.mark.asyncio
async def test_raises_connection_error_on_network_failure_with_distinct_message(
self, mock_client, addon_install, mock_async_client_class
Expand Down
Loading