[DTP-Worker 20260806_124544] VÉRIF axe NEUF « atteignabilité transitive d'une assertion » → CLEAN 0/651 (aucun test run-only faux-vert) + 1 durcissement : test_validate_exits_zero passait sans assertion explicite (mordait implicitement via SystemExit) → assertEqual(main(["validate"]),0) strictement plus fort ; détecteur AST teeth-prouvé, résolution transitive du graphe d'appels (évite 40 faux-positifs de délégation _expect_error/_breaks), 0 logique de prod touchée, run_ci 33 PASS 0 FAIL 0 SKIP, pas de gate (#5)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1079,3 +1079,40 @@ jsonschema ne masque AUCUN écart enum/const/schéma.**
|
|||||||
VPS** (#8). `./run_ci.sh` inchangé = **33 PASS · 0 FAIL · 0 SKIP**. Fichiers touchés : ce journal +
|
VPS** (#8). `./run_ci.sh` inchangé = **33 PASS · 0 FAIL · 0 SKIP**. Fichiers touchés : ce journal +
|
||||||
1 entrée mémoire (`schema-oracle-enum-const-vs-S-skip`). Sweep rejouable tel quel (jsonschema 3.2.0
|
1 entrée mémoire (`schema-oracle-enum-const-vs-S-skip`). Sweep rejouable tel quel (jsonschema 3.2.0
|
||||||
local ; stdlib pur sinon ; sans réseau) — **NON promu en gate** (#5, occurrence-zéro).
|
local ; stdlib pur sinon ; sans réseau) — **NON promu en gate** (#5, occurrence-zéro).
|
||||||
|
|
||||||
|
## Session `20260806_124544` — VÉRIF axe NEUF « atteignabilité transitive d'une assertion » (aucun test run-only faux-vert) → CLEAN 0/651 + 1 durcissement (implicite→explicite)
|
||||||
|
|
||||||
|
**Choix de tâche.** `./run_ci.sh` au démarrage = **33 PASS · 0 FAIL · 0 SKIP**, arbre propre, roadmap
|
||||||
|
`ROADMAP_8_WEEKS_OR_LESS.md` intégralement livrée/gatée (Sprints 1→7 ; prod VPS / builds EAS / dépôts
|
||||||
|
ONAPI hors périmètre worker · #8). Registre `OPEN_DECISIONS_REGISTER.md` = 3 arbitrages produit
|
||||||
|
appartenant à Michel (non éditables). Objectif : un **axe teeth encore jamais balayé**.
|
||||||
|
|
||||||
|
**Axe NEUF (absent de la mémoire).** `test-shadowing-sweep` couvre les tests **jamais exécutés** (dup de
|
||||||
|
nom/classe). Distinct et complémentaire : un test **exécuté** mais qui **n'atteint aucune assertion**
|
||||||
|
(run-only) reste vert même si le code casse = faux-vert. Détecteur AST (stdlib pur) : une méthode
|
||||||
|
`test*` est **édentée** si elle ne contient NI assertion directe (`self.assert*`/`self.fail`/
|
||||||
|
`assertRaises`/`pytest.raises`), NI appel levant (`jsonschema.validate`, `_validate(...)`,
|
||||||
|
`check_output/call`, `raise`), NI délégation à un helper de sa classe lui-même édenté-négatif
|
||||||
|
(`self._expect_error`/`_breaks`…), NI appel à une fonction nommée assert/expect/breaks/reject/check.
|
||||||
|
Résolution **transitive** du graphe d'appels intra-classe (sinon 40 faux positifs sur les batteries
|
||||||
|
d'injections négatives qui délèguent à `_expect_error`/`_breaks`).
|
||||||
|
|
||||||
|
**Dents prouvées.** Mutants : un `test_bad` qui ne fait que `print(compute(3))` → **MORD** (True) ;
|
||||||
|
`self.assertEqual(...)` → non signalé ; délégation helper (par set toothed OU par nom) → non signalé
|
||||||
|
(3/3 discriminés).
|
||||||
|
|
||||||
|
**Résultat & durcissement.** Sur **651 méthodes** de test : **1 seul** candidat →
|
||||||
|
`qa/acceptance/tests/test_acceptance.py::TestCLI.test_validate_exits_zero`, qui appelait `gen.main(
|
||||||
|
["validate"])` **sans assertion explicite**. Investigation : ce n'était PAS un faux-vert réel — il mord
|
||||||
|
**implicitement** via `SystemExit` (`cmd_validate`→`_validate_or_die`→`sys.exit(1)` si erreurs). MAIS
|
||||||
|
fragilité latente : un refactor remplaçant `sys.exit(1)` par un `return 1` **silencieux** rendrait le
|
||||||
|
test édenté sans que rien ne rougisse. **Durcissement** (strictement plus fort, aucun inconvénient) :
|
||||||
|
`self.assertEqual(gen.main(["validate"]), 0)` — mord désormais AUSSI sur le chemin retour-non-zéro, et
|
||||||
|
rend les dents **visibles** (aligné sur le style `test_build_writes_outputs` voisin). `main` retourne 0
|
||||||
|
en succès (`cmd_validate` → `return 0`), assertion correcte.
|
||||||
|
|
||||||
|
**Conclusion.** **CLEAN 0/651** après durcissement (le détecteur re-passé confirme 0 édenté). Axe
|
||||||
|
teeth-prouvé. Édition minimale : **1 test durci** (pas de logique de production touchée · #8), suite
|
||||||
|
`qa-acceptance` OK, `./run_ci.sh` = **33 PASS · 0 FAIL · 0 SKIP**. Fichiers : ce journal + le test
|
||||||
|
durci + 1 entrée mémoire (`test-assertion-reachability-sweep`). Sweep **rejouable** (AST stdlib pur) —
|
||||||
|
**NON promu en gate** (#5, occurrence-quasi-zéro après fix).
|
||||||
|
|||||||
@@ -373,8 +373,11 @@ class TestCLI(unittest.TestCase):
|
|||||||
self.assertTrue(os.path.exists(os.path.join(tmp, "MANIFEST.json")))
|
self.assertTrue(os.path.exists(os.path.join(tmp, "MANIFEST.json")))
|
||||||
|
|
||||||
def test_validate_exits_zero(self):
|
def test_validate_exits_zero(self):
|
||||||
# cmd_validate ne lève pas sur le dépôt réel (sinon SystemExit).
|
# cmd_validate ne lève pas sur le dépôt réel (sinon SystemExit via
|
||||||
gen.main(["validate"])
|
# _validate_or_die) ET retourne le code 0. On l'asserte explicitement :
|
||||||
|
# ainsi le test mord aussi si un refactor remplaçait sys.exit(1) par un
|
||||||
|
# `return 1` silencieux (le SystemExit ne serait alors plus levé).
|
||||||
|
self.assertEqual(gen.main(["validate"]), 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user