diff --git a/tests/conftest.py b/tests/conftest.py index 4141ed9..5ea8286 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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/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/`. diff --git a/tests/test_analista.py b/tests/test_analista.py index c97f530..aa81b72 100644 --- a/tests/test_analista.py +++ b/tests/test_analista.py @@ -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