feat: mută secretele Discord/Telegram din TOML în .env

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>
This commit is contained in:
2026-04-21 09:37:24 +03:00
parent 9e8cbafbd4
commit 9c44eb6e31
14 changed files with 610 additions and 33 deletions

View File

@@ -6,6 +6,13 @@ 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",
@@ -26,8 +33,6 @@ def _minimal_config_data() -> dict:
"baseline_phash": "abc123",
"drift_threshold": 8,
},
"discord": {"webhook_url": "http://example.com/hook"},
"telegram": {"bot_token": "123:abc", "chat_id": "456"},
}
@@ -55,6 +60,16 @@ def test_write_config_and_marker(tmp_path: Path) -> None:
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