88 lines
2.9 KiB
Python
88 lines
2.9 KiB
Python
"""Control channel action dispatch tests (REQ-043 / BAT-012)."""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from trex_core.application.messaging import RpcRequest
|
|
from trex_core.application.messaging_handlers import make_control_rpc_handler
|
|
from trex_core.application.portfolio import InMemoryPauseStore
|
|
|
|
|
|
class _FakeKillSwitch:
|
|
def __init__(self):
|
|
self.fired = []
|
|
self.resumed = []
|
|
|
|
def fire(self, *, scope, portfolio_id=None, actor="operator"):
|
|
self.fired.append((scope, portfolio_id, actor))
|
|
return 3
|
|
|
|
def resume(self, *, scope, portfolio_id=None):
|
|
self.resumed.append((scope, portfolio_id))
|
|
|
|
|
|
def _req(**body):
|
|
return RpcRequest(payload=body, correlation_id="c1")
|
|
|
|
|
|
@pytest.fixture
|
|
def ks():
|
|
return _FakeKillSwitch()
|
|
|
|
|
|
@pytest.fixture
|
|
def handler(ks):
|
|
return make_control_rpc_handler(InMemoryPauseStore(), kill_switch_service=ks)
|
|
|
|
|
|
def test_pause_action_delegates(handler):
|
|
"""AC-1: pause delegates to REQ-032 pause handler."""
|
|
resp = handler(_req(action="pause", portfolio_id="pf-A"))
|
|
assert resp.payload["status"] == "ok"
|
|
assert "pf-A" in resp.payload["paused"]
|
|
|
|
|
|
def test_resume_and_list_actions(handler):
|
|
"""AC-2: resume/list delegate to pause handler."""
|
|
handler(_req(action="pause", portfolio_id="pf-A"))
|
|
assert handler(_req(action="resume", portfolio_id="pf-A")).payload["status"] == "ok"
|
|
assert handler(_req(action="list")).payload["status"] == "ok"
|
|
|
|
|
|
def test_kill_switch_routed(handler, ks):
|
|
"""AC-3: kill_switch routes to the kill-switch service."""
|
|
resp = handler(_req(action="kill_switch", scope="global"))
|
|
assert resp.payload["status"] == "ok"
|
|
assert resp.payload["liquidated"] == 3
|
|
assert ks.fired == [("global", None, "operator")]
|
|
|
|
|
|
def test_resume_kill_switch_routed(handler, ks):
|
|
"""AC-4: resume_kill_switch routes to the kill-switch service."""
|
|
resp = handler(_req(action="resume_kill_switch", scope="portfolio", portfolio_id="pf-A"))
|
|
assert resp.payload["status"] == "ok"
|
|
assert ks.resumed == [("portfolio", "pf-A")]
|
|
|
|
|
|
def test_unknown_action(handler):
|
|
"""AC-5: unknown action → error."""
|
|
resp = handler(_req(action="bogus"))
|
|
assert resp.payload["status"] == "error"
|
|
assert resp.payload["reason"] == "unknown_action:bogus"
|
|
|
|
|
|
def test_kill_switch_portfolio_requires_id(handler):
|
|
resp = handler(_req(action="kill_switch", scope="portfolio"))
|
|
assert resp.payload == {"status": "error", "reason": "portfolio_id_required"}
|
|
|
|
|
|
def test_kill_switch_bad_scope(handler):
|
|
resp = handler(_req(action="kill_switch", scope="weird"))
|
|
assert resp.payload["reason"] == "bad_scope:weird"
|
|
|
|
|
|
def test_kill_switch_unsupported_without_service():
|
|
handler = make_control_rpc_handler(InMemoryPauseStore()) # no service
|
|
resp = handler(_req(action="kill_switch", scope="global"))
|
|
assert resp.payload == {"status": "error", "reason": "kill_switch_unsupported"}
|