fix: _poll_oneshot for DirSize/MD5 with burst-retry on early 599
Add FileStationClient.start_and_poll_immediately: starts the async task and immediately makes the first status poll within the same method, with no intermediate awaits other than the two HTTP calls. This minimises scheduler latency between start and first poll for one-shot tasks. _poll_oneshot now accepts the first_status from start_and_poll_immediately: - finished=True on first poll → return immediately - finished=False → Phase 2 (exponential backoff, 60 s timeout) - None (first poll was 599) → burst-retry 10× at 10 ms, then Phase 2 (Phase 2 keeps polling through 599 until seen_alive, then fails fast) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,8 +22,24 @@ def config() -> AppConfig:
|
||||
|
||||
def _make_mcp_and_tools(config: AppConfig, client: MagicMock) -> dict:
|
||||
"""Register FileStation tools on a mock FastMCP and collect them by name."""
|
||||
from mcp_synology_filestation.client import FileStationClient
|
||||
from mcp_synology_filestation.tools.filestation import register_filestation
|
||||
|
||||
# Bind the real start_and_poll_immediately so it delegates into the
|
||||
# already-mocked client.request — no separate mock needed per test.
|
||||
async def _start_and_poll_immediately(
|
||||
api: str,
|
||||
start_params: dict,
|
||||
poll_version: int,
|
||||
*,
|
||||
start_version: int | None = None,
|
||||
):
|
||||
return await FileStationClient.start_and_poll_immediately(
|
||||
client, api, start_params, poll_version, start_version=start_version
|
||||
)
|
||||
|
||||
client.start_and_poll_immediately = _start_and_poll_immediately
|
||||
|
||||
registered: dict[str, object] = {}
|
||||
|
||||
mcp = MagicMock()
|
||||
@@ -1635,12 +1651,11 @@ async def test_dir_size_retries_on_transient_599(config: AppConfig) -> None:
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dir_size_window_timeout_on_persistent_599(config: AppConfig) -> None:
|
||||
"""dir_size fails fast (window_timeout=3 s) when DSM returns only 599s.
|
||||
async def test_dir_size_times_out_on_persistent_599(config: AppConfig) -> None:
|
||||
"""dir_size times out after 60 s when DSM returns only 599s for every poll.
|
||||
|
||||
Once the window_timeout elapses without ever seeing the task running
|
||||
(finished=False), _poll_task returns the "result window missed" error
|
||||
rather than waiting the full 60 s.
|
||||
The immediate poll + burst both return 599; Phase 2 keeps polling (large
|
||||
directories eventually surface) until the 60 s timeout fires.
|
||||
"""
|
||||
client = MagicMock()
|
||||
|
||||
@@ -1656,7 +1671,7 @@ async def test_dir_size_window_timeout_on_persistent_599(config: AppConfig) -> N
|
||||
result = await tools["dir_size"](path="/dead")
|
||||
|
||||
assert result.startswith("Error:")
|
||||
assert "retry" in result.lower() or "window" in result.lower() or "poll" in result.lower()
|
||||
assert "timed out" in result.lower() or "60 seconds" in result
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user