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:
Adriano Dal Pastro
2026-08-28 13:04:19 +00:00
parent 418f523be7
commit 32e1649dcb
2 changed files with 58 additions and 5 deletions
+53
View File
@@ -39,6 +39,59 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
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)
def _isola_file_operativi_vivi(tmp_path, monkeypatch):
"""Ogni test scrive il watermark in una cartella temporanea, mai in `data/live/`.
+5 -5
View File
@@ -191,7 +191,7 @@ def test_l_agente_non_tocca_la_nota(db, monkeypatch, tmp_path):
_prepara(db)
monkeypatch.setattr(A, "interroga", lambda *a, **k: ("Analisi valida senza numeri nuovi.", None))
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()
assert r["nota"] == "nota dell'operatore"
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)
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
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"))
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 "
"WHERE giorno='2026-08-21'").fetchone()
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)
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
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
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"
@@ -228,7 +228,7 @@ def test_il_markdown_dichiara_modello_ora_e_esito_del_controllo(db, monkeypatch,
voce = _prepara(db)
monkeypatch.setattr(J, "JOURNAL_DIR", tmp_path)
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)
assert "## Analisi (agente)" in md and "modello-finto" in md
assert "puo' sbagliare" in md and "Controllo sui numeri: **ok**" in md