Add audit command and outbox foundations
This commit is contained in:
83
tests/test_audit_delivery.py
Normal file
83
tests/test_audit_delivery.py
Normal file
@@ -0,0 +1,83 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user