build 059.3: parse websocket OHLC events
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# app/tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_parser.py
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import MappingProxyType
|
||||
|
||||
import pytest
|
||||
|
||||
from src.market_data.acquisition.adapters.dzengi.models import (
|
||||
DzengiWebSocketOhlcEvent,
|
||||
)
|
||||
from src.market_data.acquisition.adapters.dzengi.parser import (
|
||||
parse_dzengi_websocket_ohlc,
|
||||
)
|
||||
from src.market_data.acquisition.exceptions import (
|
||||
CandleWebSocketParseError,
|
||||
)
|
||||
from src.market_data.acquisition.validation.schema import (
|
||||
ValidatedWebSocketOhlcDocument,
|
||||
)
|
||||
|
||||
|
||||
def _validated_document(
|
||||
**payload_overrides: object,
|
||||
) -> ValidatedWebSocketOhlcDocument:
|
||||
payload: dict[str, object] = {
|
||||
"symbol": "BTC/USD_LEVERAGE",
|
||||
"interval": "1m",
|
||||
"type": "classic",
|
||||
"t": 1784224740000,
|
||||
"o": 63992.0,
|
||||
"h": 64032.55,
|
||||
"l": 63984.0,
|
||||
"c": 64032.55,
|
||||
}
|
||||
payload.update(payload_overrides)
|
||||
|
||||
return ValidatedWebSocketOhlcDocument(
|
||||
payload=MappingProxyType(payload),
|
||||
status="OK",
|
||||
destination="ohlc.event",
|
||||
correlation_id=None,
|
||||
)
|
||||
|
||||
|
||||
def test_parse_websocket_ohlc_returns_transport_model() -> None:
|
||||
result = parse_dzengi_websocket_ohlc(
|
||||
_validated_document()
|
||||
)
|
||||
|
||||
assert result == DzengiWebSocketOhlcEvent(
|
||||
symbol="BTC/USD_LEVERAGE",
|
||||
interval="1m",
|
||||
candle_type="classic",
|
||||
open_time=1784224740000,
|
||||
open_price=63992.0,
|
||||
high_price=64032.55,
|
||||
low_price=63984.0,
|
||||
close_price=64032.55,
|
||||
)
|
||||
|
||||
|
||||
def test_parse_websocket_ohlc_preserves_heikin_ashi_type() -> None:
|
||||
result = parse_dzengi_websocket_ohlc(
|
||||
_validated_document(type="heikin-ashi")
|
||||
)
|
||||
|
||||
assert result.candle_type == "heikin-ashi"
|
||||
|
||||
|
||||
def test_parse_websocket_ohlc_preserves_raw_numeric_types() -> None:
|
||||
result = parse_dzengi_websocket_ohlc(
|
||||
_validated_document(
|
||||
o="63992.00",
|
||||
h=64033,
|
||||
l=63984.0,
|
||||
c="64032.55",
|
||||
)
|
||||
)
|
||||
|
||||
assert result.open_price == "63992.00"
|
||||
assert result.high_price == 64033
|
||||
assert result.low_price == 63984.0
|
||||
assert result.close_price == "64032.55"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("field_name", "value"),
|
||||
[
|
||||
("symbol", None),
|
||||
("symbol", 123),
|
||||
("interval", None),
|
||||
("interval", []),
|
||||
("type", None),
|
||||
("type", True),
|
||||
],
|
||||
)
|
||||
def test_parse_websocket_ohlc_rejects_non_string_fields(
|
||||
field_name: str,
|
||||
value: object,
|
||||
) -> None:
|
||||
with pytest.raises(
|
||||
CandleWebSocketParseError,
|
||||
match=rf"\$\.payload\.{field_name} должен быть строкой",
|
||||
):
|
||||
parse_dzengi_websocket_ohlc(
|
||||
_validated_document(**{field_name: value})
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
None,
|
||||
True,
|
||||
1784224740000.0,
|
||||
"1784224740000",
|
||||
[],
|
||||
],
|
||||
)
|
||||
def test_parse_websocket_ohlc_rejects_invalid_open_time(
|
||||
value: object,
|
||||
) -> None:
|
||||
with pytest.raises(
|
||||
CandleWebSocketParseError,
|
||||
match=r"\$\.payload\.t должен быть целым числом",
|
||||
):
|
||||
parse_dzengi_websocket_ohlc(
|
||||
_validated_document(t=value)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"field_name",
|
||||
[
|
||||
"o",
|
||||
"h",
|
||||
"l",
|
||||
"c",
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
None,
|
||||
True,
|
||||
[],
|
||||
{},
|
||||
object(),
|
||||
],
|
||||
)
|
||||
def test_parse_websocket_ohlc_rejects_invalid_raw_numeric(
|
||||
field_name: str,
|
||||
value: object,
|
||||
) -> None:
|
||||
with pytest.raises(
|
||||
CandleWebSocketParseError,
|
||||
match=rf"\$\.payload\.{field_name} должен быть строкой или числом",
|
||||
):
|
||||
parse_dzengi_websocket_ohlc(
|
||||
_validated_document(**{field_name: value})
|
||||
)
|
||||
|
||||
|
||||
def test_parse_websocket_ohlc_does_not_validate_subject_values() -> None:
|
||||
result = parse_dzengi_websocket_ohlc(
|
||||
_validated_document(
|
||||
symbol="",
|
||||
interval="unsupported",
|
||||
type="unknown",
|
||||
t=-1,
|
||||
o="NaN",
|
||||
h=-10,
|
||||
l=100,
|
||||
c=0,
|
||||
)
|
||||
)
|
||||
|
||||
assert result.symbol == ""
|
||||
assert result.interval == "unsupported"
|
||||
assert result.candle_type == "unknown"
|
||||
assert result.open_time == -1
|
||||
assert result.open_price == "NaN"
|
||||
assert result.high_price == -10
|
||||
assert result.low_price == 100
|
||||
assert result.close_price == 0
|
||||
|
||||
|
||||
def test_parse_websocket_ohlc_ignores_envelope_metadata() -> None:
|
||||
document = ValidatedWebSocketOhlcDocument(
|
||||
payload=_validated_document().payload,
|
||||
status=123,
|
||||
destination=None,
|
||||
correlation_id={"unexpected": "value"},
|
||||
)
|
||||
|
||||
result = parse_dzengi_websocket_ohlc(document)
|
||||
|
||||
assert result.symbol == "BTC/USD_LEVERAGE"
|
||||
assert result.interval == "1m"
|
||||
Reference in New Issue
Block a user