101 lines
3.9 KiB
Python
101 lines
3.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""La sonde distingue-t-elle ce qu'un « echec » confond ?
|
||
|
|
|
||
|
|
CE QUE CES TESTS GARDENT, ET POURQUOI. Le 2026-08-28, trois faux diagnostics ont ete
|
||
|
|
poses en une journee — tous par l'instrument, aucun par le composant. Le pire des trois
|
||
|
|
n'etait pas une confusion entre « ouvert » et « ferme », mais entre DEUX FORMES DU MEME
|
||
|
|
CODE : `EHOSTUNREACH` immediat (pas de route) et `EHOSTUNREACH` apres trois secondes
|
||
|
|
(l'ARP qui renonce, donc AUCUNE MACHINE). Le second a fait appliquer un pare-feu pour
|
||
|
|
reparer un vide.
|
||
|
|
|
||
|
|
`interpreter` est une fonction PURE : elle se joue sans reseau, donc elle se garde ici.
|
||
|
|
"""
|
||
|
|
import errno
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
RACINE = Path(__file__).resolve().parent.parent.parent
|
||
|
|
sys.path.insert(0, str(RACINE / "scripts"))
|
||
|
|
|
||
|
|
import sonder # noqa: E402
|
||
|
|
|
||
|
|
|
||
|
|
def test_ouvert_est_ouvert() -> None:
|
||
|
|
assert sonder.interpreter("ouvert", None, 0.001)[0] == "ouvert"
|
||
|
|
|
||
|
|
|
||
|
|
def test_refus_immediat_est_une_politique() -> None:
|
||
|
|
"""Un RST est le signal d'un reject : une POLITIQUE, pas une absence."""
|
||
|
|
cle, phrase = sonder.interpreter("echec", errno.ECONNREFUSED, 0.002)
|
||
|
|
assert cle == "refuse"
|
||
|
|
assert "POLITIQUE" in phrase
|
||
|
|
|
||
|
|
|
||
|
|
def test_le_meme_code_dit_deux_choses_selon_le_delai() -> None:
|
||
|
|
"""LE TEST QUI COMPTE. `EHOSTUNREACH` lent = machine absente ; rapide = pas de route.
|
||
|
|
|
||
|
|
Les confondre est exactement l'erreur du 2026-08-28.
|
||
|
|
"""
|
||
|
|
lent, _ = sonder.interpreter("echec", errno.EHOSTUNREACH, 3.05)
|
||
|
|
vif, _ = sonder.interpreter("echec", errno.EHOSTUNREACH, 0.01)
|
||
|
|
assert lent == "absent", "un EHOSTUNREACH apres l'ARP dit qu'il n'y a PAS de machine"
|
||
|
|
assert vif == "injoignable", "le meme code, immediat, dit qu'il n'y a pas de route"
|
||
|
|
assert lent != vif, "les deux ne doivent JAMAIS se lire pareil"
|
||
|
|
|
||
|
|
|
||
|
|
def test_le_silence_reste_le_silence() -> None:
|
||
|
|
"""Sans code, on ne conclut pas au-dela de « quelque chose se tait »."""
|
||
|
|
cle, phrase = sonder.interpreter("echec", None, 6.0)
|
||
|
|
assert cle == "silence"
|
||
|
|
assert "frontiere" in phrase
|
||
|
|
|
||
|
|
|
||
|
|
def test_une_passerelle_de_zone_est_signalee_anycast() -> None:
|
||
|
|
"""La garde qui m'a manque : `10.17.21.1` EST une passerelle de zone."""
|
||
|
|
assert sonder._est_passerelle("10.17.21.1") == "anycast"
|
||
|
|
assert sonder._est_passerelle("10.17.19.41") == ""
|
||
|
|
|
||
|
|
|
||
|
|
def test_sans_le_moteur_la_garde_crie_au_lieu_de_se_taire() -> None:
|
||
|
|
"""Une garde qui echoue doit crier. Ma premiere version rendait « pas anycast »
|
||
|
|
quand `inventory_rules` manquait — c'est-a-dire sur un hyperviseur ou l'on a copie
|
||
|
|
le seul fichier, donc precisement la ou le piege se produit."""
|
||
|
|
vrai_import = __builtins__["__import__"] if isinstance(__builtins__, dict) else __builtins__.__import__
|
||
|
|
|
||
|
|
def sans_moteur(nom, *a, **k):
|
||
|
|
if nom == "inventory_rules":
|
||
|
|
raise ImportError("simule")
|
||
|
|
return vrai_import(nom, *a, **k)
|
||
|
|
|
||
|
|
cible = __builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__
|
||
|
|
cible["__import__"] = sans_moteur
|
||
|
|
try:
|
||
|
|
assert sonder._est_passerelle("10.17.21.1") == "anycast?", \
|
||
|
|
"sans le moteur, la garde doit avertir au conditionnel, pas se taire"
|
||
|
|
assert sonder._est_passerelle("10.17.19.41") == ""
|
||
|
|
finally:
|
||
|
|
cible["__import__"] = vrai_import
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
tests = [
|
||
|
|
test_ouvert_est_ouvert,
|
||
|
|
test_refus_immediat_est_une_politique,
|
||
|
|
test_le_meme_code_dit_deux_choses_selon_le_delai,
|
||
|
|
test_le_silence_reste_le_silence,
|
||
|
|
test_une_passerelle_de_zone_est_signalee_anycast,
|
||
|
|
test_sans_le_moteur_la_garde_crie_au_lieu_de_se_taire,
|
||
|
|
]
|
||
|
|
definis = {n for n, v in globals().items() if n.startswith("test_") and callable(v)}
|
||
|
|
oublies = definis - {t.__name__ for t in tests}
|
||
|
|
assert not oublies, f"test(s) definis mais jamais joues : {sorted(oublies)}"
|
||
|
|
for t in tests:
|
||
|
|
t()
|
||
|
|
print(f"ok {t.__name__}")
|
||
|
|
print(f"{len(tests)} tests passes.")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|