From 41bb10ab651d7da9e3df764303283366e88e5ba2 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Mon, 14 Sep 2026 14:41:19 -0400 Subject: [PATCH] =?UTF-8?q?[FIX]=20script=20todo=20:=20lire=20=C2=AB=20def?= =?UTF-8?q?ault=20=C2=BB=20l=C3=A0=20o=C3=B9=20le=20fichier=20l'=C3=A9crit?= =?UTF-8?q?=20ainsi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'étiquette « - Default » ne paraissait jamais, ni au choix de version ni au choix d'environnement : les deux lectures interrogeaient « Default » avec une majuscule, quand conf/supported_version_erplibre.json écrit « default ». dict.get rend None sur une clé absente, sans rien dire — la version par défaut passait donc pour une version ordinaire, et rien ne le signalait. Le garde-fou ne vérifie pas ce seul cas : il apparie les deux côtés, et toute clé que le menu interroge doit exister dans le fichier. Vérifié : 4 tests, dont deux tombent si la majuscule revient. --- EN --- The « - Default » label never appeared, neither on the version choice nor on the environment one: both reads asked for « Default » with a capital, while conf/supported_version_erplibre.json writes « default ». dict.get returns None on a missing key without a word — the default version therefore looked like an ordinary one, and nothing said otherwise. The guard does not check that single case: it pairs both sides, and every key the menu asks for must exist in the file. Checked: 4 tests, two of which fail if the capital comes back. Assisted-by: Claude Opus 5 --- script/todo/todo.py | 4 +- test/test_version_menu_keys.py | 101 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 test/test_version_menu_keys.py diff --git a/script/todo/todo.py b/script/todo/todo.py index d0c9eba..6880299 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -424,7 +424,7 @@ class TODO( label += " - Installed" if odoo_version == odoo_installed_version: label += " - Actual" - if version_info.get("Default"): + if version_info.get("default"): label += " - Default" if version_info.get("is_deprecated"): label += " - Deprecated" @@ -5262,7 +5262,7 @@ class TODO( label = f"{key_s}: {erplibre_version}" if odoo_version == odoo_installed_version: label += f" - {t('Current')}" - if version_info.get("Default"): + if version_info.get("default"): label += f" - {t('Default')}" environments[key_s] = { diff --git a/test/test_version_menu_keys.py b/test/test_version_menu_keys.py new file mode 100644 index 0000000..76f30e6 --- /dev/null +++ b/test/test_version_menu_keys.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# © 2026 TechnoLibre (http://www.technolibre.ca) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +"""Le menu des versions lit-il les clés que le fichier des versions écrit ? + +`get_odoo_version()` rend les entrées de `conf/supported_version_erplibre.json` +TELLES QUELLES, plus une clé synthétisée. Le menu les interroge par +`version_info.get("...")`, et `dict.get` sur une clé absente rend None sans +rien dire : une étiquette qui ne s'affiche jamais, et aucun message. + +C'est arrivé — `get("Default")` avec une majuscule contre `"default"` dans le +JSON, à deux endroits : l'étiquette « - Default » ne paraissait ni au choix de +version, ni au choix d'environnement, et la version par défaut passait donc +pour une version ordinaire. + +Le test apparie les deux côtés plutôt que de vérifier ce seul cas : toute clé +que le menu lit doit exister dans le fichier, quelle que soit celle qu'on +ajoutera demain. +""" + +import json +import re +import sys +import unittest +from pathlib import Path + +RACINE = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(RACINE)) + +TODO_PY = RACINE / "script" / "todo" / "todo.py" +VERSIONS = RACINE / "conf" / "supported_version_erplibre.json" + +# Ce que `get_odoo_version` ajoute aux entrées du fichier. +SYNTHETISEES = {"erplibre_version"} + + +def cles_du_fichier(): + with VERSIONS.open(encoding="utf-8") as fh: + data = json.load(fh) + cles = set(SYNTHETISEES) + for entree in data.values(): + cles |= set(entree) + return cles + + +def cles_lues_par_le_menu(): + src = TODO_PY.read_text(encoding="utf-8") + return set(re.findall(r'version_info\.get\(\s*["\']([^"\']+)["\']', src)) + + +class TestClesDuMenuDesVersions(unittest.TestCase): + def test_le_menu_ne_lit_que_des_cles_qui_existent(self): + lues = cles_lues_par_le_menu() + self.assertTrue( + lues, "aucune lecture trouvée : le test ne mesure rien" + ) + inconnues = sorted(lues - cles_du_fichier()) + self.assertEqual( + inconnues, + [], + "le menu interroge des clés que le fichier des versions n'écrit " + f"pas : {inconnues} — dict.get rend None sans rien dire, donc " + "l'étiquette ne paraît jamais", + ) + + def test_la_casse_compte(self): + """La panne d'origine tenait à une seule majuscule.""" + self.assertNotIn( + "Default", + cles_lues_par_le_menu(), + "« Default » est relu avec une majuscule, alors que le fichier " + "écrit « default »", + ) + + def test_une_seule_version_par_defaut(self): + with VERSIONS.open(encoding="utf-8") as fh: + data = json.load(fh) + defauts = [k for k, v in data.items() if v.get("default")] + self.assertEqual( + len(defauts), + 1, + f"{len(defauts)} versions marquées par défaut : {defauts}", + ) + + def test_letiquette_par_defaut_est_atteignable(self): + """La clé lue et la valeur du fichier doivent se rencontrer : c'est ce + que la panne empêchait.""" + from script.todo.version_manager import get_odoo_version + + versions, _installees, _actuelle = get_odoo_version() + marquees = [v for v in versions if v.get("default")] + self.assertEqual( + len(marquees), + 1, + "aucune entrée rendue par get_odoo_version ne porte « default »", + ) + + +if __name__ == "__main__": + unittest.main()