test: la suite non parla piu' al canale di allarme vero
Difetto trovato addosso: `uv run pytest` mandava messaggi Telegram VERI sul canale dell'operatore — cinque per giro, col testo delle fixture («Analisi di ieri.», «rete KO», «Testo dell'agente.») sotto l'intestazione di una voce di giornale del 21/08. Colpevoli i cinque test di `analista.CLI.scrivi()`, che non passavano `telegram=False` e percorrevano `invia()` fino a `notifier.send()`. `_cfg()` legge il token da `.env.mainnet`/`.env`: il fatto che non fosse nell'ambiente non proteggeva niente. NON E' COSMETICO. E' P9 al contrario — allarmi finti spesi su un canale che deve restare credibile il giorno che l'allarme e' vero. Chi riceve cinque messaggi identici a ogni giro di test impara a non aprirli, ed e' l'unico canale da cui passano disaster-SL, uscita di fondi e venue giu'. RIPARAZIONE STRUTTURALE, stessa lezione del watermark (§5.12): si blocca la RETE in una fixture autouse, non si chiede a ogni autore di ricordarsi un parametro. Un test che vuole davvero esercitare `send()` continua a funzionare — patcha `urlopen` nel proprio corpo e vince su questo. In piu' una guardia di sessione: bloccare non basta, un tentativo va TROVATO e reso esplicito, o resta li' pronto a tornare vero il giorno che qualcuno cambia il boundary. Validata su controllo positivo (M15): un test che chiama `notifier.send()` fa fallire la sessione. ⚠️ E un difetto fatto e corretto nello stesso giro: la prima versione registrava l'URL, che contiene il BOT TOKEN in chiaro — e quella lista finisce nel messaggio di un assert, cioe' nell'output della suite e nei log. Ora si registra solo "telegram sendMessage": serve sapere CHE si e' tentato, non VERSO DOVE. Il token e' stato esposto una volta nell'output di quella verifica: va ruotato. Suite: 789 passati, 0 falliti. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,59 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================================
|
||||||
|
# IL CANALE DI ALLARME VERO
|
||||||
|
# =============================================================================================
|
||||||
|
# ⚠️ 2026-08-28, difetto trovato addosso di nuovo. Lanciando `uv run pytest` la suite ha mandato
|
||||||
|
# messaggi Telegram VERI sul canale di allarme dell'operatore: cinque per giro, col testo delle
|
||||||
|
# fixture — «Analisi di ieri.», «rete KO», «Testo dell'agente.» — sotto l'intestazione di una voce
|
||||||
|
# di giornale del 2026-08-21. Colpevoli: i test di `analista.CLI.scrivi()`, che non passavano
|
||||||
|
# `telegram=False` e quindi percorrevano `invia()` fino a `notifier.send()`. `_cfg()` legge il
|
||||||
|
# token da `.env.mainnet`/`.env`, quindi il fatto che il token non fosse nell'ambiente non
|
||||||
|
# proteggeva niente.
|
||||||
|
#
|
||||||
|
# NON E' COSMETICO. E' P9 al contrario: allarmi finti spesi su un canale che deve restare
|
||||||
|
# credibile il giorno che l'allarme e' vero. Chi riceve cinque messaggi identici a ogni giro di
|
||||||
|
# test impara a non aprirli — ed e' l'unico canale da cui passano il disaster-SL, l'uscita di
|
||||||
|
# fondi e il venue giu'.
|
||||||
|
#
|
||||||
|
# LA RIPARAZIONE E' STRUTTURALE (stessa lezione del watermark): si blocca la RETE, non si chiede
|
||||||
|
# a ogni autore di ricordarsi un parametro. Un test che vuole davvero esercitare `send()` continua
|
||||||
|
# a funzionare: patcha `urlopen` da se' nel corpo, e il suo monkeypatch vince su questo.
|
||||||
|
_INVII_TENTATI: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _blocca_il_canale_di_allarme_vero(monkeypatch):
|
||||||
|
"""Nessun test puo' raggiungere Telegram. Il tentativo si REGISTRA (P3: l'errore si scrive
|
||||||
|
nel punto in cui lo si ingoia — `send()` cattura tutto, quindi senza questo elenco un invio
|
||||||
|
tentato sparirebbe senza lasciare traccia)."""
|
||||||
|
from src.live import notifier
|
||||||
|
|
||||||
|
def _vietato(url, *a, **k):
|
||||||
|
# ⚠️ NON si registra l'URL: contiene il BOT TOKEN in chiaro, e questa lista finisce
|
||||||
|
# dentro il messaggio di un assert — cioe' nell'output della suite, nei log di CI e
|
||||||
|
# nello scrollback di chiunque lanci i test. Il difetto e' stato fatto e corretto il
|
||||||
|
# 2026-08-28: la prima versione stampava `https://api.telegram.org/bot<TOKEN>/sendM…`.
|
||||||
|
# Qui serve sapere CHE si e' tentato, non VERSO DOVE.
|
||||||
|
_INVII_TENTATI.append("telegram sendMessage")
|
||||||
|
raise RuntimeError("invio Telegram VERO bloccato dalla suite (tests/conftest.py)")
|
||||||
|
|
||||||
|
monkeypatch.setattr(notifier.urllib.request, "urlopen", _vietato)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True, scope="session")
|
||||||
|
def _guardia_nessun_invio_vero():
|
||||||
|
"""Bloccare non basta: se un test PROVA a mandare, va trovato e reso esplicito. Un blocco
|
||||||
|
silenzioso lascerebbe il tentativo in giro pronto a tornare vero il giorno che qualcuno
|
||||||
|
cambia il boundary."""
|
||||||
|
yield
|
||||||
|
assert not _INVII_TENTATI, (
|
||||||
|
f"{len(_INVII_TENTATI)} test hanno PROVATO a mandare un messaggio Telegram vero "
|
||||||
|
"(bloccati, ma il tentativo non deve esistere): passare `telegram=False` o iniettare "
|
||||||
|
"un sender finto. Vedi la nota in testa a questo file.")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def _isola_file_operativi_vivi(tmp_path, monkeypatch):
|
def _isola_file_operativi_vivi(tmp_path, monkeypatch):
|
||||||
"""Ogni test scrive il watermark in una cartella temporanea, mai in `data/live/`.
|
"""Ogni test scrive il watermark in una cartella temporanea, mai in `data/live/`.
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ def test_l_agente_non_tocca_la_nota(db, monkeypatch, tmp_path):
|
|||||||
_prepara(db)
|
_prepara(db)
|
||||||
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi valida senza numeri nuovi.", None))
|
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi valida senza numeri nuovi.", None))
|
||||||
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
||||||
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False)
|
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False, telegram=False)
|
||||||
r = db.execute("SELECT nota, analisi, analisi_stato FROM journal WHERE giorno='2026-08-21'").fetchone()
|
r = db.execute("SELECT nota, analisi, analisi_stato FROM journal WHERE giorno='2026-08-21'").fetchone()
|
||||||
assert r["nota"] == "nota dell'operatore"
|
assert r["nota"] == "nota dell'operatore"
|
||||||
assert r["analisi"].startswith("Analisi valida") and r["analisi_stato"] == "ok"
|
assert r["analisi"].startswith("Analisi valida") and r["analisi_stato"] == "ok"
|
||||||
@@ -203,9 +203,9 @@ def test_se_il_modello_non_risponde_l_analisi_di_IERI_non_resta_in_pagina(db, mo
|
|||||||
_prepara(db)
|
_prepara(db)
|
||||||
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
||||||
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi di ieri.", None))
|
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi di ieri.", None))
|
||||||
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False)
|
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False, telegram=False)
|
||||||
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("", "rete KO"))
|
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("", "rete KO"))
|
||||||
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False)
|
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False, telegram=False)
|
||||||
r = db.execute("SELECT analisi, analisi_stato, analisi_motivi FROM journal "
|
r = db.execute("SELECT analisi, analisi_stato, analisi_motivi FROM journal "
|
||||||
"WHERE giorno='2026-08-21'").fetchone()
|
"WHERE giorno='2026-08-21'").fetchone()
|
||||||
assert r["analisi"] == "" # niente in pagina
|
assert r["analisi"] == "" # niente in pagina
|
||||||
@@ -217,7 +217,7 @@ def test_un_ricalcolo_del_giornale_non_cancella_l_analisi(db, monkeypatch, tmp_p
|
|||||||
voce = _prepara(db)
|
voce = _prepara(db)
|
||||||
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
||||||
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi da conservare.", None))
|
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi da conservare.", None))
|
||||||
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False)
|
CLI.scrivi(db, date(2026, 8, 21), modello="finto", verbose=False, telegram=False)
|
||||||
J.salva(db, voce) # il cron rigenera le sezioni misurate
|
J.salva(db, voce) # il cron rigenera le sezioni misurate
|
||||||
r = db.execute("SELECT analisi, nota FROM journal WHERE giorno='2026-08-21'").fetchone()
|
r = db.execute("SELECT analisi, nota FROM journal WHERE giorno='2026-08-21'").fetchone()
|
||||||
assert r["analisi"] == "Analisi da conservare." and r["nota"] == "nota dell'operatore"
|
assert r["analisi"] == "Analisi da conservare." and r["nota"] == "nota dell'operatore"
|
||||||
@@ -228,7 +228,7 @@ def test_il_markdown_dichiara_modello_ora_e_esito_del_controllo(db, monkeypatch,
|
|||||||
voce = _prepara(db)
|
voce = _prepara(db)
|
||||||
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
|
||||||
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Testo dell'agente.", None))
|
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Testo dell'agente.", None))
|
||||||
CLI.scrivi(db, date(2026, 8, 21), modello="modello-finto", verbose=False)
|
CLI.scrivi(db, date(2026, 8, 21), modello="modello-finto", verbose=False, telegram=False)
|
||||||
md = J.rendi_markdown(db, voce)
|
md = J.rendi_markdown(db, voce)
|
||||||
assert "## Analisi (agente)" in md and "modello-finto" in md
|
assert "## Analisi (agente)" in md and "modello-finto" in md
|
||||||
assert "puo' sbagliare" in md and "Controllo sui numeri: **ok**" in md
|
assert "puo' sbagliare" in md and "Controllo sui numeri: **ok**" in md
|
||||||
|
|||||||
Reference in New Issue
Block a user