Skip to content

test(localization): #204 field-mapping regression — Rules/Virtues/Scenarii (#216 root cause) - #477

Merged
jsboige merged 1 commit into
masterfrom
test/204-localization-field-mapping
Jun 15, 2026
Merged

test(localization): #204 field-mapping regression — Rules/Virtues/Scenarii (#216 root cause)#477
jsboige merged 1 commit into
masterfrom
test/204-localization-field-mapping

Conversation

@jsboige

@jsboige jsboige commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What

3 NEW additive test files covering the localization field-mapping for the three remaining card types, guarding the #216 class of bug (FrontFieldConversions referencing field names absent from the template → template.Replace is a silent no-op → non-FR PDFs ship French content). Baseline 168/0/5 → 182/0/5.

Why

#216 was caused exactly by this: Fallacies conversions pointed at Titre/Definition/Exemple which never existed in the template. The Fallacies file already has regression tests, but Rules/Virtues/Scenarii had none — the same silent-contamination bug could recur there undetected. Issue #204 flagged "LocalizationConfig — field conversion logic" as Medium Priority.

NEW files (dispatch 3xvx9d amend)

Per ai-01's amend, created as separate files so FallaciesLocalizationTests.cs stays untouched (owned by PR #444 — avoids merge collision). Each applies the real substitution chain (mirrors CardSetLocalization.TranslateCardSetInfo front branch) against the template on disk — no mocking:

File Tests Coverage
RulesLocalizationTests.cs 3 {{markdown Text}}{{markdown Text_<lang>}} per language · root-cause guard (every sourceFieldName must exist as a token in template) · 7-language Text conversion
VirtuesLocalizationTests.cs 3 title/description/remark/family/subfamily/subsubfamily _fr_<lang> for all 7 langs · root-cause token-existence guard · suffix-overlap invariant
ScenariiLocalizationTests.cs 3 FR tokens (titre/catégorie/contexte/enjeu/baratineur/piocheur) → localized cols for en/ru/pt · root-cause guard (accented catégorie included) · ExceptionPatterns presence (asset filenames must backtrack to FR catégorie)

Root-cause guard pattern

Each file has a *_Front_Conversions_Reference_Only_Template_Existing_Tokens test: loops over every sourceFieldName in the FrontFieldConversions and asserts the formatted pattern actually appears in the template. If anyone adds a conversion pointing at a non-existent field, this fails immediately instead of silently shipping FR content. All passed — no latent bug found.

Investigation honesty — one test rewritten

The first Virtues ordering test (Virtues_Front_Conversions_Are_Ordered_MostSpecificFirst) failed: the config orders family_fr before subfamily_fr/subsubfamily_fr (not most-specific-first). I reasoned through it (didn't "verdir" by flipping the assertion):

  • family_fr}} is a suffix-substring of subfamily_fr}} (overlap exists),
  • BUT .Replace("family_fr}}","family_en}}") on {{subfamily_fr}} yields {{subfamily_en}}, and the subsequent steps find no subfamily_fr}} to match (harmless no-op),
  • so the end bindings are correct because the _en destination suffix matches the field suffix.

Rewrote as Virtues_Family_Subtokens_Resist_Suffix_Overlap_Under_Config_Order — empirically asserts the end bindings are correct across all 7 languages (no residual _fr). Documents why the non-most-specific-first order is safe here. No bug; just a fragile-by-chance invariant made explicit.

DoD

  • ✅ 168 → 182 pass / 0 fail / 5 skip (full suite)
  • ✅ Additive only (3 new files, 374 lines, no existing file modified — FallaciesLocalizationTests.cs untouched)
  • ✅ Build green (0 errors)
  • ✅ Real code path, real template files on disk

Scope

Test-only — no CSV, no template, no rendering, no config, no existing test changed. Serves #204 (coverage) + guards #216 recurrence. Release-independent (mapping logic stable regardless of visual validation outcome).

🤖 Worker po-2024 on dispatch 3a0d2l (secondary, per amend 3xvx9d). Verdict QA visuelle stays ai-01.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

…narii (#216 root cause)

Adds 3 NEW additive test files covering the localization field-mapping for the
three remaining card types, guarding the #216 class of bug (FrontFieldConversions
referencing field names that don't exist in the template -> template.Replace is a
silent no-op -> non-FR PDFs ship French content). Baseline 168/0/5 -> 182/0/5.

NEW files (dispatch #204 amend 3xvx9d): created separately so FallaciesLocalizationTests.cs
stays untouched (owned by PR #444, avoids merge collision).

Each file applies the REAL substitution chain (mirrors CardSetLocalization.TranslateCardSetInfo
front branch) against the template on disk — no mocking:

- RulesLocalizationTests.cs (3): {{markdown Text}} -> {{markdown Text_<lang>}} per language;
  root-cause guard (every FrontFieldConversions source must exist as a token in the template);
  7-language coverage of the Text conversion.
- VirtuesLocalizationTests.cs (3): title/description/remark/family/subfamily/subsubfamily _fr
  -> _<lang> for all 7 languages; root-cause token-existence guard; suffix-overlap invariant
  (documents why the non-most-specific-first Virtues order still produces correct bindings —
  the trailing "}}" suffix + matching "_<lang>" destination makes the overlap idempotent).
- ScenariiLocalizationTests.cs (3): FR tokens (titre/catégorie/contexte/enjeu/baratineur/
  piocheur) -> localized columns for en/ru/pt; root-cause token-existence guard (accented
  catégorie included); ExceptionPatterns presence guard (category-based asset filenames must
  backtrack to FR catégorie or images break in non-FR Scenarii).

DoD: 168 -> 182 pass, 0 fail, 5 skip. Additive only (3 new files, no existing file modified).
No bug revealed: all conversions produce correct bindings; the Virtues ordering test was
rewritten from a too-strong substring invariant to an empirical end-binding check after
reasoning showed the overlap is benign (idempotent under matching suffix).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jsboige
jsboige merged commit cc5914e into master Jun 15, 2026
3 checks passed
@jsboige
jsboige deleted the test/204-localization-field-mapping branch June 15, 2026 05:08
jsboige added a commit that referenced this pull request Jun 15, 2026
…columns guard (+2) (#485)

Fallacy is the largest entity (~1408 rows) yet had no LoadFromContent regression
coverage — the #476 note "Fallacy already has coverage elsewhere" is stale (only an
incidental ClassMap registration in MmGeneratorTests existed, asserting no mappings).

Two tests via the real CsvBase<Fallacy,FallacyClassMap>.LoadFromContent path:
- MapsRequiredColumns: pins pk→PK+Id (GetId resolves), FR required columns, and that
  present localized columns (en/zh) map.
- OptionalLocalizedColumns_AbsentDoesNotThrow [highest value]: a FR-only header set
  parses without throwing and leaves every representative Optional column null across
  all 8 languages + Latin/print_and_play. Guards the multilingual harvest — a single
  localized column losing its .Optional() would crash HeaderValidated (#216/#477 class).

Additive only: new file, no existing test/production code modified.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jun 15, 2026
…HeaderValidationException (#488)

New additive test file pinning the bidirectional CSV load contract of the
production CsvBase<T,TMap>.LoadFromContent (CsvHelper 31.0.4). The existing
ClassMap regression suites (#476 EntityClassMapRegressionTests, #485
FallacyClassMapRegressionTests) covered only ONE direction of the contract
("Optional absent -> no throw"). This pins the inverse half + isolation.

Contract halves via a synthetic ContractEntity (decoupled from evolving
domain ClassMaps), exercising the REAL CsvBase configuration:
- required (non-Optional) column absent from header -> HeaderValidationException.
  The guard that catches a dropped .Optional() at load time instead of silently
  zeroing a field (the #216/#477 localization fragility class).
- Optional column absent from header -> no throw, property null.
- short data row -> MissingFieldFound (non-fatal Logger override) -> no throw;
  the missing bound field resolves to EMPTY STRING, not null (distinct from the
  Optional-absent-from-header case which yields null).

3 tests, all green. Full suite 251 passed / 0 failed / 5 skipped (no regression).
Additive only: no production code or existing test modified.

Dispatch 4rkh1s secondaire (#204 fragile zone, new test file).

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
jsboige added a commit that referenced this pull request Jun 16, 2026
#491)

Pin the two fragile zones of the DocumentLocalization mechanism that
existing coverage (MmGeneratorTests) missed:

1. Ordering contract (headline). The family-hierarchy DocumentLocalization in
   AssetConverterConfig carries the documented comment "Order: most specific
   first (Soussousfamille > SousFamille > Famille) to avoid partial matches"
   because DoStaticConversions is a naive substring Replace and "Famille" is a
   substring of "SousFamille". No test pinned this contract. New theory runs the
   REAL family-hierarchy entry (located by TargetProperties, not list index) on a
   template carrying all three family keywords, for all 7 target langs, asserting
   each resolves to its distinct localized property with no "SousFamily"
   contamination — the corruption signature of a broken ordering. Same substring
   fragility class as the card-set mapping (#477) and CSV contract (#488).

2. 8-language release scope. The existing MindMapLocalization theory covers only
   en/ru/pt; the release targets 8 langs and the config declares 7 target langs
   (en/ru/pt/es/ar/fa/zh). New theory closes the gap for es/ar/fa/zh across every
   Fallacy text + family expression, so a dropped conversion surfaces immediately.

Additive only: 1 new file (MindMapLocalizationRegressionTests.cs), 11 tests,
zero production code and zero existing test modified.

Verified: 11/11 new tests green; full suite 270 passed / 0 failed / 5 skipped.

Co-authored-by: Your <your.email@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant