Build 060.7: implement REST Trade Adapter

This commit is contained in:
2026-07-19 10:56:06 +03:00
parent 6b0d5badce
commit 7eee8ce36c
5 changed files with 2318 additions and 27 deletions

View File

@@ -0,0 +1,188 @@
# tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py
from __future__ import annotations
from datetime import datetime, timezone
from decimal import Decimal
from types import MappingProxyType
import pytest
from src.market_data.acquisition.adapters.dzengi import rest_trade_adapter
from src.market_data.acquisition.adapters.dzengi.rest_trade_adapter import (
adapt_rest_agg_trades_document,
)
from src.market_data.acquisition.exceptions import (
TradeMappingError,
TradeParseError,
TradeValueError,
)
from src.market_data.acquisition.models.trade import TradeAggressorSide
from src.market_data.acquisition.validation.schema import (
ValidatedRestAggTradesDocument,
)
def test_adapt_rest_agg_trades_document_returns_canonical_trades() -> None:
document = ValidatedRestAggTradesDocument(
items=(
MappingProxyType(
{
"a": 101,
"p": "43210.50",
"q": "0.125",
"T": 1_700_000_000_000,
"m": False,
}
),
MappingProxyType(
{
"a": 102,
"p": "43211.75",
"q": "0.250",
"T": 1_700_000_001_000,
"m": True,
}
),
)
)
result = adapt_rest_agg_trades_document(
document,
symbol=" BTCUSDT ",
)
assert isinstance(result, tuple)
assert len(result) == 2
first_trade = result[0]
assert first_trade.symbol == "BTCUSDT"
assert first_trade.trade_id == 101
assert first_trade.price == Decimal("43210.50")
assert first_trade.quantity == Decimal("0.125")
assert first_trade.executed_at == datetime(
2023,
11,
14,
22,
13,
20,
tzinfo=timezone.utc,
)
assert first_trade.aggressor_side is TradeAggressorSide.BUY
assert first_trade.source == "dzengi"
second_trade = result[1]
assert second_trade.symbol == "BTCUSDT"
assert second_trade.trade_id == 102
assert second_trade.price == Decimal("43211.75")
assert second_trade.quantity == Decimal("0.250")
assert second_trade.executed_at == datetime(
2023,
11,
14,
22,
13,
21,
tzinfo=timezone.utc,
)
assert second_trade.aggressor_side is TradeAggressorSide.SELL
assert second_trade.source == "dzengi"
def test_adapt_rest_agg_trades_document_propagates_parse_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
document = ValidatedRestAggTradesDocument(items=())
expected_error = TradeParseError("test parse error")
def raise_parse_error(
_: ValidatedRestAggTradesDocument,
) -> tuple[()]:
raise expected_error
monkeypatch.setattr(
rest_trade_adapter,
"parse_rest_agg_trades",
raise_parse_error,
)
with pytest.raises(TradeParseError) as exc_info:
adapt_rest_agg_trades_document(
document,
symbol="BTCUSDT",
)
assert exc_info.value is expected_error
def test_adapt_rest_agg_trades_document_propagates_value_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
document = ValidatedRestAggTradesDocument(items=())
expected_error = TradeValueError("test value error")
monkeypatch.setattr(
rest_trade_adapter,
"parse_rest_agg_trades",
lambda _: (),
)
def raise_value_error(_: tuple[object, ...]) -> None:
raise expected_error
monkeypatch.setattr(
rest_trade_adapter,
"validate_rest_agg_trade_values",
raise_value_error,
)
with pytest.raises(TradeValueError) as exc_info:
adapt_rest_agg_trades_document(
document,
symbol="BTCUSDT",
)
assert exc_info.value is expected_error
def test_adapt_rest_agg_trades_document_propagates_mapping_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
document = ValidatedRestAggTradesDocument(items=())
expected_error = TradeMappingError("test mapping error")
monkeypatch.setattr(
rest_trade_adapter,
"parse_rest_agg_trades",
lambda _: (),
)
monkeypatch.setattr(
rest_trade_adapter,
"validate_rest_agg_trade_values",
lambda _: None,
)
def raise_mapping_error(
_: tuple[object, ...],
*,
symbol: str,
) -> tuple[()]:
del symbol
raise expected_error
monkeypatch.setattr(
rest_trade_adapter,
"map_dzengi_rest_agg_trades_to_trades",
raise_mapping_error,
)
with pytest.raises(TradeMappingError) as exc_info:
adapt_rest_agg_trades_document(
document,
symbol="BTCUSDT",
)
assert exc_info.value is expected_error