build 042: remove MarketPriceCache compatibility facade

This commit is contained in:
2026-07-14 18:56:40 +03:00
parent b2f6377143
commit 225f07bc4b
11 changed files with 140 additions and 193 deletions

View File

@@ -1,74 +0,0 @@
# app/tests/unit/integrations/exchange/test_market_cache.py
from __future__ import annotations
from datetime import datetime, timezone
from decimal import Decimal
from typing import Iterator
import pytest
from src.integrations.exchange.market_cache import MarketPriceCache
from src.market_data.acquisition.models.quote import Quote
from src.storage.quote_store import InMemoryQuoteStore, QuoteStoreProtocol
def _quote(symbol: str = "BTC/USD_LEVERAGE") -> Quote:
return Quote(
symbol=symbol,
last_price=Decimal("100"),
bid_price=Decimal("99"),
ask_price=Decimal("101"),
exchange_timestamp=None,
received_at=datetime.now(timezone.utc),
source="test",
)
@pytest.fixture(autouse=True)
def reset_market_price_cache() -> Iterator[None]:
original_store = MarketPriceCache._store
MarketPriceCache._store = InMemoryQuoteStore()
yield
MarketPriceCache._store = original_store
def test_market_price_cache_uses_quote_store_protocol() -> None:
assert isinstance(MarketPriceCache._store, QuoteStoreProtocol)
def test_set_and_get_quote_preserve_identity() -> None:
quote = _quote()
MarketPriceCache.set_quote(quote, runtime_key="auto")
assert MarketPriceCache.get_quote(quote.symbol, runtime_key="auto") is quote
def test_symbol_and_runtime_key_are_normalized() -> None:
quote = _quote("BTC/USD_LEVERAGE")
MarketPriceCache.set_quote(quote, runtime_key=" AUTO ")
assert MarketPriceCache.get_quote(" btc/usd_leverage ", runtime_key="auto") is quote
def test_runtime_keys_are_isolated() -> None:
auto = _quote()
debug = Quote(
symbol=auto.symbol, last_price=Decimal("200"), bid_price=Decimal("199"),
ask_price=Decimal("201"), exchange_timestamp=None,
received_at=datetime.now(timezone.utc), source="debug",
)
MarketPriceCache.set_quote(auto, runtime_key="auto")
MarketPriceCache.set_quote(debug, runtime_key="debug_auto")
assert MarketPriceCache.get_quote(auto.symbol, runtime_key="auto") is auto
assert MarketPriceCache.get_quote(auto.symbol, runtime_key="debug_auto") is debug
def test_clear_targeted_and_all() -> None:
btc = _quote("BTC/USD_LEVERAGE")
eth = _quote("ETH/USD_LEVERAGE")
MarketPriceCache.set_quote(btc, runtime_key="auto")
MarketPriceCache.set_quote(eth, runtime_key="auto")
MarketPriceCache.clear(btc.symbol, runtime_key="auto")
assert MarketPriceCache.get_quote(btc.symbol, runtime_key="auto") is None
assert MarketPriceCache.get_quote(eth.symbol, runtime_key="auto") is eth
MarketPriceCache.clear()
assert MarketPriceCache.get_quote(eth.symbol, runtime_key="auto") is None

View File

@@ -205,14 +205,15 @@ def test_run_websocket_uses_canonical_quote_adapter(
mapped_documents.append(document)
return quote
class Cache:
@classmethod
def set_quote(
cls,
class Store:
def set(
self,
source_name: str,
value: Quote,
*,
runtime_key: str = "default",
) -> None:
assert source_name == runner_module.QUOTE_RUNTIME_SOURCE_NAME
stored_quotes.append((value, runtime_key))
context = MarketRuntimeContext(
@@ -247,7 +248,7 @@ def test_run_websocket_uses_canonical_quote_adapter(
)
monkeypatch.setattr(runner_module, "ExchangeWebSocketClient", Client)
monkeypatch.setattr(runner_module, "DzengiWebSocketQuoteAdapter", Adapter)
monkeypatch.setattr(runner_module, "MarketPriceCache", Cache)
monkeypatch.setattr(runner_module, "get_quote_store", lambda: Store())
asyncio.run(
MarketDataRunner._run_websocket(

View File

@@ -357,14 +357,15 @@ def test_start_market_stream_maps_message_to_quote(
mapped_documents.append(document)
return quote
class Cache:
@classmethod
def set_quote(
cls,
class Store:
def set(
self,
source_name: str,
value: Quote,
*,
runtime_key: str = "default",
) -> None:
assert source_name == stream_module.QUOTE_RUNTIME_SOURCE_NAME
stored_quotes.append((value, runtime_key))
monkeypatch.setattr(stream_module, "load_settings", lambda: settings)
@@ -379,7 +380,7 @@ def test_start_market_stream_maps_message_to_quote(
monkeypatch.setattr(stream_module, "ExchangeService", lambda: service)
monkeypatch.setattr(stream_module, "ExchangeWebSocketClient", Client)
monkeypatch.setattr(stream_module, "DzengiWebSocketQuoteAdapter", Adapter)
monkeypatch.setattr(stream_module, "MarketPriceCache", Cache)
monkeypatch.setattr(stream_module, "get_quote_store", lambda: Store())
monkeypatch.setattr(stream_module.asyncio, "sleep", _raise_stop_stream)
with pytest.raises(StopStream):
@@ -416,9 +417,8 @@ def test_start_market_stream_skips_invalid_adapter_message(
del document
raise QuoteSchemaError("Invalid message.")
class Cache:
@classmethod
def set_quote(cls, *args: object, **kwargs: object) -> None:
class Store:
def set(self, *args: object, **kwargs: object) -> None:
nonlocal cache_calls
cache_calls += 1
@@ -434,7 +434,7 @@ def test_start_market_stream_skips_invalid_adapter_message(
monkeypatch.setattr(stream_module, "ExchangeService", lambda: service)
monkeypatch.setattr(stream_module, "ExchangeWebSocketClient", Client)
monkeypatch.setattr(stream_module, "DzengiWebSocketQuoteAdapter", Adapter)
monkeypatch.setattr(stream_module, "MarketPriceCache", Cache)
monkeypatch.setattr(stream_module, "get_quote_store", lambda: Store())
monkeypatch.setattr(stream_module.asyncio, "sleep", _raise_stop_stream)
with pytest.raises(StopStream):

View File

@@ -10,9 +10,10 @@ from typing import cast
import pytest
from src.core.config import Settings
from src.integrations.exchange.market_cache import MarketPriceCache
import src.integrations.exchange.service as service_module
from src.integrations.exchange.service import ExchangeService
from src.market_data.acquisition.models.quote import Quote
from src.storage.quote_store import QUOTE_RUNTIME_SOURCE_NAME
def _service() -> ExchangeService:
@@ -55,7 +56,13 @@ def test_execution_snapshot_reads_canonical_cached_quote(
quote = _quote()
monkeypatch.setattr(service, "validate_symbol", lambda _: _valid_validation())
monkeypatch.setattr(MarketPriceCache, "get_quote", lambda *_, **__: quote)
class Store:
def get(self, source_name, symbol, *, runtime_key="default"):
assert source_name == QUOTE_RUNTIME_SOURCE_NAME
del symbol, runtime_key
return quote
monkeypatch.setattr(service_module, "get_quote_store", lambda: Store())
monkeypatch.setattr(
service,
"_get_fresh_quote",
@@ -89,13 +96,18 @@ def test_execution_snapshot_uses_fresh_quote_for_stale_cache(
stored: list[tuple[Quote, str]] = []
monkeypatch.setattr(service, "validate_symbol", lambda _: _valid_validation())
monkeypatch.setattr(MarketPriceCache, "get_quote", lambda *_, **__: stale)
class Store:
def get(self, source_name, symbol, *, runtime_key="default"):
assert source_name == QUOTE_RUNTIME_SOURCE_NAME
del symbol, runtime_key
return stale
def set(self, source_name, quote, *, runtime_key="default"):
assert source_name == QUOTE_RUNTIME_SOURCE_NAME
stored.append((quote, runtime_key))
monkeypatch.setattr(service_module, "get_quote_store", lambda: Store())
monkeypatch.setattr(service, "_get_fresh_quote", lambda _: fresh)
monkeypatch.setattr(
MarketPriceCache,
"set_quote",
lambda quote, *, runtime_key: stored.append((quote, runtime_key)),
)
result = service.get_execution_snapshot(
"BTC/USD_LEVERAGE",

View File

@@ -12,10 +12,14 @@ import pytest
import src.integrations.exchange.service as service_module
from src.core.config import Settings
from src.integrations.exchange.exceptions import ExchangeError
from src.integrations.exchange.market_cache import MarketPriceCache
from src.integrations.exchange.service import ExchangeService
from src.market_data.acquisition.models.quote import Quote
from src.storage.quote_store import InMemoryQuoteStore
import src.storage.quote_store as quote_store_module
from src.storage.quote_store import (
InMemoryQuoteStore,
QUOTE_RUNTIME_SOURCE_NAME,
get_quote_store,
)
def _service(*, exchange_enabled: bool = True) -> ExchangeService:
@@ -46,11 +50,13 @@ def _quote(*, age_seconds: float = 0.0) -> Quote:
@pytest.fixture(autouse=True)
def reset_store():
original = MarketPriceCache._store
MarketPriceCache._store = InMemoryQuoteStore()
def reset_store(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(
quote_store_module,
"_SHARED_QUOTE_STORE",
InMemoryQuoteStore(),
)
yield
MarketPriceCache._store = original
def test_get_quote_returns_fresh_cached_identity(
@@ -58,7 +64,8 @@ def test_get_quote_returns_fresh_cached_identity(
) -> None:
service = _service()
quote = _quote()
MarketPriceCache.set_quote(quote, runtime_key="auto")
get_quote_store().set(
QUOTE_RUNTIME_SOURCE_NAME,quote, runtime_key="auto")
monkeypatch.setattr(
service,
"validate_symbol",
@@ -83,7 +90,8 @@ def test_get_quote_refreshes_stale_quote(
service = _service()
stale = _quote(age_seconds=10)
fresh = _quote()
MarketPriceCache.set_quote(stale, runtime_key="auto")
get_quote_store().set(
QUOTE_RUNTIME_SOURCE_NAME,stale, runtime_key="auto")
monkeypatch.setattr(
service,
"validate_symbol",
@@ -98,7 +106,8 @@ def test_get_quote_refreshes_stale_quote(
result = service.get_quote(fresh.symbol, runtime_key="auto")
assert result is fresh
assert MarketPriceCache.get_quote(fresh.symbol, runtime_key="auto") is fresh
assert get_quote_store().get(
QUOTE_RUNTIME_SOURCE_NAME,fresh.symbol, runtime_key="auto") is fresh
def test_get_quote_wraps_acquisition_error(
@@ -125,7 +134,8 @@ def test_refresh_quote_cache_forces_fresh_quote_and_preserves_identity(
service = _service()
cached = _quote()
fresh = _quote()
MarketPriceCache.set_quote(cached, runtime_key="auto")
get_quote_store().set(
QUOTE_RUNTIME_SOURCE_NAME,cached, runtime_key="auto")
requested_symbols: list[str] = []
monkeypatch.setattr(
@@ -147,7 +157,8 @@ def test_refresh_quote_cache_forces_fresh_quote_and_preserves_identity(
assert requested_symbols == [" btc/usd_leverage "]
assert result is fresh
assert result is not cached
assert MarketPriceCache.get_quote(fresh.symbol, runtime_key="auto") is fresh
assert get_quote_store().get(
QUOTE_RUNTIME_SOURCE_NAME,fresh.symbol, runtime_key="auto") is fresh
def test_refresh_quote_cache_uses_default_runtime_key(
@@ -169,7 +180,8 @@ def test_refresh_quote_cache_uses_default_runtime_key(
result = service.refresh_quote_cache(fresh.symbol)
assert result is fresh
assert MarketPriceCache.get_quote(fresh.symbol, runtime_key="auto") is fresh
assert get_quote_store().get(
QUOTE_RUNTIME_SOURCE_NAME,fresh.symbol, runtime_key="auto") is fresh
def test_refresh_quote_cache_normalizes_runtime_key(
@@ -195,7 +207,8 @@ def test_refresh_quote_cache_normalizes_runtime_key(
assert result is fresh
assert (
MarketPriceCache.get_quote(
get_quote_store().get(
QUOTE_RUNTIME_SOURCE_NAME,
fresh.symbol,
runtime_key="debug_auto",
)
@@ -234,7 +247,8 @@ def test_refresh_quote_cache_does_not_update_cache_on_acquisition_error(
service = _service()
cached = _quote()
original = RuntimeError("unavailable")
MarketPriceCache.set_quote(cached, runtime_key="auto")
get_quote_store().set(
QUOTE_RUNTIME_SOURCE_NAME,cached, runtime_key="auto")
monkeypatch.setattr(
service,
"validate_symbol",
@@ -255,7 +269,8 @@ def test_refresh_quote_cache_does_not_update_cache_on_acquisition_error(
service.refresh_quote_cache(cached.symbol, runtime_key="auto")
assert exc_info.value.__cause__ is original
assert MarketPriceCache.get_quote(cached.symbol, runtime_key="auto") is cached
assert get_quote_store().get(
QUOTE_RUNTIME_SOURCE_NAME,cached.symbol, runtime_key="auto") is cached
def test_refresh_quote_cache_uses_and_stores_mock_quote(
@@ -286,7 +301,8 @@ def test_refresh_quote_cache_uses_and_stores_mock_quote(
assert result is mock
assert (
MarketPriceCache.get_quote(
get_quote_store().get(
QUOTE_RUNTIME_SOURCE_NAME,
mock.symbol,
runtime_key="debug_auto",
)