RAPTOR v18.4: Исправлена отчетность, активированы выходные
This commit is contained in:
0
invest-python-master/tests/caches/__init__.py
Normal file
0
invest-python-master/tests/caches/__init__.py
Normal file
275
invest-python-master/tests/caches/test_instrument_cache.py
Normal file
275
invest-python-master/tests/caches/test_instrument_cache.py
Normal file
@@ -0,0 +1,275 @@
|
||||
import random
|
||||
import uuid
|
||||
from datetime import timedelta
|
||||
from typing import Any, Callable, Dict, Iterator, Type
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from pytest_freezegun import freeze_time
|
||||
|
||||
from t_tech.invest import (
|
||||
Bond,
|
||||
BondResponse,
|
||||
BondsResponse,
|
||||
Client,
|
||||
CurrenciesResponse,
|
||||
Currency,
|
||||
CurrencyResponse,
|
||||
Etf,
|
||||
EtfResponse,
|
||||
EtfsResponse,
|
||||
Future,
|
||||
FutureResponse,
|
||||
FuturesResponse,
|
||||
InstrumentIdType,
|
||||
Share,
|
||||
ShareResponse,
|
||||
SharesResponse,
|
||||
)
|
||||
from t_tech.invest.caching.instruments_cache.instruments_cache import InstrumentsCache
|
||||
from t_tech.invest.caching.instruments_cache.models import InstrumentsResponse
|
||||
from t_tech.invest.caching.instruments_cache.settings import InstrumentsCacheSettings
|
||||
from t_tech.invest.services import Services
|
||||
|
||||
|
||||
def uid() -> str:
|
||||
return uuid.uuid4().hex
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def token() -> str:
|
||||
return uid()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def real_services(token: str) -> Iterator[Services]:
|
||||
with Client(token) as services:
|
||||
yield services
|
||||
|
||||
|
||||
def gen_meta_ids() -> Dict[str, str]:
|
||||
return {"class_code": uid(), "figi": uid(), "ticker": uid(), "uid": uid()}
|
||||
|
||||
|
||||
def gen_instruments(type_: Type, instrument_count: int = 10):
|
||||
return [
|
||||
type_(name=f"{type_.__name__}_{i}", **gen_meta_ids())
|
||||
for i in range(instrument_count)
|
||||
]
|
||||
|
||||
|
||||
def gen_instruments_response(
|
||||
response_type: Type, type_: Type, instrument_count: int = 10
|
||||
):
|
||||
return response_type(instruments=gen_instruments(type_, instrument_count))
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def instrument_map():
|
||||
return {
|
||||
Etf: gen_instruments_response(EtfsResponse, Etf),
|
||||
Share: gen_instruments_response(SharesResponse, Share),
|
||||
Bond: gen_instruments_response(BondsResponse, Bond),
|
||||
Currency: gen_instruments_response(CurrenciesResponse, Currency),
|
||||
Future: gen_instruments_response(FuturesResponse, Future),
|
||||
}
|
||||
|
||||
|
||||
def mock_get_by(instrument_response: InstrumentsResponse, response_type):
|
||||
type_to_field_extractor = {
|
||||
InstrumentIdType.INSTRUMENT_ID_UNSPECIFIED: lambda i: i.figi,
|
||||
InstrumentIdType.INSTRUMENT_ID_TYPE_FIGI: lambda i: i.figi,
|
||||
InstrumentIdType.INSTRUMENT_ID_TYPE_TICKER: lambda i: i.ticker,
|
||||
InstrumentIdType.INSTRUMENT_ID_TYPE_UID: lambda i: i.uid,
|
||||
}
|
||||
|
||||
def _mock_get_by(
|
||||
*,
|
||||
id_type: InstrumentIdType = InstrumentIdType(0),
|
||||
class_code: str = "",
|
||||
id: str = "",
|
||||
):
|
||||
get_id = type_to_field_extractor[id_type]
|
||||
|
||||
def filter_(instrument):
|
||||
return get_id(instrument) == id and instrument.class_code == class_code
|
||||
|
||||
(found_instrument,) = filter(filter_, instrument_response.instruments)
|
||||
return response_type(instrument=found_instrument)
|
||||
|
||||
return Mock(wraps=_mock_get_by)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mock_instruments_service(
|
||||
real_services: Services,
|
||||
mocker,
|
||||
instrument_map,
|
||||
) -> Services:
|
||||
real_services.instruments = mocker.Mock(
|
||||
wraps=real_services.instruments,
|
||||
)
|
||||
|
||||
real_services.instruments.etfs.__name__ = "etfs"
|
||||
real_services.instruments.etfs.return_value = instrument_map[Etf]
|
||||
real_services.instruments.etf_by = mock_get_by(instrument_map[Etf], EtfResponse)
|
||||
|
||||
real_services.instruments.shares.__name__ = "shares"
|
||||
real_services.instruments.shares.return_value = instrument_map[Share]
|
||||
real_services.instruments.share_by = mock_get_by(
|
||||
instrument_map[Share], ShareResponse
|
||||
)
|
||||
|
||||
real_services.instruments.bonds.__name__ = "bonds"
|
||||
real_services.instruments.bonds.return_value = instrument_map[Bond]
|
||||
real_services.instruments.bond_by = mock_get_by(instrument_map[Bond], BondResponse)
|
||||
|
||||
real_services.instruments.currencies.__name__ = "currencies"
|
||||
real_services.instruments.currencies.return_value = instrument_map[Currency]
|
||||
real_services.instruments.currency_by = mock_get_by(
|
||||
instrument_map[Currency], CurrencyResponse
|
||||
)
|
||||
|
||||
real_services.instruments.futures.__name__ = "futures"
|
||||
real_services.instruments.futures.return_value = instrument_map[Future]
|
||||
real_services.instruments.future_by = mock_get_by(
|
||||
instrument_map[Future], FutureResponse
|
||||
)
|
||||
|
||||
return real_services
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mocked_services(
|
||||
real_services: Services,
|
||||
mock_instruments_service,
|
||||
) -> Services:
|
||||
return real_services
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def settings() -> InstrumentsCacheSettings:
|
||||
return InstrumentsCacheSettings()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def frozen_datetime():
|
||||
with freeze_time() as frozen_datetime:
|
||||
yield frozen_datetime
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def instruments_cache(
|
||||
settings: InstrumentsCacheSettings, mocked_services, frozen_datetime
|
||||
) -> InstrumentsCache:
|
||||
return InstrumentsCache(
|
||||
settings=settings, instruments_service=mocked_services.instruments
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("get_instruments_of_type", "get_instrument_of_type_by"),
|
||||
[
|
||||
(
|
||||
lambda instruments: instruments.etfs,
|
||||
lambda instruments: instruments.etf_by,
|
||||
),
|
||||
(
|
||||
lambda instruments: instruments.shares,
|
||||
lambda instruments: instruments.share_by,
|
||||
),
|
||||
(
|
||||
lambda instruments: instruments.bonds,
|
||||
lambda instruments: instruments.bond_by,
|
||||
),
|
||||
(
|
||||
lambda instruments: instruments.currencies,
|
||||
lambda instruments: instruments.currency_by,
|
||||
),
|
||||
(
|
||||
lambda instruments: instruments.futures,
|
||||
lambda instruments: instruments.future_by,
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("id_type", "get_id"),
|
||||
[
|
||||
(
|
||||
InstrumentIdType.INSTRUMENT_ID_UNSPECIFIED,
|
||||
lambda instrument: instrument.figi,
|
||||
),
|
||||
(
|
||||
InstrumentIdType.INSTRUMENT_ID_TYPE_FIGI,
|
||||
lambda instrument: instrument.figi,
|
||||
),
|
||||
(
|
||||
InstrumentIdType.INSTRUMENT_ID_TYPE_TICKER,
|
||||
lambda instrument: instrument.ticker,
|
||||
),
|
||||
(
|
||||
InstrumentIdType.INSTRUMENT_ID_TYPE_UID,
|
||||
lambda instrument: instrument.uid,
|
||||
),
|
||||
],
|
||||
)
|
||||
class TestInstrumentCache:
|
||||
def test_gets_from_net_then_cache(
|
||||
self,
|
||||
mocked_services: Services,
|
||||
settings: InstrumentsCacheSettings,
|
||||
instruments_cache: InstrumentsCache,
|
||||
get_instruments_of_type,
|
||||
get_instrument_of_type_by,
|
||||
id_type: InstrumentIdType,
|
||||
get_id: Callable[[Any], str],
|
||||
):
|
||||
get_instruments = get_instruments_of_type(mocked_services.instruments)
|
||||
get_instrument_by = get_instrument_of_type_by(mocked_services.instruments)
|
||||
get_instrument_by_cached = get_instrument_of_type_by(instruments_cache)
|
||||
(inst,) = random.sample(get_instruments().instruments, k=1)
|
||||
from_server = get_instrument_by(
|
||||
id_type=id_type,
|
||||
class_code=inst.class_code,
|
||||
id=get_id(inst),
|
||||
)
|
||||
get_instrument_by.assert_called_once()
|
||||
get_instrument_by.reset_mock()
|
||||
|
||||
from_cache = get_instrument_by_cached(
|
||||
id_type=id_type,
|
||||
class_code=inst.class_code,
|
||||
id=get_id(inst),
|
||||
)
|
||||
|
||||
get_instrument_by.assert_not_called()
|
||||
assert str(from_server) == str(from_cache)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"settings", [InstrumentsCacheSettings(ttl=timedelta(milliseconds=10))]
|
||||
)
|
||||
def test_refreshes_on_ttl(
|
||||
self,
|
||||
mocked_services: Services,
|
||||
settings: InstrumentsCacheSettings,
|
||||
instruments_cache: InstrumentsCache,
|
||||
get_instruments_of_type,
|
||||
get_instrument_of_type_by,
|
||||
id_type: InstrumentIdType,
|
||||
get_id: Callable[[Any], str],
|
||||
frozen_datetime,
|
||||
):
|
||||
get_instruments = get_instruments_of_type(mocked_services.instruments)
|
||||
get_instrument_by_cached = get_instrument_of_type_by(instruments_cache)
|
||||
get_instruments.assert_called_once()
|
||||
(inst,) = random.sample(get_instruments().instruments, k=1)
|
||||
get_instruments.reset_mock()
|
||||
frozen_datetime.tick(timedelta(seconds=10))
|
||||
|
||||
_ = get_instrument_by_cached(
|
||||
id_type=id_type,
|
||||
class_code=inst.class_code,
|
||||
id=get_id(inst),
|
||||
)
|
||||
|
||||
get_instruments.assert_called_once()
|
||||
26
invest-python-master/tests/caches/test_ttl_cache.py
Normal file
26
invest-python-master/tests/caches/test_ttl_cache.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from cachetools import TTLCache as StandardTTLCache
|
||||
from pytest_freezegun import freeze_time
|
||||
|
||||
from t_tech.invest.caching.overrides import TTLCache as OverridenTTLCache
|
||||
|
||||
|
||||
class TestTTLCache:
|
||||
def _assert_ttl_cache(self, ttl_cache_class, expires):
|
||||
with freeze_time() as frozen_datetime:
|
||||
ttl = ttl_cache_class(
|
||||
maxsize=10,
|
||||
ttl=1,
|
||||
)
|
||||
ttl.update({"1": 1})
|
||||
|
||||
assert ttl.keys()
|
||||
frozen_datetime.tick(timedelta(seconds=10000))
|
||||
assert not ttl.keys() == expires
|
||||
|
||||
def test_overriden_cache(self):
|
||||
self._assert_ttl_cache(OverridenTTLCache, expires=True)
|
||||
|
||||
def test_standard_cache(self):
|
||||
self._assert_ttl_cache(StandardTTLCache, expires=False)
|
||||
Reference in New Issue
Block a user