TOML-urile din configs/ rămân 100% calibrare — safe to commit. Secretele (ATM_DISCORD_URL, ATM_TG_TOKEN, ATM_TG_CHAT) trăiesc în .env la rădăcină (ignored), cu loader stdlib (shell wins peste file). Validare fail-fast pentru env lipsă, placeholder REPLACE_ME, chat_id non-numeric. Include .env.example + secţiune README §Secrets. Tests: 19 noi (env loader + missing-env + placeholder + chat_id + regression post-migrate snapshot). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
84 lines
3.1 KiB
Python
84 lines
3.1 KiB
Python
"""Tests for atm.calibrate."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _secrets_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("ATM_DISCORD_URL", "https://example.com/hook")
|
|
monkeypatch.setenv("ATM_TG_TOKEN", "123:abc")
|
|
monkeypatch.setenv("ATM_TG_CHAT", "456")
|
|
|
|
|
|
def _minimal_config_data() -> dict:
|
|
return {
|
|
"window_title": "Test Chart",
|
|
"dot_roi": {"x": 0, "y": 0, "w": 100, "h": 100},
|
|
"chart_roi": {"x": 0, "y": 0, "w": 800, "h": 600},
|
|
"colors": {
|
|
"turquoise": {"rgb": [0, 200, 200], "tolerance": 30.0},
|
|
"yellow": {"rgb": [255, 255, 0], "tolerance": 30.0},
|
|
"dark_green": {"rgb": [0, 128, 0], "tolerance": 30.0},
|
|
"dark_red": {"rgb": [139, 0, 0], "tolerance": 30.0},
|
|
"light_green": {"rgb": [144, 238, 144], "tolerance": 30.0},
|
|
"light_red": {"rgb": [255, 182, 193], "tolerance": 30.0},
|
|
"gray": {"rgb": [128, 128, 128], "tolerance": 30.0},
|
|
},
|
|
"y_axis": {"p1_y": 100, "p1_price": 10000.0, "p2_y": 200, "p2_price": 9000.0},
|
|
"canary": {
|
|
"roi": {"x": 0, "y": 0, "w": 50, "h": 50},
|
|
"baseline_phash": "abc123",
|
|
"drift_threshold": 8,
|
|
},
|
|
}
|
|
|
|
|
|
def test_write_config_and_marker(tmp_path: Path) -> None:
|
|
from atm.calibrate import write_config
|
|
from atm.config import Config
|
|
|
|
config_path = write_config(_minimal_config_data(), tmp_path)
|
|
|
|
assert config_path.exists()
|
|
assert config_path.suffix == ".toml"
|
|
|
|
# Must be loadable by Config.load
|
|
cfg = Config.load(config_path)
|
|
assert cfg.window_title == "Test Chart"
|
|
assert cfg.y_axis.p1_price == pytest.approx(10000.0)
|
|
|
|
# Marker must point at the filename (basename only)
|
|
marker = tmp_path / "current.txt"
|
|
assert marker.exists()
|
|
assert marker.read_text(encoding="utf-8").strip() == config_path.name
|
|
|
|
# Config.load_current should also work
|
|
cfg2 = Config.load_current(tmp_path)
|
|
assert cfg2.window_title == cfg.window_title
|
|
|
|
|
|
def test_write_config_omits_secrets(tmp_path: Path) -> None:
|
|
"""Calibration output must NOT contain Discord/Telegram secret fields."""
|
|
from atm.calibrate import write_config
|
|
|
|
config_path = write_config(_minimal_config_data(), tmp_path)
|
|
text = config_path.read_text(encoding="utf-8")
|
|
for marker in ("[discord]", "[telegram]", "webhook_url", "bot_token", "chat_id"):
|
|
assert marker not in text, f"calibrated TOML leaked secret marker: {marker}"
|
|
|
|
|
|
def test_import_safe() -> None:
|
|
"""Importing atm.calibrate must succeed in a headless environment (no tkinter at top-level)."""
|
|
import importlib # noqa: F401
|
|
import importlib.util
|
|
|
|
spec = importlib.util.find_spec("atm.calibrate")
|
|
assert spec is not None, "atm.calibrate module not found"
|
|
# Actually importing must not raise (tkinter is only used inside run_calibration)
|
|
mod = importlib.import_module("atm.calibrate")
|
|
assert hasattr(mod, "write_config")
|
|
assert hasattr(mod, "run_calibration")
|