""" Test event parsing from JSON payloads (mirrors market-data-service contracts). """ import json from senpai.md_consumer.models import ( EventType, TradeEvent, QuoteEvent, HeartbeatEvent, parse_event, ) # ── Trade events ─────────────────────────────────────────────────────── def test_parse_trade_basic(): data = { "event_type": "trade", "provider": "binance", "symbol": "BTCUSDT", "price": 70500.0, "size": 1.5, "ts_recv": "2026-02-09T12:00:00+00:00", } event = parse_event(data) assert event is not None assert isinstance(event, TradeEvent) assert event.event_type == EventType.TRADE assert event.symbol == "BTCUSDT" assert event.price == 70500.0 assert event.size == 1.5 assert event.provider == "binance" def test_parse_trade_with_extra_fields(): """Unknown fields should be silently ignored (tolerant parsing).""" data = { "event_type": "trade", "provider": "bybit", "symbol": "ETHUSDT", "price": 2100.0, "size": 10.0, "ts_recv": "2026-02-09T12:00:00+00:00", "unknown_field": "should_be_ignored", "another_extra": 42, } event = parse_event(data) assert event is not None assert event.symbol == "ETHUSDT" def test_parse_trade_with_side_and_exchange_ts(): data = { "event_type": "trade", "provider": "binance", "symbol": "BTCUSDT", "price": 70000.0, "size": 0.5, "side": "buy", "ts_exchange": "2026-02-09T12:00:00+00:00", "ts_recv": "2026-02-09T12:00:00.100+00:00", "trade_id": "t12345", } event = parse_event(data) assert event.side == "buy" assert event.trade_id == "t12345" assert event.ts_exchange is not None # ── Quote events ─────────────────────────────────────────────────────── def test_parse_quote_basic(): data = { "event_type": "quote", "provider": "binance", "symbol": "BTCUSDT", "bid": 70000.0, "ask": 70001.0, "bid_size": 5.0, "ask_size": 3.0, "ts_recv": "2026-02-09T12:00:00+00:00", } event = parse_event(data) assert isinstance(event, QuoteEvent) assert event.bid == 70000.0 assert event.ask == 70001.0 def test_parse_quote_zero_values(): data = { "event_type": "quote", "provider": "binance", "symbol": "BTCUSDT", "bid": 0.0, "ask": 0.0, "bid_size": 0.0, "ask_size": 0.0, } event = parse_event(data) assert event is not None assert event.bid == 0.0 # ── Heartbeat events ────────────────────────────────────────────────── def test_parse_heartbeat(): data = { "event_type": "heartbeat", "provider": "alpaca", "ts_recv": "2026-02-09T12:00:00+00:00", } event = parse_event(data) assert isinstance(event, HeartbeatEvent) assert event.provider == "alpaca" # ── Edge cases ───────────────────────────────────────────────────────── def test_parse_unknown_type(): data = {"event_type": "unknown_type", "provider": "test"} event = parse_event(data) assert event is None def test_parse_missing_type(): data = {"provider": "test", "symbol": "BTC"} event = parse_event(data) assert event is None def test_parse_invalid_data(): data = {"event_type": "trade"} # missing required fields event = parse_event(data) assert event is None def test_parse_empty_dict(): event = parse_event({}) assert event is None def test_parse_from_json_bytes(): """Simulate actual NATS message deserialization.""" raw = b'{"event_type":"trade","provider":"binance","symbol":"BTCUSDT","price":70500.0,"size":1.5}' data = json.loads(raw) event = parse_event(data) assert event is not None assert event.price == 70500.0