feat(intents): dcy-011 buy intent portfolio_id 필수 계약 + ledger skip 가시화 (BAT-015)
dcy-011 전 주기 delivery. issue #6(buy 식별자 정정) + issue #11(ledger silent skip) 흡수. 거버넌스: 기존 Implemented REQ는 change-req, 신규 기능만 신규 REQ(OQ-3). - REQ-007 (change-req): buy admission portfolio_id 필수화 + SnapshotStore symbol resolve + unknown_portfolio 거부 (hard cutover, OQ-1/OQ-2) - REQ-021 (change-req): buy 메시지 payload symbol 제거 - REQ-018 (change-req): _record_ledger skip 가시화 (WARN + ledger_skipped 카운터) - REQ-055 (신규): closed↔ledger_outbox anti-join 운영 점검 find_ledger_gap_slots 코드: intents.py(BuyIntent portfolio_id 필수 + resolve_symbol 주입 + _admit_buy reject 분기), messaging_handlers.py, runtime/daemon.py(_resolve_symbol 배선), execution.py(skip WARN 로그), adapters/persistence/ops_checks.py(신규 anti-join). 검증: 단위 282 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 314 passed, ruff clean. BAT-015(standard) release-candidate. srs/index Next ID → REQ-056. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -80,7 +80,13 @@ def test_portfolio_id_absent_skips_ledger_but_transitions_slot():
|
||||
ledger = InMemoryLedger()
|
||||
repo, svc, intents, execution, outbox, writer = _wire(PaperBroker(fill_after_polls=0),
|
||||
ledger=ledger)
|
||||
sid = _buy(intents, iid="no-pf", portfolio_id=None).slot_id
|
||||
# dcy-011: buy admission now requires portfolio_id, so create the
|
||||
# pending_buy slot directly (bypass admission) to exercise the
|
||||
# execution-loop ledger skip + visibility (REQ-018 FR-4).
|
||||
idle = repo.find_idle_slot(DAY)
|
||||
svc.transit_slot(idle.slot_id, SlotState.IDLE, SlotState.PENDING_BUY,
|
||||
reason="buy", actor="test", symbol="005930", portfolio_id=None)
|
||||
sid = idle.slot_id
|
||||
r = execution.run_once()
|
||||
assert r.filled == 1 and r.ledger_enqueued == 0 and r.ledger_skipped == 1
|
||||
assert repo.fetch_slot(sid).status is SlotState.HOLDING
|
||||
@@ -88,6 +94,21 @@ def test_portfolio_id_absent_skips_ledger_but_transitions_slot():
|
||||
assert ledger.count() == 0
|
||||
|
||||
|
||||
def test_ledger_skip_emits_warning(caplog):
|
||||
"""dcy-011 / REQ-018 FR-4 (S-3): ledger skip is logged, not silent."""
|
||||
import logging
|
||||
ledger = InMemoryLedger()
|
||||
repo, svc, intents, execution, outbox, writer = _wire(PaperBroker(fill_after_polls=0),
|
||||
ledger=ledger)
|
||||
idle = repo.find_idle_slot(DAY)
|
||||
svc.transit_slot(idle.slot_id, SlotState.IDLE, SlotState.PENDING_BUY,
|
||||
reason="buy", actor="test", symbol="005930", portfolio_id=None)
|
||||
with caplog.at_level(logging.WARNING, logger="trex_core.application.execution"):
|
||||
execution.run_once()
|
||||
msgs = [r.getMessage() for r in caplog.records]
|
||||
assert any("ledger skip" in m and "missing_portfolio_id" in m for m in msgs)
|
||||
|
||||
|
||||
def test_duplicate_execution_id_results_in_zero_duplicate_in_ledger():
|
||||
"""REQ-019 AC-1: same execution_id processed twice = 1 ledger entry."""
|
||||
ledger = InMemoryLedger()
|
||||
|
||||
Reference in New Issue
Block a user