test: corpus de fixtures CSV et contrat de detection #333
No reviewers
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/Simpl-Resultat#333
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-326-corpus-fixtures-csv"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Premier maillon de la pile import-CSV (#326 -> #323 -> #324 -> #325 -> #327 -> ... -> #332). Base
main, aucune dependance.Ce que fige cette PR
autoDetectConfig,detectAmountMode,detectSingleAmount,preprocessQuotedCSVetparseFrenchAmountn'avaient aucun test sur les 871 du projet, alors qu'ils portent chaque montant importe. Cette PR pose la reference contre laquelle la refonte se mesurera, avant qu'elle ne bouge.Aucun fichier de production n'est modifie.
csvAutoDetect.tsetamountParser.tssont identiques amain.Corpus —
src/__fixtures__/csv/(11 fichiers)Synthetiques, aucune donnee reelle. Ils couvrent des formes qu'un vrai releve ne contient jamais toutes a la fois, ce qui est l'interet :
signed-amount.csvdebit-credit.csvdebit-credit-reversed.csvunused-column-zero.csvpreamble.csvskipLines: 3)header-with-number.csvheader-numeric-label.csvno-header.csvall-positive.csvdesjardins-quoted.csvabsolute-indicator.csvLes cas defaillants, marques explicitement
Chaque
describeporteKNOWN DEFECT+ l'issue qui doit le corriger ; chaque assertion porte// DEFECT — should be X. Ungrep "KNOWN DEFECT"enumere l'ensemble.detectAmountModeaffecte la colonne la plus a gauche adebitAmount, sans jamais lire les libelles. SurDate;Description;Credit;Debit, tous les signes sont inverses — et le total se contente de changer de signe, donc aucun controle agrege ne l'attrape.0,00(#325) — la detection du mode est correcte ; c'est le parsing qui casse. La regle branche surisNaN(credit)et"0,00"rend 0, pas NaN : chaque debit s'importe a 0, seuls les credits survivent.parseFloatrend le prefixe numerique,hasNumberbascule,hasHeaderdevientfalseet la ligne d'en-tete part en ligne de donnees.positive_expenseest deduit, chaque credit s'importe en depense. Un test montre que ce fichier est aujourd'hui indiscernable deall-positive— c'est le discriminant que #328 doit poser.Points a savoir pour la revue
Un 11e fixture a ete ajoute. Le corps de l'issue nomme « en-tete contenant un nombre (« Solde 2024 ») ». Mesure : ce cas n'est pas defaillant —
parseFrenchAmount("Solde 2024")rend NaN,parseFloats'arretant sur leSinitial, doncdetectHeaderrepond correctement. Le defaut exige une cellule qui commence par des chiffres.header-with-number.csvest donc conserve tel que l'issue le nomme et fige comme fonctionnel (avec le commentaire expliquant pourquoi il survit), etheader-numeric-label.csvest ajoute pour le cas qui echoue vraiment. Sans lui, le vrai defaut serait « silencieusement absent », ce que le critere d'acceptation interdit.Les tests bout-en-bout rejouent un MIROIR de la regle du wizard.
mapRown'existe pas encore (c'est #325 qui l'extrait), la regle vit dans unuseCallbackdeuseImportWizard.parseFilesInternal(:489-542) et le depot n'a pas de jsdom. La copie est donc locale au fichier de test — et gardee : un test statique relituseImportWizard.tset verifie que les 5 expressions de production y sont toujours litteralement presentes (meme pattern que__integration__/transactions-transfer-icon.test.ts). Quand #325 les changera, le garde tombera bruyamment, et son commentaire dit au worker suivant de supprimer le miroir et d'importer le vraimapRow.Trouvaille hors perimetre nomme. La revision
/review-specdemandait d'etendre les assertions aux sites d'appel holdings. Ce faisant : une colonne de prix portant son code de devise ("150,25 CAD") enregistre la position a 15025 dans l'import de titres #245 — meme erreur de facteur 100, silencieuse, etisNaNla laisse passer. Figee dansamountParser.test.ts, a corriger par #325 (durcissement global, decision tranchee du plan).Methode. Chaque valeur attendue vient de l'execution du code, pas de sa lecture — un probe jetable a vide
autoDetectConfig+ la regle rejouee sur chaque fixture, puis les assertions ont ete ecrites depuis cette sortie. Deux attentes auraient ete fausses en devinant (header-with-numberet le fait queunused-column-zerodetecte bien son mode).Verification
npm test— 924 passed (55 fichiers), contre 871 avant. +53 tests.npm run build— tsc + vite propres.Resolves #326
Generated autonomously by /autopilot run of 2026-08-13
6c95e61d7ftoc88ebd862e/pr-review — APPROVE
Resume. PR test-only exemplaire : elle fige par caracterisation cinq fonctions qui portaient chaque montant importe sans avoir une seule assertion, et elle marque explicitement les comportements geles comme fautifs plutot que de les blanchir. Chaque affirmation a ete re-verifiee contre le code de production — toutes tiennent, y compris les deux que la revue aurait pu contester.
Verifications faites (pas seulement lues)
csvAutoDetect.ts/amountParser.tsintacts.test.tsuseImportWizard.ts:489-542Papa.parse(preprocessed, { delimiter, skipEmptyLines: true })detectAmountModebouclea < b, retournedebitCol: colAsans lire aucun libellepositive_expensepar absence de negatifs (#329)negCount / total > 0.5, aucun autre signaldetectHeaderaveugle!isNaN(parseFrenchAmount(trimmed))seul"100,00 CAD"->"10000CAD"->parseFloat= 10000tsc && vite buildexecute ligne 486 ; 55 fichiers / 924 tests passes — colle au corps de la PR (871 + 53).skip/.onlyDeux points ou la PR a raison contre l'intuition
C'est ce qui la credibilise :
header-with-number(« Solde 2024 ») n'est pas defaillant —parseFloats'arrete sur leS— et le 11e fixture ajoute pour le vrai cas (header-numeric-label) est la bonne reponse plutot que de faire passer l'issue pour satisfaite. Idem pourunused-column-zero, ou la detection du mode est correcte et seul le parsing casse : la PR ne confond pas les deux etages.Suggestions non bloquantes
Aucune ne justifie de retenir le merge.
src/__fixtures__/csv/index.ts:113—fs/path/import.meta.dirnamene type-checkent que parce que@types/node@25.2.1est installe transitivement ; il n'est pas declare endevDependencies. Le precedenttransactions-transfer-icon.test.ts:38fait deja pareil, donc l'exposition n'est pas neuve — mais la declarer explicitement coute une ligne et evite un « Cannot find module 'fs' » incomprehensible le jour ou une dependance la lache.csvAutoDetect.test.ts:563— le garde statique casse sur un simple reflow Prettier, avec un message qui ne dit rien du formatage. Le commentaire l'explique deja ; a garder en tete pour #325.mapCorpusRowomet letry/catchque la production enroule autour du mapping. Sans effet sur le corpus actuel, mais si #325 rendmapRowcapable de lever, le miroir plantera le test la ou la production emettrait une ligne d'erreur.csvAutoDetect.test.ts:837— « A semicolon file can never match it » est une sur-affirmation : un fichier;dont un champ contient,""matcherait. Contrive, mais un futur lecteur fera confiance a l'absolu.Review adversariale via
/pr-review.Mergée dans
mainen fast-forward avec le reste de la pile (tip37b832e).Forgejo ne détecte pas un merge local comme merged — la PR est donc fermée à la main, et l'issue liée s'est fermée automatiquement via son
Resolves #N.Tip cumulé validé avant push : 1181 vitest, 111 tests Rust, build tsc + vite. La CI ne tourne pas sur push
main, cette validation locale était donc le seul filet.Pull request closed