Initial release: iai-mcp v0.1.0
Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: XNLLLLH <XNLLLLH@users.noreply.github.com>
This commit is contained in:
commit
f6b876fbe7
332 changed files with 97258 additions and 0 deletions
207
tests/test_daemon_s4_first_iter_defer.py
Normal file
207
tests/test_daemon_s4_first_iter_defer.py
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
"""Phase 07.6 W1 / tests for the startup grace before the first
|
||||
`_s4_offline_loop` iteration.
|
||||
|
||||
Defends against the regression where a freshly-spawned daemon immediately
|
||||
runs the heavy S4 viability scan (sigma.compute_and_emit ->
|
||||
retrieve.build_runtime_graph -> runtime_graph_cache.save -> json.dumps),
|
||||
materialising a multi-GB intermediate Python string (CONTEXT.md D-01:
|
||||
py-spy 2026-04-29 PID 7959 RSS 7.6GB).
|
||||
|
||||
Project async-test idiom (mandatory): sync `def test_X(...)` body wraps
|
||||
`asyncio.run(_async_body(...))`. The project does NOT depend on
|
||||
`pytest-asyncio`; `@pytest.mark.asyncio` markers silently pass without
|
||||
running. See tests/test_cpu_watchdog.py:12, tests/test_cascade_no_block.py:11
|
||||
for the canonical pattern. The plan template prescribed pytest-asyncio
|
||||
markers; this file deviates (Rule 1 — fake-GREEN avoidance) per project
|
||||
precedent.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _fake_store():
|
||||
"""_s4_offline_loop only forwards `store` to s4.run_offline_pass and
|
||||
write_event; both are stubbed in these tests, so a SimpleNamespace
|
||||
placeholder is enough — never touches LanceDB.
|
||||
"""
|
||||
return SimpleNamespace()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 1: grace=0 fast-path — first iter runs within ≤100ms
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_grace_zero_runs_first_iter_within_100ms(monkeypatch):
|
||||
"""D-06 (a): grace=0 => stubbed run_offline_pass invoked within ≤100ms."""
|
||||
asyncio.run(_grace_zero_fast_path_body(monkeypatch))
|
||||
|
||||
|
||||
async def _grace_zero_fast_path_body(monkeypatch):
|
||||
import iai_mcp.daemon as daemon_mod
|
||||
|
||||
monkeypatch.setattr(daemon_mod, "S4_FIRST_ITER_GRACE_SEC", 0.0)
|
||||
called = asyncio.Event()
|
||||
call_count = {"n": 0}
|
||||
|
||||
def _stub_run_offline_pass(_store):
|
||||
call_count["n"] += 1
|
||||
called.set()
|
||||
|
||||
monkeypatch.setattr(daemon_mod.s4, "run_offline_pass", _stub_run_offline_pass)
|
||||
shutdown = asyncio.Event()
|
||||
store = _fake_store()
|
||||
t0 = time.monotonic()
|
||||
task = asyncio.create_task(daemon_mod._s4_offline_loop(store, shutdown))
|
||||
try:
|
||||
await asyncio.wait_for(called.wait(), timeout=0.1)
|
||||
elapsed = time.monotonic() - t0
|
||||
assert elapsed <= 0.15, (
|
||||
f"first run_offline_pass took {elapsed*1000:.1f}ms; expected <=100ms "
|
||||
f"(plus ~50ms slack for to_thread schedule)"
|
||||
)
|
||||
finally:
|
||||
shutdown.set()
|
||||
try:
|
||||
await asyncio.wait_for(task, timeout=1.0)
|
||||
except asyncio.TimeoutError:
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
assert call_count["n"] >= 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 2: grace>0 deferred-path — no call before grace, ≥1 call after
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_grace_positive_defers_first_iter(monkeypatch):
|
||||
"""D-06 (b): grace=0.5 => no call before 0.4s; ≥1 call after 0.7s."""
|
||||
asyncio.run(_grace_positive_deferred_body(monkeypatch))
|
||||
|
||||
|
||||
async def _grace_positive_deferred_body(monkeypatch):
|
||||
import iai_mcp.daemon as daemon_mod
|
||||
|
||||
monkeypatch.setattr(daemon_mod, "S4_FIRST_ITER_GRACE_SEC", 0.5)
|
||||
call_count = {"n": 0}
|
||||
|
||||
def _stub_run_offline_pass(_store):
|
||||
call_count["n"] += 1
|
||||
|
||||
monkeypatch.setattr(daemon_mod.s4, "run_offline_pass", _stub_run_offline_pass)
|
||||
shutdown = asyncio.Event()
|
||||
store = _fake_store()
|
||||
task = asyncio.create_task(daemon_mod._s4_offline_loop(store, shutdown))
|
||||
try:
|
||||
await asyncio.sleep(0.4)
|
||||
assert call_count["n"] == 0, (
|
||||
f"S4 ran before 0.5s grace elapsed: call_count={call_count['n']}"
|
||||
)
|
||||
# Total ~0.7s — past 0.5s grace + to_thread schedule slack.
|
||||
await asyncio.sleep(0.3)
|
||||
assert call_count["n"] >= 1, (
|
||||
f"S4 did not run after grace elapsed: call_count={call_count['n']}"
|
||||
)
|
||||
finally:
|
||||
shutdown.set()
|
||||
try:
|
||||
await asyncio.wait_for(task, timeout=1.0)
|
||||
except asyncio.TimeoutError:
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 3: shutdown during grace — clean return, no run, no exception
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_shutdown_during_grace_returns_cleanly(monkeypatch):
|
||||
"""shutdown set during grace => loop returns cleanly, 0 calls."""
|
||||
asyncio.run(_shutdown_during_grace_body(monkeypatch))
|
||||
|
||||
|
||||
async def _shutdown_during_grace_body(monkeypatch):
|
||||
import iai_mcp.daemon as daemon_mod
|
||||
|
||||
monkeypatch.setattr(daemon_mod, "S4_FIRST_ITER_GRACE_SEC", 5.0)
|
||||
call_count = {"n": 0}
|
||||
|
||||
def _stub_run_offline_pass(_store):
|
||||
call_count["n"] += 1
|
||||
|
||||
monkeypatch.setattr(daemon_mod.s4, "run_offline_pass", _stub_run_offline_pass)
|
||||
shutdown = asyncio.Event()
|
||||
store = _fake_store()
|
||||
task = asyncio.create_task(daemon_mod._s4_offline_loop(store, shutdown))
|
||||
await asyncio.sleep(0.05)
|
||||
shutdown.set()
|
||||
# raises if loop did not return cleanly within 1s.
|
||||
await asyncio.wait_for(task, timeout=1.0)
|
||||
assert call_count["n"] == 0, (
|
||||
f"S4 ran despite shutdown during grace: call_count={call_count['n']}"
|
||||
)
|
||||
assert task.done(), "loop task did not finish"
|
||||
assert task.exception() is None, (
|
||||
f"loop raised during shutdown-in-grace: {task.exception()!r}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 4: existing s4_offline_pass_error event-emit preserved
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_run_offline_pass_error_still_emits_event(monkeypatch):
|
||||
"""Existing layered-defense preserved: run_offline_pass raises => write_event
|
||||
called with kind='s4_offline_pass_error' + severity='warning'.
|
||||
"""
|
||||
asyncio.run(_error_event_preserved_body(monkeypatch))
|
||||
|
||||
|
||||
async def _error_event_preserved_body(monkeypatch):
|
||||
import iai_mcp.daemon as daemon_mod
|
||||
|
||||
monkeypatch.setattr(daemon_mod, "S4_FIRST_ITER_GRACE_SEC", 0.0)
|
||||
events: list[tuple[str, dict, str]] = []
|
||||
|
||||
def _stub_run_offline_pass(_store):
|
||||
raise RuntimeError("boom")
|
||||
|
||||
def _stub_write_event(_store, kind, payload, severity="info", **_kwargs):
|
||||
events.append((kind, dict(payload) if isinstance(payload, dict) else payload, severity))
|
||||
|
||||
monkeypatch.setattr(daemon_mod.s4, "run_offline_pass", _stub_run_offline_pass)
|
||||
monkeypatch.setattr(daemon_mod, "write_event", _stub_write_event)
|
||||
shutdown = asyncio.Event()
|
||||
store = _fake_store()
|
||||
task = asyncio.create_task(daemon_mod._s4_offline_loop(store, shutdown))
|
||||
# Give the loop time to: enter while-body, hit run_offline_pass raise,
|
||||
# emit s4_offline_pass_error, then await the inter-iteration wait_for.
|
||||
await asyncio.sleep(0.1)
|
||||
shutdown.set()
|
||||
try:
|
||||
await asyncio.wait_for(task, timeout=1.0)
|
||||
except asyncio.TimeoutError:
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
matching = [
|
||||
e for e in events
|
||||
if e[0] == "s4_offline_pass_error"
|
||||
and e[2] == "warning"
|
||||
and "boom" in str(e[1])
|
||||
]
|
||||
assert matching, f"expected s4_offline_pass_error event with severity=warning + 'boom' payload, got: {events}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue