Skip to content

fix(tests): exiger que le cleanup EFFACE le marqueur, pas qu'il le mentionne - #55

Closed
stephrobert wants to merge 4 commits into
mainfrom
fix/marqueurs-vraiment-effaces
Closed

fix(tests): exiger que le cleanup EFFACE le marqueur, pas qu'il le mentionne#55
stephrobert wants to merge 4 commits into
mainfrom
fix/marqueurs-vraiment-effaces

Conversation

@stephrobert

Copy link
Copy Markdown
Owner

Summary

Le test devait garantir qu'un lab reste rejouable : ce qu'un creates: protège,
le cleanup.yaml doit le rendre. Sa vérification était assert marqueur in texte — le chemin apparaît quelque part dans le fichier. Un commentaire qui
le cite, ou la lecture . /root/xxx.env, satisfaisaient la condition sans rien
supprimer.

Le défaut, reproduit avant d'être corrigé

La mutation exacte que décrit l'issue, sur l2-filesystem-create-xfs :

-          rm -f /root/xfs-disk.env /root/.xfs-lab-ready
+          echo "chemins cités mais jamais effacés : /root/xfs-disk.env /root/.xfs-lab-ready"
Résultat
avant le correctif 45 passed, 1 skippedle test ne voit rien
après le correctif 2 failed, 43 passed, 1 skipped

C'est le piège que la partie « comptes » du même module avait déjà corrigé
avec _est_supprime(), dont la docstring note : « ce test a commencé sa vie
ainsi, et il passait sur un cleanup dont on venait de retirer le userdel »
.
La leçon n'avait pas été reportée sur les marqueurs.

Cinq labs remontés, cinq corrigés, aucun exempté

La vérification stricte a fait remonter cinq labs — l'issue le prévoyait — et
tous pour la même raison : le cleanup lit le .env pour connaître le disque,
puis ne l'efface jamais.

Lab Ce qui manquait
l2-autofs-ondemand autofs-disk.env absent du rm
l2-disk-space-troubleshoot dsk-disk.env absent de la loop state: absent
l2-partition-gpt aucune suppression du tout
l2-storage-performance perf-disk.env absent du rm -rf
l3-fs-readonly-recover fsro-disk.env absent du rm -f — le lab de #48

La suppression est placée après la lecture dans chaque cas : l'effacer avant
priverait le cleanup de la variable dont il a besoin.

Trois formes distinctes, traitées une par une plutôt que par une substitution
générale qui aurait placé le rm au mauvais endroit dans au moins l'une d'elles.

Ce que _est_efface() accepte

Deux formes, et seulement elles :

  • shell : rm suivi du chemin, éventuellement parmi d'autres ;
  • Ansible : une tâche portant à la fois le chemin et state: absent.

Vérification

  • la mutation de l'issue rend le test rouge ;
  • retirer le .env d'un cleanup corrigé le rend rouge aussi ;
  • les cinq cleanup.yaml restent du YAML valide et passent
    ansible-playbook --syntax-check ;
  • 871 tests verts, dsoxlab validate-structure : les 84 labs valides.

Critères de l'issue

Related issues

Closes #49
Refs #48 — le marqueur de l3-fs-readonly-recover est corrigé ici. Le point 1 de
cette issue (l'hôte qui devient injoignable en séquence) reste ouvert : il demande
une campagne de validation réelle.

🤖 Generated with Claude Code

stephrobert and others added 4 commits July 27, 2026 13:59
Première campagne de validation complète du catalogue : les 84 labs rejoués
sur KVM avec contrôle négatif (rouge sans la solution, vert avec). Dix défauts
sont remontés, tous invisibles jusque-là. Les tests d'un lab ne tournent que si
quelqu'un le joue, et personne ne les enchaînait.

Trois labs étaient purement infaisables. l4-reverse-proxy-lb et
l4-ldap-integration démarraient leur service sur le second nœud sans ouvrir le
port dans son pare-feu : HAProxy rendait un 503, SSSD ne résolvait rien.
l2-filesystem-create-xfs n'était rejouable qu'une fois, son cleanup laissant le
marqueur qui garde son setup.

Six cleanup.yaml ne pouvaient pas s'exécuter : « . fichier || exit 0 » ne
protège rien, un sh POSIX non interactif meurt sur le source avant d'atteindre
le ||. Leur dsoxlab reset échouait, donc l'apprenant restait bloqué.

Le reste tient à l'état partagé entre labs, qui se joue toujours de la même
façon : le lab fautif reste vert, et c'est le suivant qui échoue. Trois labs
laissaient une partition sur le disque commun, trois autres laissaient leurs
comptes (alice, appuser, smbuser…) et drill-firewall laissait firewalld
désactivé, ce qui cassait le capstone RHCSA.

Un cas inverse mérite d'être noté : sur l2-luks-encryption, c'était le TEST qui
était faux, pas la solution. Il cherchait le « 2 » de Version: dans une tranche
de 8 caractères, alors que l'alignement de cryptsetup le place en 9e position.
Le volume était en LUKS2 depuis le début.

Trois garde-fous accompagnent ces corrections, chacun vérifié en le faisant
échouer :

- test_playbooks_syntaxe.py joue ansible-playbook --syntax-check sur les 129
  playbooks. Un YAML valide ne prouve pas qu'Ansible charge les tâches : une
  apostrophe française dans un bloc shell casse le découpage des arguments, et
  cela ne se voyait qu'à l'exécution, en rc=4 sans une seule tâche jouée.
- test_marqueurs_setup_cleanup.py exige qu'un cleanup rende ce que son setup
  garde : marqueurs « creates: » et comptes créés. Il a trouvé deux défauts que
  la lecture manuelle avait manqués, dont smbuser.
- verify-solutions.py compare l'état du disque partagé avant et après chaque
  lab vm, et nomme le lab qui salit au lieu de laisser échouer le suivant.

Enfin, la séquence l1 exigeait d'éditer un fichier sans jamais l'avoir
enseigné : l1-first-terminal passe en tête et gagne une section sur nano et la
survie sous vi. Le challenge interdit la redirection, l'éditeur était donc
obligatoire et introuvable dans le cours.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…jamais traduite

Le job « Pre-commit parity » joue les hooks sur TOUS les fichiers, là où un
commit ne les joue que sur les fichiers touchés. Il révélait donc 27 problèmes
ruff préexistants, dans du code que personne n'avait relinté depuis que le hook
utilise `uvx ruff` (donc la dernière version publiée, pas un pin).

Dix-sept se corrigent d'eux-mêmes (imports, `re.M` en `re.MULTILINE`). Les dix
autres demandaient une décision :

- cinq subprocess.run sans `check` explicite, dans des tests de labs et le
  conftest racine. Tous JUGENT le code de retour, ils ne doivent surtout pas
  lever dessus : `check=False` est rendu explicite.
- `datetime.date.today()` sans fuseau, dans le test l1 qui compare l'année
  courante. Remplacé par une heure locale assumée.
- un `if` imbriqué inutile dans le conftest racine.
- un shebang sans bit exécutable sur gen_catalog.py.

Les deux dernières étaient le même défaut, et il avait une conséquence
visible : deux conditions « if lang == en else » dont les deux branches
disaient exactement la même chose. La variante anglaise n'avait jamais été
écrite, si bien que le README ANGLAIS affichait sa légende de catalogue en
français. Elle est traduite, et les deux catalogues sont régénérés.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ça échoue

Trois collections hors ansible.builtin sont employées et aucune n'était
déclarée. Deux se trouvaient installées par hasard sur la machine de
développement, la troisième non, et le capstone rhcsa-mock-exam échouait alors
sur « rc=4, Stats : {} » — le code « unreachable » d'Ansible, qui envoie
chercher un problème de réseau pendant que la cause est une dépendance absente.
Quatre pistes ont été instruites avant la bonne, dont l'état des VM,
l'inventaire et la clé SSH, alors que les trois hôtes répondaient au ping.

Le volet qui vaut le plus n'est donc pas requirements.yml, c'est le message :
la fixture remonte désormais la première ligne d'erreur d'Ansible, qu'elle
avait déjà sous la main dans PlaybookResult.stdout et n'utilisait pas.

Un test refuse qu'une collection employée manque à la déclaration. Il porte sur
ce que les fichiers emploient, pas sur ce que la machine a d'installé : une
suite verte parce que le poste est bien garni ne dit rien de celui du prochain
qui clone. Il déchiffre les solutions au passage, et un second test vérifie que
ce déchiffrement a bien eu lieu, sans quoi le premier passerait au vert sans
avoir rien regardé.

Un premier jet du motif cherchait mot.mot.mot n'importe où et remontait
auto.master, logs.tar, net.ipv4 et dl.flathub — des chemins, une archive et un
nom de domaine. Un garde-fou qui crie au loup se fait désactiver, donc il ne
regarde que la position de clé dans une tâche, et la raison est écrite dans le
fichier.

Éprouvé par mutation : retirer community.crypto de la déclaration fait échouer
le test en nommant le fichier fautif.

Au passage, runtime.hosts_required disparaît du capstone : le champ n'est lu ni
par dsoxlab ni par ce dépôt, et le compte d'hôtes est déjà porté par
runtime.targets[].

Validation : 84 labs verts sur 84, infra provisionnée, solutions rejouées.

Closes #50

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ntionne

Le test devait garantir qu'un lab reste rejouable : ce qu'un « creates: »
protège, le cleanup doit le rendre. Sa vérification était « assert marqueur in
texte » — le chemin apparaît quelque part dans le fichier. Un commentaire qui le
cite, ou la lecture « . /root/xxx.env », satisfaisaient la condition sans rien
supprimer.

Défaut reproduit avant d'être corrigé, avec la mutation exacte de l'issue : sur
l2-filesystem-create-xfs, remplacer le « rm -f » par un « echo » qui cite les
deux mêmes chemins laissait la suite à 45 passed. Plus aucun marqueur n'était
effacé, et le test ne voyait rien.

C'est le piège que la partie « comptes » du même module avait déjà corrigé avec
_est_supprime(), dont la docstring note : « ce test a commencé sa vie ainsi, et
il passait sur un cleanup dont on venait de retirer le userdel ». La leçon
n'avait pas été reportée sur les marqueurs.

_est_efface() n'accepte que deux formes, celles qui suppriment vraiment : un
« rm » shell portant le chemin parmi d'autres, ou une tâche Ansible qui porte à
la fois le chemin et « state: absent ».

La vérification stricte a fait remonter CINQ labs, comme l'issue le prévoyait, et
tous pour la même raison : le cleanup lit le fichier .env pour connaître le
disque, puis ne l'efface jamais. Aucun n'est exempté, les cinq sont corrigés —
l2-autofs-ondemand, l2-disk-space-troubleshoot, l2-partition-gpt,
l2-storage-performance et l3-fs-readonly-recover. La suppression est placée
APRÈS la lecture dans chaque cas : l'effacer avant priverait le cleanup de la
variable dont il a besoin.

Trois formes distinctes selon les labs, traitées une par une plutôt que par une
substitution générale qui aurait placé le rm au mauvais endroit : ajout au rm
existant, ajout à la loop d'un « file: state: absent », et création du rm là où
il n'y en avait aucun (l2-partition-gpt n'effaçait rien du tout).

Vérifié : la mutation de l'issue rend maintenant le test rouge, retirer le .env
d'un cleanup corrigé aussi, les cinq cleanups restent du YAML valide et passent
le --syntax-check d'ansible-playbook, 871 tests verts et les 84 labs valides.

Closes #49
Refs #48
@stephrobert

Copy link
Copy Markdown
Owner Author

Remplacée par #56 : cette branche partait d'un main antérieur au merge de #46 et ne se rebasait pas proprement (7 conflits, dont un add/add sur le fichier central). Même contenu, rejoué sur le main actuel et revérifié — 880 tests verts, la mutation de l'issue rend bien le test rouge.

@stephrobert
stephrobert deleted the fix/marqueurs-vraiment-effaces branch August 24, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Le test des marqueurs exige la mention du chemin, pas sa suppression

1 participant