test: validate ledger backfill flow

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-08-24 10:25:35 +00:00
committed by GitHub
co-authored by genichin
parent e2c4bbba34
commit 3e74d35299
3 changed files with 17 additions and 14 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
"""LedgerOutbox + LedgerWriter direct unit tests (REQ-019).""" """LedgerOutbox + LedgerWriter direct unit tests (REQ-019)."""
from __future__ import annotations from __future__ import annotations
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from trex_core.adapters.ledger.in_memory import InMemoryLedger from trex_core.adapters.ledger.in_memory import InMemoryLedger
from trex_core.application.ledger_outbox import ( from trex_core.application.ledger_outbox import (
@@ -17,7 +17,7 @@ def _trade(execution_id="paper:PAPER-1:10") -> LedgerTrade:
return LedgerTrade( return LedgerTrade(
asset_id="PF-1", order_id="PAPER-1", execution_id=execution_id, asset_id="PF-1", order_id="PAPER-1", execution_id=execution_id,
side="buy", qty=10, price=1000.0, side="buy", qty=10, price=1000.0,
executed_at=datetime(2026, 6, 4, tzinfo=timezone.utc), executed_at=datetime(2026, 6, 4, tzinfo=UTC),
) )
@@ -57,7 +57,7 @@ def test_writer_marks_conflict_blocked_and_does_not_retry():
def test_writer_retries_until_success(): def test_writer_retries_until_success():
# dcy-015: retryable rows now back off; share an injected clock between the # dcy-015: retryable rows now back off; share an injected clock between the
# outbox (list_pending "now") and the writer (next_retry_at) to advance past it. # outbox (list_pending "now") and the writer (next_retry_at) to advance past it.
now = {"t": datetime(2026, 6, 4, tzinfo=timezone.utc)} now = {"t": datetime(2026, 6, 4, tzinfo=UTC)}
outbox = InMemoryLedgerOutbox(clock=lambda: now["t"]) outbox = InMemoryLedgerOutbox(clock=lambda: now["t"])
ledger = InMemoryLedger(fail_n_then_succeed=1) ledger = InMemoryLedger(fail_n_then_succeed=1)
writer = LedgerWriter(outbox, ledger, clock=lambda: now["t"]) writer = LedgerWriter(outbox, ledger, clock=lambda: now["t"])
+2 -2
View File
@@ -147,8 +147,8 @@ def test_ledger_backfill_existing_completed_row_is_idempotent(monkeypatch, tmp_p
def process_correlation_id(self, correlation_id): def process_correlation_id(self, correlation_id):
raise AssertionError("completed execution must not be re-processed") raise AssertionError("completed execution must not be re-processed")
import trex_core.adapters.persistence.postgresql as pg_mod
import trex_core.adapters.ledger.portfolio_hub as hub_mod import trex_core.adapters.ledger.portfolio_hub as hub_mod
import trex_core.adapters.persistence.postgresql as pg_mod
monkeypatch.setattr(pg_mod, "PostgreSQLLedgerOutbox", lambda dsn: _Outbox()) monkeypatch.setattr(pg_mod, "PostgreSQLLedgerOutbox", lambda dsn: _Outbox())
monkeypatch.setattr(hub_mod, "PortfolioHubLedger", lambda *args, **kwargs: object()) monkeypatch.setattr(hub_mod, "PortfolioHubLedger", lambda *args, **kwargs: object())
@@ -195,8 +195,8 @@ def test_ledger_backfill_live_enqueue_and_processes_exact_trade(monkeypatch, tmp
assert correlation_id == self.seen_outbox.trade.correlation_id assert correlation_id == self.seen_outbox.trade.correlation_id
return OutboxOutcome.COMPLETED return OutboxOutcome.COMPLETED
import trex_core.adapters.persistence.postgresql as pg_mod
import trex_core.adapters.ledger.portfolio_hub as hub_mod import trex_core.adapters.ledger.portfolio_hub as hub_mod
import trex_core.adapters.persistence.postgresql as pg_mod
monkeypatch.setattr(pg_mod, "PostgreSQLLedgerOutbox", lambda dsn: outbox) monkeypatch.setattr(pg_mod, "PostgreSQLLedgerOutbox", lambda dsn: outbox)
monkeypatch.setattr(hub_mod, "PortfolioHubLedger", lambda *args, **kwargs: object()) monkeypatch.setattr(hub_mod, "PortfolioHubLedger", lambda *args, **kwargs: object())
+12 -9
View File
@@ -38,8 +38,8 @@ import json as _json
import re import re
import sys import sys
import uuid import uuid
from pathlib import Path
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from trex_core.adapters.messaging import topology from trex_core.adapters.messaging import topology
@@ -394,7 +394,8 @@ def _write_ledger_backfill_audit(args, report: dict) -> str:
def _lookup_ledger_backfill_evidence(cfg: DaemonConfig, *, trading_date: str, execution_id: str): def _lookup_ledger_backfill_evidence(cfg: DaemonConfig, *, trading_date: str, execution_id: str):
from trex_core.runtime.broker_provider import ( from trex_core.runtime.broker_provider import (
BrokerCredentialUnavailable, build_broker_provider, BrokerCredentialUnavailable,
build_broker_provider,
) )
broker, source = build_broker_provider(cfg).get_broker_for_read() broker, source = build_broker_provider(cfg).get_broker_for_read()
@@ -664,10 +665,11 @@ def _cmd_sync(args, cfg, confirm) -> int:
return 1 return 1
# REQ-119/120: resolve and validate broker truth before opening/enumerating DB. # REQ-119/120: resolve and validate broker truth before opening/enumerating DB.
from trex_core.runtime.broker_provider import (
BrokerCredentialUnavailable, build_broker_provider,
)
from trex_core.application.broker_port import BrokerBalanceError from trex_core.application.broker_port import BrokerBalanceError
from trex_core.runtime.broker_provider import (
BrokerCredentialUnavailable,
build_broker_provider,
)
try: try:
broker_port, _source = build_broker_provider(cfg).get_broker_for_read() broker_port, _source = build_broker_provider(cfg).get_broker_for_read()
broker_port = _CachedBalanceBroker(broker_port, broker_port.get_balance()) broker_port = _CachedBalanceBroker(broker_port, broker_port.get_balance())
@@ -787,10 +789,11 @@ def _cmd_verify(args, cfg) -> int:
instance = _resolve_instance(args) instance = _resolve_instance(args)
# REQ-120 no-write/no-enumeration barrier also applies to verify. # REQ-120 no-write/no-enumeration barrier also applies to verify.
from trex_core.runtime.broker_provider import (
BrokerCredentialUnavailable, build_broker_provider,
)
from trex_core.application.broker_port import BrokerBalanceError from trex_core.application.broker_port import BrokerBalanceError
from trex_core.runtime.broker_provider import (
BrokerCredentialUnavailable,
build_broker_provider,
)
try: try:
broker_port, _source = build_broker_provider(cfg).get_broker_for_read() broker_port, _source = build_broker_provider(cfg).get_broker_for_read()
broker_port = _CachedBalanceBroker(broker_port, broker_port.get_balance()) broker_port = _CachedBalanceBroker(broker_port, broker_port.get_balance())
@@ -987,9 +990,9 @@ def _cmd_report(args, cfg) -> int:
def _cmd_ledger_backfill(args, cfg, confirm) -> int: def _cmd_ledger_backfill(args, cfg, confirm) -> int:
from trex_core.adapters.broker.kis import KISEvidenceLookupError
from trex_core.adapters.ledger.portfolio_hub import PortfolioHubLedger from trex_core.adapters.ledger.portfolio_hub import PortfolioHubLedger
from trex_core.adapters.persistence.postgresql import PostgreSQLLedgerOutbox from trex_core.adapters.persistence.postgresql import PostgreSQLLedgerOutbox
from trex_core.adapters.broker.kis import KISEvidenceLookupError
from trex_core.runtime.broker_provider import BrokerCredentialUnavailable from trex_core.runtime.broker_provider import BrokerCredentialUnavailable
instance = _resolve_instance(args) instance = _resolve_instance(args)