[claude] Fix 10 vassal tests flaky under xdist parallel execution (#1243) (#1245)

This commit is contained in:
2026-03-23 23:29:25 +00:00
parent 261b7be468
commit fedd164686
2 changed files with 106 additions and 19 deletions

View File

@@ -336,7 +336,12 @@ async def test_check_agent_health_no_token():
"""Returns idle status gracefully when Gitea token is absent.""" """Returns idle status gracefully when Gitea token is absent."""
from timmy.vassal.agent_health import check_agent_health from timmy.vassal.agent_health import check_agent_health
status = await check_agent_health("claude") mock_settings = MagicMock()
mock_settings.gitea_enabled = True
mock_settings.gitea_token = "" # explicitly no token → early return
with patch("config.settings", mock_settings):
status = await check_agent_health("claude")
# Should not raise; returns idle (no active issues discovered) # Should not raise; returns idle (no active issues discovered)
assert isinstance(status, AgentStatus) assert isinstance(status, AgentStatus)
assert status.agent == "claude" assert status.agent == "claude"
@@ -478,7 +483,12 @@ async def test_check_agent_health_fetch_exception(monkeypatch):
async def test_get_full_health_report_returns_both_agents(): async def test_get_full_health_report_returns_both_agents():
from timmy.vassal.agent_health import get_full_health_report from timmy.vassal.agent_health import get_full_health_report
report = await get_full_health_report() mock_settings = MagicMock()
mock_settings.gitea_enabled = False # disabled → no network calls
mock_settings.gitea_token = ""
with patch("config.settings", mock_settings):
report = await get_full_health_report()
agent_names = {a.agent for a in report.agents} agent_names = {a.agent for a in report.agents}
assert "claude" in agent_names assert "claude" in agent_names
assert "kimi" in agent_names assert "kimi" in agent_names
@@ -488,7 +498,12 @@ async def test_get_full_health_report_returns_both_agents():
async def test_get_full_health_report_structure(): async def test_get_full_health_report_structure():
from timmy.vassal.agent_health import get_full_health_report from timmy.vassal.agent_health import get_full_health_report
report = await get_full_health_report() mock_settings = MagicMock()
mock_settings.gitea_enabled = False # disabled → no network calls
mock_settings.gitea_token = ""
with patch("config.settings", mock_settings):
report = await get_full_health_report()
assert isinstance(report, AgentHealthReport) assert isinstance(report, AgentHealthReport)
assert len(report.agents) == 2 assert len(report.agents) == 2

View File

@@ -10,6 +10,29 @@ from timmy.vassal.orchestration_loop import VassalCycleRecord, VassalOrchestrato
pytestmark = pytest.mark.unit pytestmark = pytest.mark.unit
# ---------------------------------------------------------------------------
# Helpers — prevent real network calls under xdist parallel execution
# ---------------------------------------------------------------------------
def _disabled_settings() -> MagicMock:
"""Settings mock with Gitea disabled — backlog + agent health skip HTTP."""
s = MagicMock()
s.gitea_enabled = False
s.gitea_token = ""
s.vassal_stuck_threshold_minutes = 120
return s
def _fast_snapshot() -> MagicMock:
"""Minimal SystemSnapshot mock — no disk warnings, Ollama not probed."""
snap = MagicMock()
snap.warnings = []
snap.disk.percent_used = 0.0
return snap
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# VassalCycleRecord # VassalCycleRecord
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -74,7 +97,15 @@ async def test_run_cycle_completes_without_services():
clear_dispatch_registry() clear_dispatch_registry()
orch = VassalOrchestrator(cycle_interval=300) orch = VassalOrchestrator(cycle_interval=300)
record = await orch.run_cycle() with (
patch("config.settings", _disabled_settings()),
patch(
"timmy.vassal.house_health.get_system_snapshot",
new_callable=AsyncMock,
return_value=_fast_snapshot(),
),
):
record = await orch.run_cycle()
assert isinstance(record, VassalCycleRecord) assert isinstance(record, VassalCycleRecord)
assert record.cycle_id == 1 assert record.cycle_id == 1
@@ -95,8 +126,16 @@ async def test_run_cycle_increments_cycle_count():
clear_dispatch_registry() clear_dispatch_registry()
orch = VassalOrchestrator() orch = VassalOrchestrator()
await orch.run_cycle() with (
await orch.run_cycle() patch("config.settings", _disabled_settings()),
patch(
"timmy.vassal.house_health.get_system_snapshot",
new_callable=AsyncMock,
return_value=_fast_snapshot(),
),
):
await orch.run_cycle()
await orch.run_cycle()
assert orch.cycle_count == 2 assert orch.cycle_count == 2
assert len(orch.history) == 2 assert len(orch.history) == 2
@@ -109,7 +148,15 @@ async def test_get_status_after_cycle():
clear_dispatch_registry() clear_dispatch_registry()
orch = VassalOrchestrator() orch = VassalOrchestrator()
await orch.run_cycle() with (
patch("config.settings", _disabled_settings()),
patch(
"timmy.vassal.house_health.get_system_snapshot",
new_callable=AsyncMock,
return_value=_fast_snapshot(),
),
):
await orch.run_cycle()
status = orch.get_status() status = orch.get_status()
assert status["cycle_count"] == 1 assert status["cycle_count"] == 1
@@ -183,10 +230,18 @@ async def test_run_cycle_records_backlog_error():
clear_dispatch_registry() clear_dispatch_registry()
orch = VassalOrchestrator() orch = VassalOrchestrator()
with patch( with (
"timmy.vassal.backlog.fetch_open_issues", patch(
new_callable=AsyncMock, "timmy.vassal.backlog.fetch_open_issues",
side_effect=ConnectionError("gitea unreachable"), new_callable=AsyncMock,
side_effect=ConnectionError("gitea unreachable"),
),
patch("config.settings", _disabled_settings()),
patch(
"timmy.vassal.house_health.get_system_snapshot",
new_callable=AsyncMock,
return_value=_fast_snapshot(),
),
): ):
record = await orch.run_cycle() record = await orch.run_cycle()
@@ -202,10 +257,18 @@ async def test_run_cycle_records_agent_health_error():
clear_dispatch_registry() clear_dispatch_registry()
orch = VassalOrchestrator() orch = VassalOrchestrator()
with patch( with (
"timmy.vassal.agent_health.get_full_health_report", patch(
new_callable=AsyncMock, "timmy.vassal.agent_health.get_full_health_report",
side_effect=RuntimeError("health check failed"), new_callable=AsyncMock,
side_effect=RuntimeError("health check failed"),
),
patch("config.settings", _disabled_settings()),
patch(
"timmy.vassal.house_health.get_system_snapshot",
new_callable=AsyncMock,
return_value=_fast_snapshot(),
),
): ):
record = await orch.run_cycle() record = await orch.run_cycle()
@@ -221,10 +284,13 @@ async def test_run_cycle_records_house_health_error():
clear_dispatch_registry() clear_dispatch_registry()
orch = VassalOrchestrator() orch = VassalOrchestrator()
with patch( with (
"timmy.vassal.house_health.get_system_snapshot", patch(
new_callable=AsyncMock, "timmy.vassal.house_health.get_system_snapshot",
side_effect=OSError("disk check failed"), new_callable=AsyncMock,
side_effect=OSError("disk check failed"),
),
patch("config.settings", _disabled_settings()),
): ):
record = await orch.run_cycle() record = await orch.run_cycle()
@@ -301,6 +367,12 @@ async def test_run_cycle_respects_max_dispatch_cap():
"timmy.vassal.dispatch.dispatch_issue", "timmy.vassal.dispatch.dispatch_issue",
new_callable=AsyncMock, new_callable=AsyncMock,
), ),
patch("config.settings", _disabled_settings()),
patch(
"timmy.vassal.house_health.get_system_snapshot",
new_callable=AsyncMock,
return_value=_fast_snapshot(),
),
): ):
record = await orch.run_cycle() record = await orch.run_cycle()