Initial public release
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import json
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from hydra.data.models import Candle
|
||||
from hydra.indicator.engine import IndicatorEngine
|
||||
from hydra.indicator.calculator import IndicatorCalculator
|
||||
|
||||
|
||||
def _make_candles(n: int = 250) -> list[Candle]:
|
||||
candles = []
|
||||
price = 50000.0
|
||||
for i in range(n):
|
||||
close = price * (1 + (0.001 if i % 2 == 0 else -0.001))
|
||||
price = close
|
||||
candles.append(Candle(
|
||||
market="binance", symbol="BTC/USDT", timeframe="1m",
|
||||
open_time=1_000_000 + i * 60_000,
|
||||
open=price, high=price * 1.001, low=price * 0.999, close=close,
|
||||
volume=100.0, close_time=1_000_000 + i * 60_000 + 59_999,
|
||||
))
|
||||
return candles
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_store():
|
||||
store = AsyncMock()
|
||||
store.query = AsyncMock(return_value=_make_candles(250))
|
||||
store.get_symbols = AsyncMock(return_value=[
|
||||
{"market": "binance", "symbol": "BTC/USDT", "timeframe": "1m"}
|
||||
])
|
||||
return store
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_redis():
|
||||
r = MagicMock()
|
||||
r.set = AsyncMock()
|
||||
r.keys = MagicMock(return_value=[])
|
||||
return r
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def engine(mock_store, mock_redis):
|
||||
calc = IndicatorCalculator()
|
||||
return IndicatorEngine(store=mock_store, redis_client=mock_redis, calculator=calc)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_event_writes_to_redis(engine, mock_redis):
|
||||
await engine._handle_event("binance", "BTC/USDT", "1m")
|
||||
mock_redis.set.assert_called_once()
|
||||
key, value = mock_redis.set.call_args[0]
|
||||
assert key == "hydra:indicator:binance:BTC/USDT:1m"
|
||||
data = json.loads(value)
|
||||
assert "RSI_14" in data
|
||||
assert "calculated_at" in data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_event_skips_empty_result(engine, mock_redis, mock_store):
|
||||
# Only 5 candles → calculator returns {}
|
||||
mock_store.query = AsyncMock(return_value=_make_candles(5))
|
||||
await engine._handle_event("binance", "BTC/USDT", "1m")
|
||||
mock_redis.set.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_event_does_not_raise_on_exception(engine, mock_store):
|
||||
mock_store.query = AsyncMock(side_effect=RuntimeError("db error"))
|
||||
# Should not raise — failures must be isolated
|
||||
await engine._handle_event("binance", "BTC/USDT", "1m")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cold_start_processes_all_symbols(engine, mock_redis, mock_store):
|
||||
mock_store.get_symbols = AsyncMock(return_value=[
|
||||
{"market": "binance", "symbol": "BTC/USDT", "timeframe": "1m"},
|
||||
{"market": "upbit", "symbol": "BTC/KRW", "timeframe": "1h"},
|
||||
])
|
||||
await engine.cold_start()
|
||||
assert mock_redis.set.call_count == 2
|
||||
Reference in New Issue
Block a user