84 lines
3.3 KiB
Python
84 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from govoplan_audit.backend.commands import AuditCommand, CommandBus
|
|
from govoplan_audit.backend.db.models import AuditOutboxEvent
|
|
from govoplan_audit.backend.outbox import SqlAuditOutbox
|
|
from govoplan_core.core.events import EventActorRef, PlatformEvent
|
|
from govoplan_core.db.base import Base
|
|
|
|
|
|
class AuditCommandBusTests(unittest.TestCase):
|
|
def test_command_bus_dispatches_commands_separately_from_events(self) -> None:
|
|
bus = CommandBus()
|
|
seen: list[AuditCommand] = []
|
|
wildcard: list[AuditCommand] = []
|
|
|
|
bus.subscribe("retention.run", seen.append)
|
|
bus.subscribe("*", wildcard.append)
|
|
command = AuditCommand(type="retention.run", module_id="policy", payload={"dry_run": True})
|
|
|
|
bus.dispatch(command)
|
|
|
|
self.assertEqual([command], seen)
|
|
self.assertEqual([command], wildcard)
|
|
self.assertEqual("retention.run", command.to_dict()["type"])
|
|
|
|
|
|
class AuditOutboxTests(unittest.TestCase):
|
|
def test_outbox_enqueues_governed_event_and_dispatches_pending_rows(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(bind=engine, tables=[AuditOutboxEvent.__table__])
|
|
Session = sessionmaker(bind=engine)
|
|
outbox = SqlAuditOutbox()
|
|
seen: list[PlatformEvent] = []
|
|
|
|
with Session() as session:
|
|
event = PlatformEvent(
|
|
type="tenant.created",
|
|
module_id="tenancy",
|
|
payload={"tenant_id": "tenant-1"},
|
|
actor=EventActorRef(type="user", id="user-1"),
|
|
)
|
|
row = outbox.enqueue(session, event)
|
|
|
|
self.assertEqual("pending", row.status)
|
|
self.assertEqual("tenant.created", row.event_type)
|
|
self.assertEqual(event.event_id, row.event_id)
|
|
self.assertEqual(event.event_id, row.correlation_id)
|
|
self.assertEqual("user-1", row.payload["actor"]["id"])
|
|
|
|
counts = outbox.dispatch_pending(session, dispatcher=seen.append)
|
|
|
|
self.assertEqual({"selected": 1, "dispatched": 1, "failed": 0}, counts)
|
|
self.assertEqual(1, len(seen))
|
|
self.assertEqual("tenant.created", seen[0].type)
|
|
self.assertEqual("dispatched", row.status)
|
|
self.assertEqual(1, row.attempts)
|
|
self.assertIsNotNone(row.dispatched_at)
|
|
|
|
def test_outbox_records_failed_dispatch_for_retry(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(bind=engine, tables=[AuditOutboxEvent.__table__])
|
|
Session = sessionmaker(bind=engine)
|
|
outbox = SqlAuditOutbox()
|
|
|
|
with Session() as session:
|
|
row = outbox.enqueue(session, PlatformEvent(type="demo.failed", module_id="audit"))
|
|
|
|
counts = outbox.dispatch_pending(session, dispatcher=lambda event: (_ for _ in ()).throw(RuntimeError("offline")))
|
|
|
|
self.assertEqual({"selected": 1, "dispatched": 0, "failed": 1}, counts)
|
|
self.assertEqual("failed", row.status)
|
|
self.assertEqual(1, row.attempts)
|
|
self.assertEqual("offline", row.last_error)
|
|
self.assertIsNotNone(row.next_attempt_at)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|