from types import SimpleNamespace import pytest from sqlalchemy import create_engine, select from sqlalchemy.orm import sessionmaker from app.core.background.task_queue import lifecycle as lifecycle_queue from app.core.database import Base from app.modules.reports.constants import ReportType from app.modules.workflows.constants import WorkflowStatus, WorkflowType from app.modules.workflows.models import WorkflowAction, WorkflowInstance from app.tasks import celery_app def test_lifecycle_enqueue_failure_marks_workflow_failed( monkeypatch: pytest.MonkeyPatch, ) -> None: engine = create_engine("sqlite://") Base.metadata.create_all(engine) factory = sessionmaker(bind=engine, expire_on_commit=False) class BrokenSignature: def apply_async(self) -> None: raise RuntimeError("broker unavailable") monkeypatch.setattr(lifecycle_queue, "SessionLocal", factory) monkeypatch.setattr( lifecycle_queue, "get_settings", lambda: SimpleNamespace(task_queue_enabled=True), ) monkeypatch.setattr( celery_app, "signature", lambda *args, **kwargs: BrokenSignature(), ) try: with pytest.raises(RuntimeError, match="broker unavailable"): lifecycle_queue.enqueue_lifecycle_report( report_type=ReportType.WEEKLY, actor="pytest", ) with factory() as db: workflow = db.execute( select(WorkflowInstance).where( WorkflowInstance.workflow_type == WorkflowType.LIFECYCLE_REPORT ) ).scalar_one() actions = list( db.execute( select(WorkflowAction) .where(WorkflowAction.workflow_code == workflow.code) .order_by(WorkflowAction.id.asc()) ).scalars() ) assert workflow.status == WorkflowStatus.FAILED assert workflow.current_step == "enqueue_failed" assert workflow.completed_at is not None assert workflow.payload == {"error": "broker unavailable"} assert [action.action for action in actions] == [ "queued", "enqueue_failed", ] assert actions[-1].to_status == WorkflowStatus.FAILED finally: engine.dispose()