"""KIS current-price adapter unit tests (REQ-038 / BAT-011). Network is monkeypatched — these run always (no KIS credentials needed). """ from __future__ import annotations from decimal import Decimal import httpx from trex_core.adapters.broker.kis import KISBroker from trex_core.adapters.broker.paper import PaperBroker _MOCK_URL = "https://openapivts.koreainvestment.com:29443" _LIVE_URL = "https://openapi.koreainvestment.com:9443" class _FakeResp: def __init__(self, payload): self._payload = payload def json(self): return self._payload def _mock_broker(): return KISBroker("12345678-01", app_key="k", app_secret="s", base_url=_MOCK_URL) def _live_broker(): return KISBroker("12345678-01", app_key="k", app_secret="s", base_url=_LIVE_URL, allow_live=True) def test_tr_price_live_vs_mock(): """AC-7: live uses TTTC8397R, mock uses VTTC8397R.""" assert _live_broker()._tr_price() == "TTTC8397R" assert _mock_broker()._tr_price() == "VTTC8397R" def test_get_current_price_parses_stck_prpr(monkeypatch): """get_current_price parses output.stck_prpr into a Decimal.""" captured = {} def fake_get(url, headers=None, params=None, timeout=None): captured["url"] = url captured["tr_id"] = headers["tr_id"] captured["params"] = params return _FakeResp({"rt_cd": "0", "output": {"stck_prpr": "49000"}}) b = _mock_broker() monkeypatch.setattr(b, "_ensure_token", lambda: "tok") monkeypatch.setattr(httpx, "get", fake_get) price = b.get_current_price("005930") assert price == Decimal("49000") assert captured["tr_id"] == "VTTC8397R" assert captured["params"]["FID_INPUT_ISCD"] == "005930" def test_get_current_price_network_error_returns_none(monkeypatch): """Network error → None (worker skips the slot, no false trigger).""" def boom(*a, **kw): raise httpx.ConnectError("down") b = _mock_broker() monkeypatch.setattr(b, "_ensure_token", lambda: "tok") monkeypatch.setattr(httpx, "get", boom) assert b.get_current_price("005930") is None def test_get_current_price_error_rt_cd_returns_none(monkeypatch): """Non-zero rt_cd → None.""" def fake_get(*a, **kw): return _FakeResp({"rt_cd": "1", "msg1": "error"}) b = _mock_broker() monkeypatch.setattr(b, "_ensure_token", lambda: "tok") monkeypatch.setattr(httpx, "get", fake_get) assert b.get_current_price("005930") is None def test_get_current_price_missing_field_returns_none(monkeypatch): """Missing/blank stck_prpr → None.""" def fake_get(*a, **kw): return _FakeResp({"rt_cd": "0", "output": {"stck_prpr": ""}}) b = _mock_broker() monkeypatch.setattr(b, "_ensure_token", lambda: "tok") monkeypatch.setattr(httpx, "get", fake_get) assert b.get_current_price("005930") is None class TestPaperCurrentPrice: def test_unset_returns_none(self): assert PaperBroker().get_current_price("005930") is None def test_set_and_get(self): b = PaperBroker() b.set_current_price("005930", Decimal("51000")) assert b.get_current_price("005930") == Decimal("51000")