"""LedgerOutbox + LedgerWriter direct unit tests (REQ-019).""" from __future__ import annotations from datetime import datetime, timezone from trex_core.adapters.ledger.in_memory import InMemoryLedger from trex_core.application.ledger_outbox import ( InMemoryLedgerOutbox, LedgerWriter, OutboxOutcome, WorkItemStatus, ) from trex_core.application.ledger_port import LedgerTrade def _trade(execution_id="paper:PAPER-1:10") -> LedgerTrade: return LedgerTrade( asset_id="PF-1", order_id="PAPER-1", execution_id=execution_id, side="buy", qty=10, price=1000.0, executed_at=datetime(2026, 6, 4, tzinfo=timezone.utc), ) def test_enqueue_is_idempotent_on_correlation_id(): outbox = InMemoryLedgerOutbox() a = outbox.enqueue(_trade()) b = outbox.enqueue(_trade()) assert a.correlation_id == b.correlation_id assert len(outbox.all_items()) == 1 def test_writer_run_once_drains_pending_and_marks_completed(): outbox = InMemoryLedgerOutbox() ledger = InMemoryLedger() writer = LedgerWriter(outbox, ledger) outbox.enqueue(_trade(execution_id="e-1")) outbox.enqueue(_trade(execution_id="e-2")) stats = writer.run_once() assert stats.completed == 2 and ledger.count() == 2 assert all(it.status is WorkItemStatus.COMPLETED for it in outbox.all_items()) assert all(it.last_outcome is OutboxOutcome.COMPLETED for it in outbox.all_items()) def test_writer_marks_conflict_blocked_and_does_not_retry(): outbox = InMemoryLedgerOutbox() ledger = InMemoryLedger(conflict_executions={"e-x"}) writer = LedgerWriter(outbox, ledger) outbox.enqueue(_trade(execution_id="e-x")) writer.run_once() [item] = outbox.all_items() assert item.status is WorkItemStatus.CONFLICT_BLOCKED assert item.last_outcome is OutboxOutcome.CONFLICT_BLOCKED # No pending items remain, so a subsequent run is a no-op. assert outbox.list_pending() == [] def test_writer_retries_until_success(): outbox = InMemoryLedgerOutbox() ledger = InMemoryLedger(fail_n_then_succeed=1) writer = LedgerWriter(outbox, ledger) outbox.enqueue(_trade(execution_id="e-r")) s1 = writer.run_once() assert s1.retryable == 1 and ledger.count() == 0 s2 = writer.run_once() assert s2.completed == 1 and ledger.count() == 1