Files
siloqy/prod/tests/test_pink_ditav2_restart_reconcile.py

76 lines
2.3 KiB
Python
Raw Normal View History

"""Crash/restart reconcile convergence tests for PINK → DITAv2."""
from __future__ import annotations
from datetime import datetime, timezone
import unittest
from prod.clean_arch.dita_v2 import (
ExecutionKernel,
InMemoryControlPlane,
InMemoryZincPlane,
KernelCommandType,
KernelIntent,
MockVenueAdapter,
MockVenueScenario,
TradeSide,
TradeSlot,
TradeStage,
)
class TestRestartReconcile(unittest.TestCase):
"""Verify exchange-led state convergence after simulated crash/restart."""
def setUp(self):
self.control = InMemoryControlPlane()
self.venue = MockVenueAdapter() # deterministic mock
self.kernel = ExecutionKernel(
max_slots=2,
control_plane=self.control,
venue=self.venue,
zinc_plane=InMemoryZincPlane(),
)
def _enter_position(self) -> None:
intent = KernelIntent(
timestamp=datetime.now(timezone.utc),
intent_id="entry-001",
trade_id="trade-001",
slot_id=0,
asset="BTCUSDT",
side=TradeSide.SHORT,
action=KernelCommandType.ENTER,
reference_price=65000.0,
target_size=0.01,
leverage=2.0,
reason="restart_test_entry",
)
self.kernel.process_intent(intent)
def test_entry_opens_slot(self):
self._enter_position()
slot = self.kernel.slot(0)
self.assertTrue(slot.is_open(),
f"Expected open slot after entry, got {slot.fsm_state}")
def test_reconcile_with_empty_does_not_crash(self):
self._enter_position()
# Reconcile with empty list — no-op
outcome = self.kernel.reconcile_from_slots([])
self.assertIsNotNone(outcome,
"Reconcile should return an outcome")
def test_capital_seed_after_reconcile(self):
self._enter_position()
capital_before = self.kernel.account.snapshot.capital
self.assertGreater(capital_before, 0)
self.kernel.reconcile_from_slots([])
capital_after = self.kernel.account.snapshot.capital
self.assertEqual(capital_after, capital_before,
"Capital should not change during reconcile")
if __name__ == "__main__":
unittest.main()