import pytest import os import tempfile from unittest.mock import AsyncMock, MagicMock, patch from agent.task_queue import PersistentTaskQueue from agent.recovery import recover_on_startup, ContainerCleaner @pytest.fixture async def task_queue(): fd, db_path = tempfile.mkstemp(suffix=".db") os.close(fd) queue = PersistentTaskQueue(db_path=db_path) await queue.initialize() yield queue await queue.close() os.unlink(db_path) @pytest.mark.asyncio async def test_recover_resets_running_to_pending(task_queue): await task_queue.enqueue("thread-1", "gitea", {"msg": "interrupted"}) await task_queue.dequeue() # → running assert await task_queue.has_running_task("thread-1") is True with patch("agent.recovery._cleanup_zombie_containers", new_callable=AsyncMock): await recover_on_startup(task_queue) assert await task_queue.has_running_task("thread-1") is False pending = await task_queue.get_pending() assert len(pending) == 1 @pytest.mark.asyncio async def test_recover_no_running_tasks(task_queue): with patch("agent.recovery._cleanup_zombie_containers", new_callable=AsyncMock): await recover_on_startup(task_queue) pending = await task_queue.get_pending() assert len(pending) == 0 @pytest.mark.asyncio async def test_container_cleaner_removes_old(): mock_container = MagicMock() mock_container.name = "galaxis-sandbox-old" mock_container.labels = {"galaxis-agent-sandbox": "true"} mock_container.attrs = {"Created": "2026-03-19T00:00:00Z"} mock_container.stop = MagicMock() mock_container.remove = MagicMock() mock_docker = MagicMock() mock_docker.containers.list.return_value = [mock_container] cleaner = ContainerCleaner(docker_client=mock_docker, max_age_seconds=600) removed = await cleaner.cleanup_once() assert removed == 1 mock_container.stop.assert_called_once() mock_container.remove.assert_called_once() @pytest.mark.asyncio async def test_container_cleaner_keeps_recent(): from datetime import datetime, timezone now = datetime.now(timezone.utc).isoformat() mock_container = MagicMock() mock_container.labels = {"galaxis-agent-sandbox": "true"} mock_container.attrs = {"Created": now} mock_docker = MagicMock() mock_docker.containers.list.return_value = [mock_container] cleaner = ContainerCleaner(docker_client=mock_docker, max_age_seconds=3600) removed = await cleaner.cleanup_once() assert removed == 0 mock_container.stop.assert_not_called()