"""Unit tests for timmy.vassal.orchestration_loop — VassalOrchestrator.""" from __future__ import annotations import pytest from timmy.vassal.orchestration_loop import VassalCycleRecord, VassalOrchestrator # --------------------------------------------------------------------------- # VassalCycleRecord # --------------------------------------------------------------------------- def test_cycle_record_healthy_when_no_errors(): r = VassalCycleRecord( cycle_id=1, started_at="2026-01-01T00:00:00+00:00", ) assert r.healthy is True def test_cycle_record_unhealthy_with_errors(): r = VassalCycleRecord( cycle_id=1, started_at="2026-01-01T00:00:00+00:00", errors=["backlog: connection refused"], ) assert r.healthy is False def test_cycle_record_unhealthy_with_warnings(): r = VassalCycleRecord( cycle_id=1, started_at="2026-01-01T00:00:00+00:00", house_warnings=["disk 90% full"], ) assert r.healthy is False # --------------------------------------------------------------------------- # VassalOrchestrator state # --------------------------------------------------------------------------- def test_orchestrator_initial_state(): orch = VassalOrchestrator() assert orch.cycle_count == 0 assert orch.is_running is False assert orch.history == [] def test_orchestrator_get_status_no_cycles(): orch = VassalOrchestrator() status = orch.get_status() assert status["running"] is False assert status["cycle_count"] == 0 assert status["last_cycle"] is None # --------------------------------------------------------------------------- # run_cycle — integration (no Gitea, no Ollama in test env) # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_run_cycle_completes_without_services(): """run_cycle must complete and record even when external services are down.""" from timmy.vassal.dispatch import clear_dispatch_registry clear_dispatch_registry() orch = VassalOrchestrator(cycle_interval=300) record = await orch.run_cycle() assert isinstance(record, VassalCycleRecord) assert record.cycle_id == 1 assert record.finished_at # was set assert record.duration_ms >= 0 # No Gitea → fetched = 0, dispatched = 0 assert record.issues_fetched == 0 assert record.issues_dispatched == 0 # History updated assert len(orch.history) == 1 assert orch.cycle_count == 1 @pytest.mark.asyncio async def test_run_cycle_increments_cycle_count(): from timmy.vassal.dispatch import clear_dispatch_registry clear_dispatch_registry() orch = VassalOrchestrator() await orch.run_cycle() await orch.run_cycle() assert orch.cycle_count == 2 assert len(orch.history) == 2 @pytest.mark.asyncio async def test_get_status_after_cycle(): from timmy.vassal.dispatch import clear_dispatch_registry clear_dispatch_registry() orch = VassalOrchestrator() await orch.run_cycle() status = orch.get_status() assert status["cycle_count"] == 1 last = status["last_cycle"] assert last is not None assert last["cycle_id"] == 1 assert last["issues_fetched"] == 0 # --------------------------------------------------------------------------- # start / stop # --------------------------------------------------------------------------- def test_orchestrator_stop_when_not_running(): """stop() on an idle orchestrator must not raise.""" orch = VassalOrchestrator() orch.stop() # should be a no-op assert orch.is_running is False # --------------------------------------------------------------------------- # Module-level singleton # --------------------------------------------------------------------------- def test_module_singleton_exists(): from timmy.vassal import VassalOrchestrator, vassal_orchestrator assert isinstance(vassal_orchestrator, VassalOrchestrator)