Build 060.25: implement Production Runtime Integration

This commit is contained in:
2026-07-31 00:29:36 +03:00
parent c142145361
commit 60bec1eaf9
50 changed files with 14044 additions and 83 deletions

View File

@@ -0,0 +1,202 @@
from __future__ import annotations
import pytest
from src.core.config import load_settings
_TRADE_STREAM_VARIABLES = (
"TRADE_STREAM_ENABLED",
"TRADE_STREAM_WS_URL",
"TRADE_STREAM_SYMBOLS",
"TRADE_STREAM_OPEN_TIMEOUT_SECONDS",
"TRADE_STREAM_PROBE_TIMEOUT_SECONDS",
"TRADE_STREAM_CLOSE_TIMEOUT_SECONDS",
"TRADE_STREAM_HEARTBEAT_TIMEOUT_SECONDS",
"TRADE_STREAM_SCHEDULER_INTERVAL_SECONDS",
"TRADE_STREAM_RECOVERY_WINDOW_MS",
)
def prepare_environment(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("BOT_TOKEN", "test-token")
monkeypatch.delenv("EXCHANGE_BASE_URL", raising=False)
monkeypatch.delenv("EXCHANGE_ENABLED", raising=False)
for variable in _TRADE_STREAM_VARIABLES:
monkeypatch.delenv(variable, raising=False)
def enable_trade_stream(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("TRADE_STREAM_ENABLED", "true")
monkeypatch.setenv(
"TRADE_STREAM_WS_URL",
"wss://stream.example.test",
)
monkeypatch.setenv(
"TRADE_STREAM_SYMBOLS",
"ETH/USD_LEVERAGE,BTC/USD_LEVERAGE",
)
monkeypatch.setenv(
"EXCHANGE_BASE_URL",
"https://rest.example.test",
)
def test_trade_stream_is_disabled_by_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
prepare_environment(monkeypatch)
monkeypatch.setenv("EXCHANGE_ENABLED", "true")
settings = load_settings()
assert settings.exchange_enabled is True
assert settings.trade_stream.enabled is False
assert settings.trade_stream.websocket_url == ""
assert settings.trade_stream.symbols == ()
def test_disabled_trade_stream_ignores_dependent_values(
monkeypatch: pytest.MonkeyPatch,
) -> None:
prepare_environment(monkeypatch)
monkeypatch.setenv("TRADE_STREAM_OPEN_TIMEOUT_SECONDS", "invalid")
monkeypatch.setenv("TRADE_STREAM_SYMBOLS", ",")
settings = load_settings()
assert settings.trade_stream.enabled is False
def test_trade_stream_flag_is_strict(
monkeypatch: pytest.MonkeyPatch,
) -> None:
prepare_environment(monkeypatch)
monkeypatch.setenv("TRADE_STREAM_ENABLED", "sometimes")
with pytest.raises(
ValueError,
match="TRADE_STREAM_ENABLED",
):
load_settings()
@pytest.mark.parametrize(
("missing_variable", "message"),
(
(
"TRADE_STREAM_WS_URL",
"TRADE_STREAM_WS_URL",
),
(
"TRADE_STREAM_SYMBOLS",
"TRADE_STREAM_SYMBOLS",
),
(
"EXCHANGE_BASE_URL",
"EXCHANGE_BASE_URL",
),
),
)
def test_enabled_trade_stream_requires_explicit_endpoints_and_symbols(
monkeypatch: pytest.MonkeyPatch,
missing_variable: str,
message: str,
) -> None:
prepare_environment(monkeypatch)
enable_trade_stream(monkeypatch)
monkeypatch.setenv(
"EXCHANGE_WS_URL",
"wss://legacy-fallback.example.test",
)
monkeypatch.setenv(
"DEFAULT_SYMBOL",
"LEGACY_FALLBACK",
)
monkeypatch.delenv(missing_variable)
with pytest.raises(
RuntimeError,
match=message,
):
load_settings()
def test_enabled_trade_stream_parses_independent_settings(
monkeypatch: pytest.MonkeyPatch,
) -> None:
prepare_environment(monkeypatch)
enable_trade_stream(monkeypatch)
monkeypatch.setenv(
"TRADE_STREAM_SYMBOLS",
" ETH/USD_LEVERAGE, BTC/USD_LEVERAGE,ETH/USD_LEVERAGE ",
)
monkeypatch.setenv("TRADE_STREAM_OPEN_TIMEOUT_SECONDS", "11.5")
monkeypatch.setenv("TRADE_STREAM_PROBE_TIMEOUT_SECONDS", "21")
monkeypatch.setenv("TRADE_STREAM_CLOSE_TIMEOUT_SECONDS", "9")
monkeypatch.setenv("TRADE_STREAM_HEARTBEAT_TIMEOUT_SECONDS", "31")
monkeypatch.setenv("TRADE_STREAM_SCHEDULER_INTERVAL_SECONDS", "6")
monkeypatch.setenv("TRADE_STREAM_RECOVERY_WINDOW_MS", "123456")
settings = load_settings()
trade_stream = settings.trade_stream
assert trade_stream.enabled is True
assert trade_stream.websocket_url == "wss://stream.example.test"
assert trade_stream.symbols == (
"BTC/USD_LEVERAGE",
"ETH/USD_LEVERAGE",
)
assert trade_stream.open_timeout_seconds == 11.5
assert trade_stream.probe_timeout_seconds == 21.0
assert trade_stream.close_timeout_seconds == 9.0
assert trade_stream.heartbeat_timeout_seconds == 31.0
assert trade_stream.scheduler_interval_seconds == 6.0
assert trade_stream.recovery_window_ms == 123_456
@pytest.mark.parametrize(
("variable", "value"),
(
("TRADE_STREAM_OPEN_TIMEOUT_SECONDS", "0"),
("TRADE_STREAM_PROBE_TIMEOUT_SECONDS", "-1"),
("TRADE_STREAM_CLOSE_TIMEOUT_SECONDS", "nan"),
("TRADE_STREAM_HEARTBEAT_TIMEOUT_SECONDS", "inf"),
("TRADE_STREAM_SCHEDULER_INTERVAL_SECONDS", "invalid"),
("TRADE_STREAM_RECOVERY_WINDOW_MS", "1.5"),
("TRADE_STREAM_RECOVERY_WINDOW_MS", "0"),
),
)
def test_enabled_trade_stream_rejects_invalid_numeric_settings(
monkeypatch: pytest.MonkeyPatch,
variable: str,
value: str,
) -> None:
prepare_environment(monkeypatch)
enable_trade_stream(monkeypatch)
monkeypatch.setenv(variable, value)
with pytest.raises(ValueError, match=variable):
load_settings()
def test_symbols_must_not_contain_empty_items(
monkeypatch: pytest.MonkeyPatch,
) -> None:
prepare_environment(monkeypatch)
enable_trade_stream(monkeypatch)
monkeypatch.setenv(
"TRADE_STREAM_SYMBOLS",
"BTC/USD_LEVERAGE,,ETH/USD_LEVERAGE",
)
with pytest.raises(
ValueError,
match="empty symbols",
):
load_settings()