from datetime import UTC, datetime from uuid import uuid4 import pytest from sqlalchemy import delete, select from app.infrastructure.db import SessionFactory from app.model.audit import InteractionAudit from app.model.platform import DomainEventOutbox from app.service.outbox_admin_service import OutboxAdminService # 依赖真实 MySQL:必须打 integration marker,否则按 marker 过滤时会漏测这批用例。 pytestmark = pytest.mark.integration @pytest.mark.asyncio async def test_dead_event_replay_is_audited() -> None: event_id = str(uuid4()) now = datetime.now(UTC).replace(tzinfo=None) try: async with SessionFactory() as session: session.add( DomainEventOutbox( event_id=event_id, event_type="test.dead", aggregate_type="test", aggregate_id=event_id, trace_id=event_id, payload={}, status="dead", retry_count=5, last_error="RuntimeError", occurred_at=now, created_at=now, updated_at=now, ) ) await session.commit() async with SessionFactory() as session: event = await OutboxAdminService(session).replay_dead(event_id, actor_id=1) await session.commit() assert event.status == "pending" assert event.retry_count == 0 async with SessionFactory() as session: audit = await session.scalar( select(InteractionAudit).where( InteractionAudit.action_type == "outbox.dead_replay", InteractionAudit.detail["event_id"] == event_id, ) ) assert audit is not None finally: async with SessionFactory() as session: await session.execute( delete(InteractionAudit).where( InteractionAudit.action_type == "outbox.dead_replay", InteractionAudit.detail["event_id"] == event_id, ) ) await session.execute( delete(DomainEventOutbox).where(DomainEventOutbox.event_id == event_id) ) await session.commit()