from contextlib import asynccontextmanager from types import SimpleNamespace from unittest.mock import AsyncMock from uuid import uuid4 import pytest from app.domain.domain_errors import DomainError from app.domain.schemas.operational_task import AgentRunCommand from app.infra.db.postgresql.operational_tasks import OperationalTaskRepository from app.services.operational_tasks import OperationalTaskService @pytest.fixture def anyio_backend(): return "asyncio" @pytest.mark.anyio @pytest.mark.parametrize("available", [False, True]) async def test_execution_requires_message_storage(available): cursor = SimpleNamespace(fetchone=AsyncMock(return_value={"available": available})) connection = SimpleNamespace(execute=AsyncMock(return_value=cursor)) repository = OperationalTaskRepository(connection, uuid4()) if available: await repository.require_execution_storage() else: with pytest.raises(DomainError) as failure: await repository.require_execution_storage() assert failure.value.status == 503 assert failure.value.code == "DEPENDENCY_UNAVAILABLE" assert "024_agent_messages.sql" in failure.value.message assert "to_regclass('workbench.agent_messages')" in connection.execute.call_args.args[0] @pytest.mark.anyio async def test_missing_message_storage_rejects_claim_before_state_changes(): @asynccontextmanager async def transaction(): yield repository = SimpleNamespace( transaction=transaction, require_execution_storage=AsyncMock(side_effect=DomainError( "DEPENDENCY_UNAVAILABLE", "Apply 024_agent_messages.sql", status=503, )), get_request=AsyncMock(return_value=None), lock=AsyncMock(), update_run=AsyncMock(), update_task=AsyncMock(), ) service = OperationalTaskService(repository, SimpleNamespace(), set()) with pytest.raises(DomainError, match="024_agent_messages.sql"): await service.claim_run(uuid4(), uuid4(), AgentRunCommand( request_id=uuid4(), expected_revision=1, source_session_id="test-session", )) repository.get_request.assert_not_awaited() repository.lock.assert_not_awaited() repository.update_run.assert_not_awaited() repository.update_task.assert_not_awaited()