Le binaire ne parlait que français alors que le menu suit EL_LANG : un opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message humain passe par T(), dont la clé EST le texte français, et un catalogue anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses motifs ne connaissant que cette langue. Un test refuse un message sans traduction, une traduction orpheline et un verbe de format qui diverge. --- EN --- The binary spoke French only while the menu follows EL_LANG: an English reader got a status he could not read. Every human message goes through T(), whose key IS the French text, and an English catalogue translates it; the language comes from --lang, then EL_LANG, French remaining the default. The menu asks for French for what it READS BACK, its patterns knowing that language alone. A test refuses a message without translation, an orphan translation, and a format verb that drifts. Assisted-by: Claude Opus 5
103 lines
4.1 KiB
Python
103 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
"""La langue du binaire du cache, vue du menu.
|
|
|
|
Le binaire traduit ses messages humains selon --lang. Le menu doit donc dire
|
|
laquelle il veut, et la bonne : la langue de todo.py pour ce qu'il AFFICHE,
|
|
le français pour ce qu'il LIT — il ne connaît les libellés que dans cette
|
|
langue. Et ce qu'il lit dans un journal écrit par le service, dont la langue
|
|
est fixée à l'installation, il doit le reconnaître dans les deux.
|
|
"""
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
RACINE = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(RACINE))
|
|
|
|
from script.todo import qemu_cache_menu as menu # noqa: E402
|
|
|
|
CATALOGUE = RACINE / "script" / "qemu_cache" / "catalogue_en.go"
|
|
|
|
|
|
class TestLOptionDeLangue(unittest.TestCase):
|
|
def test_une_sortie_lue_est_demandee_en_francais(self):
|
|
with mock.patch.object(menu, "get_lang", return_value="en"):
|
|
self.assertEqual(menu.option_langue(lue=True), "--lang fr")
|
|
|
|
def test_une_sortie_affichee_suit_la_langue_de_todo(self):
|
|
for langue in ("fr", "en"):
|
|
with mock.patch.object(menu, "get_lang", return_value=langue):
|
|
self.assertEqual(menu.option_langue(), f"--lang {langue}")
|
|
|
|
def test_le_pre_remplissage_parle_la_langue_de_todo(self):
|
|
"""Lancé par sudo -u : EL_LANG n'y arriverait pas, l'option si."""
|
|
with mock.patch.object(menu, "get_lang", return_value="en"):
|
|
cmd = menu.miroir_prefetch_cmd("/tmp/liste", [])
|
|
self.assertIn("--lang en", cmd)
|
|
|
|
def test_le_retrait_d_exception_garde_nft_en_bout_de_tube(self):
|
|
"""Le message humain va à l'erreur standard ; la sortie standard
|
|
porte les règles que nft lit, et ne se traduit jamais."""
|
|
with mock.patch.object(menu, "get_lang", return_value="en"):
|
|
cmd = menu.bypass_retrait_cmd("52:54:00:00:00:04")
|
|
self.assertIn("--lang en", cmd)
|
|
self.assertTrue(cmd.endswith("| sudo nft -f -"))
|
|
|
|
|
|
class TestCeQueLeMenuLit(unittest.TestCase):
|
|
def test_l_occupation_du_miroir_est_lue_en_francais(self):
|
|
vu = {}
|
|
|
|
def faux_lire(cmd, delai=15):
|
|
vu["cmd"] = cmd
|
|
return "objets : 3\ndépôts git : 12 en miroir, 4.2 Gio\n"
|
|
|
|
with mock.patch.object(
|
|
menu.QemuCacheMenuMixin, "_cache_lire", staticmethod(faux_lire)
|
|
), mock.patch.object(menu, "get_lang", return_value="en"):
|
|
rendu = menu.QemuCacheMenuMixin._cache_miroir_occupation()
|
|
self.assertIn("--lang fr", vu["cmd"])
|
|
self.assertEqual(rendu, ("12", "4.2 Gio"))
|
|
|
|
def test_le_journal_est_lu_dans_les_deux_langues(self):
|
|
"""La langue du service est celle de son installation, pas celle de
|
|
qui ouvre le menu : un refus appris doit être reconnu dans les deux."""
|
|
journal = (
|
|
"tunnel opaque retenu pour npm.example (3 échec(s)) : EOF\n"
|
|
"opaque tunnel kept for pypi.example (3 failure(s)): EOF\n"
|
|
)
|
|
commandes = []
|
|
|
|
def faux_lire(cmd, delai=15):
|
|
commandes.append(cmd)
|
|
return journal
|
|
|
|
with mock.patch.object(
|
|
menu.QemuCacheMenuMixin, "_cache_lire", staticmethod(faux_lire)
|
|
):
|
|
hotes = menu.QemuCacheMenuMixin._cache_refus_appris()
|
|
self.assertEqual(hotes, ["npm.example", "pypi.example"])
|
|
for motif in menu.REFUS_APPRIS:
|
|
self.assertIn(motif, commandes[0])
|
|
|
|
def test_le_catalogue_porte_ce_que_le_menu_lit(self):
|
|
"""Une traduction qui s'écarte de ces libellés rendrait le menu
|
|
aveugle sans qu'aucun test du binaire ne le voie."""
|
|
texte = CATALOGUE.read_text(encoding="utf-8")
|
|
self.assertIn('"opaque tunnel kept for %s (', texte)
|
|
for libelle in ("authority : %s", "fingerprint : %s"):
|
|
self.assertIn(f'"{libelle}', texte)
|
|
for prefixe in menu.LIBELLES_TUS:
|
|
self.assertTrue(
|
|
prefixe in texte or prefixe in ("autorité", "empreinte"),
|
|
prefixe,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|