74 lines
3.0 KiB
Python
74 lines
3.0 KiB
Python
|
|
"""Kernel-level finiteness guard: non-finite (inf/NaN) intents must be rejected
|
||
|
|
with INVALID_INTENT, never crash the kernel ("Rust kernel returned null string").
|
||
|
|
|
||
|
|
Two layers (defense in depth):
|
||
|
|
- Python bridge (ExecutionKernel.process_intent): rejects non-finite/insane fields
|
||
|
|
before the FFI call, naming the offending field for source-location.
|
||
|
|
- Rust kernel (FFI): a payload that fails to parse (incl. the Infinity/NaN tokens
|
||
|
|
serde rejects) or a result that fails to serialize returns a clean INVALID_INTENT
|
||
|
|
outcome instead of a null string.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from prod.clean_arch.dita_v2 import (
|
||
|
|
ExecutionKernel, InMemoryControlPlane, KernelCommandType, KernelControlSnapshot,
|
||
|
|
KernelMode, KernelVerbosity, MemoryKernelJournal, MockVenueAdapter, MockVenueScenario,
|
||
|
|
TradeSide,
|
||
|
|
)
|
||
|
|
from prod.clean_arch.dita_v2.contracts import KernelDiagnosticCode, KernelIntent
|
||
|
|
from prod.clean_arch.dita_v2.rust_backend import _get_rust, _intent_to_payload
|
||
|
|
|
||
|
|
|
||
|
|
def _kernel():
|
||
|
|
return ExecutionKernel(
|
||
|
|
control_plane=InMemoryControlPlane(
|
||
|
|
KernelControlSnapshot(mode=KernelMode.DEBUG, verbosity=KernelVerbosity.TRACE)
|
||
|
|
),
|
||
|
|
venue=MockVenueAdapter(MockVenueScenario(emit_fill_on_submit=True, partial_fill_ratio=1.0)),
|
||
|
|
journal=MemoryKernelJournal(),
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _intent(size, price, lev=3.0):
|
||
|
|
return KernelIntent(
|
||
|
|
timestamp=datetime.now(timezone.utc), intent_id="i", trade_id="T", slot_id=0,
|
||
|
|
asset="BTCUSDT", side=TradeSide.SHORT, action=KernelCommandType.ENTER,
|
||
|
|
reference_price=price, target_size=size, leverage=lev, exit_leg_ratios=(1.0,), reason="X",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("size,price,lev,field", [
|
||
|
|
(float("inf"), 100.0, 3.0, "target_size"),
|
||
|
|
(float("nan"), 100.0, 3.0, "target_size"),
|
||
|
|
(0.1, float("inf"), 3.0, "reference_price"),
|
||
|
|
(0.1, 100.0, float("nan"), "leverage"),
|
||
|
|
(-0.1, 100.0, 3.0, "target_size"),
|
||
|
|
])
|
||
|
|
def test_bridge_rejects_nonfinite_intent(size, price, lev, field):
|
||
|
|
out = _kernel().process_intent(_intent(size, price, lev))
|
||
|
|
assert out.accepted is False
|
||
|
|
assert out.diagnostic_code == KernelDiagnosticCode.INVALID_INTENT
|
||
|
|
assert out.details.get("field") == field
|
||
|
|
|
||
|
|
|
||
|
|
def test_finite_intent_still_accepted():
|
||
|
|
out = _kernel().process_intent(_intent(0.15, 100000.0))
|
||
|
|
assert out.accepted is True
|
||
|
|
assert out.diagnostic_code == KernelDiagnosticCode.OK
|
||
|
|
|
||
|
|
|
||
|
|
def test_rust_kernel_rejects_nonfinite_payload_without_null_crash():
|
||
|
|
# Bypass the Python bridge guard: hand a non-finite payload straight to the
|
||
|
|
# Rust FFI (json.dumps emits the Infinity token serde rejects). The kernel
|
||
|
|
# must return a clean INVALID_INTENT outcome, not a null string.
|
||
|
|
k = _kernel()
|
||
|
|
payload = _intent_to_payload(_intent(float("inf"), 100.0))
|
||
|
|
res = _get_rust().process_intent(k._backend, payload, mode="NORMAL", verbosity="QUIET")
|
||
|
|
assert res["outcome"]["diagnostic_code"] == "INVALID_INTENT"
|
||
|
|
assert res["outcome"]["accepted"] is False
|