test(virtue): #499 VirtueClassMap regression via LoadFromContent (5 tests) - #559
Conversation
…scovery
Pin the 8 per-language LinkXxFallback cascades on Fallacy (Fr/En = 2-deep,
Ru/Pt/Es/Ar/Fa/Zh = 3-deep: target → En → Fr), the mind-map link resolution that
feeds FallacyMindMapDocumentConfig.LinkExpression ({item.LinkFrFallback}) and is
referenced — but NOT behavior-pinned — by MmGeneratorTests and
MindMapLocalizationRegressionTests.
29 contract tests (3 Fr + 2 En + 6×4 Theory for non-source languages) via reflection
helpers so one Theory covers all 6 languages uniformly.
Discovery surfaced by the tests: the cascade floor is NULL, not string.Empty — when
all three link sources are null, the expression returns null (not ""), which propagates
into the Mustache template as empty text but is null to any C# consumer. A silent-
wrong-output hazard documented explicitly (7 of the initial assertions had to flip from
BeEmpty to BeNull — the test now pins the REAL contract).
This is the ai-01 "couverture tests" runway. No production code change — additive tests
only. Full suite green (417/0/5, baseline 388 + 29 new).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ests) Add VirtueClassMapRegressionTests — the remaining uncovered production entity ClassMap (Virtue, the #499 scale-up taxonomy entity, 223 nodes / 194 leaves). Companion to FallacyClassMapRegressionTests and EntityClassMapRegressionTests. 5 contract tests via the real load path CsvBase<T,TMap>.LoadFromContent: - MapsRequiredColumns: 16 required FR columns (pk, path, depth, decimal_path_padded, family_fr, subfamily_fr, subsubfamily_fr, title_fr, description_fr, remark_fr, link_fr, family_fr_camelcase, depth_max4, card, update, locked) all resolve. - PkPopulatesBothIdAndPk: the double map pk → Id AND Pk, so GetId() resolves (silent-failure source if either binding drops). - OptionalLocalizedColumnsAbsent_DoesNotThrow: 28 optional en/ru/pt/es columns may be absent without HeaderValidated throwing (FR-only header set loads clean). - OptionalLocalizedColumnsPresent_MapsThem: Optional = "may be absent", not "ignored". - RequiredColumnAbsent_Throws: a required FR column drift fails LOUD (HeaderValidationException), not silent null — the contract that prevents data loss. This is the ai-01 "couverture tests" runway. Additive only, no production code change. Full suite green (422/0/5, baseline 417 + 5 new). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] — #559 (test(virtue): #499 VirtueClassMap regression via LoadFromContent, 5 tests)
Verdict: clean additive contract suite, mergeable. Verified the 5 tests against the production VirtueClassMap — every column the tests pin is real, and the fragilities they guard are genuine.
ClassMap cross-check (read Virtue.cs at PR head) — the test's VirtueRequiredHeader constant matches production exactly:
- 17 non-Optional
Map()calls → 16 distinct required column names (because bothIdandPkmap to"pk"): pk, path, depth, decimal_path_padded, family_fr, subfamily_fr, subsubfamily_fr, title_fr, description_fr, remark_fr, link_fr, family_fr_camelcase, depth_max4, card, update, locked. Identical to the constant. - 28 optional localized columns (en/ru/pt/es × 7: family/subfamily/subsubfamily/title/description/remark/link), all
.Optional(). Matches the "28 localized" claim.
5 real assertions (not stubs) — all FluentAssertions on actual Virtue.LoadFromContent(csv) resolution:
MapsRequiredColumns— asserts every required FR property (Pk/Path/Depth=1 int/DecimalPathPadded/FamilyFr/SubfamilyFr/SubsubfamilyFr="" empty-cell/TitleFr/DescriptionFr/RemarkFr/LinkFr/FamilyFrCamelcase/DepthMax4/Card/Update/Locked) against an inline CSV. Depth asintparsed from "depth" column is the non-trivial check.PkPopulatesBothIdAndPk— pins the real double-map:Map(Id).Name("pk")+Map(Pk).Name("pk")both bind to thepkcolumn, soId/Pk/GetId()all resolve to "1.1". Genuine silent-failure source (dropping either binding →GetId()returns null → broken keyed generation).OptionalLocalizedColumnsAbsent_DoesNotThrow— FR-only header loads without throwing; absent optional cols → null (TitleEn/TitlePt/FamilyEs). Correct.Optional()contract.OptionalLocalizedColumnsPresent_MapsThem— when present, title_en/title_pt/family_es map to their properties. Optional ≠ ignored.RequiredColumnAbsent_Throws— droppingtitle_fr(non-Optional) from header →HeaderValidationException. Correct CsvHelper default (strict header validation for non-optional maps = loud failure, prevents silent data loss). The extra-row / MissingFieldFound nuance is correctly reasoned in the comment.
Additive-only: 2 new test files, +330/-0, zero production .cs modified. Self-contained inline CSV (no external fixtures). Security scan clean — all data synthetic.
One non-blocking note: this PR also adds FallacyLinkFallbackTests.cs (+167/-0), identical to the file in #558 — #559's branch was cut from a state including #558. If #558 merges first, #559's diff auto-shrinks to just VirtueClassMapRegressionTests.cs (the actual subject). Worth knowing for merge ordering, not a defect.
Comment-only (self-review cap).
…sts) (#561) Add TestFallacyCardClassMapRegressionTests — the LAST entity ClassMap without a dedicated regression suite. This closes the ClassMap matrix at 100%: Fallacy (#485), Rule/ArgumentVirtue/Scenario (#476), Virtue (#559), DnnUiString, and now TestFallacyCard. TestFallacyCard is the simplified fallacy entity for visual-test card generation. Its ClassMap declares 5 columns ALL required (non-Optional): Id, Title, Type, IllustrationPath, Description. 5 contract tests via the real load path CsvBase<T,TMap>.LoadFromContent: - MapsAllColumns: all 5 required columns map to their properties. - GetIdReturnsId: GetId() returns Id (the image-generator output-dictionary key). - RequiredColumnAbsent_Throws: a required column drift fails LOUD (HeaderValidationException), not silent null — the data-loss-prevention contract. - ShortRow_DoesNotThrow: a ragged row yields string.Empty for missing fields (MissingFieldFound is a log callback, not throw) — mirrors CsvBaseStrictContractTests. - MultipleRows_LoadInOrder. This is the ai-01 SECONDAIRE dispatch ("TestFallacyCard ClassMap — ferme la matrice"). Additive only, no production code change. Full suite green (434/0/5, baseline 429 + 5). Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
What
Add
VirtueClassMapRegressionTests— 5 contract tests for the remaining uncovered production entity ClassMap (Virtue, the #499 scale-up taxonomy entity, 223 nodes / 194 leaves). Companion toFallacyClassMapRegressionTestsandEntityClassMapRegressionTests. Additive only, no production code change. Full suite 422/0/5.This is the ai-01 "couverture tests" runway (continued after #558).
Why
The CsvHelper
ClassMapregression is a documented recurring silent-failure source (CLAUDE.md "CsvHelper Mapping" + the #216/#477 localization-mapping regression). 7 production ClassMaps exist; before this PR, Virtue had zero coverage (alongsideTestFallacyCard, which is visual-test-only and lower-stakes). Virtue is the entity for the active #499 scale-up lane — a ClassMap drift here = silent data loss on Virtue generation, with no signal until a card renders empty.VirtueClassMapdeclares 16 required FR columns (non-Optional) + 28 localized columns (en/ru/pt/es × 7) all.Optional(). The contract that must hold:HeaderValidatedthrows when a FR-only header set loads (breaks the multilingual harvest).pk→IdANDPk—GetId()returnsId; if either binding drops,GetId()returns null and any keyed generation pass breaks.The 5 tests (
VirtueClassMapRegressionTests)MapsRequiredColumnsDepthint parse)PkPopulatesBothIdAndPkpkcolumn →Id+Pk;GetId()resolves (the silent-failure guard)OptionalLocalizedColumnsAbsent_DoesNotThrowOptionalLocalizedColumnsPresent_MapsThemRequiredColumnAbsent_ThrowsHeaderValidationException), not silentAll via the real load path
CsvBase<T,TMap>.LoadFromContent, inline CSV (no external fixture files), self-contained.Verification
Related
FallacyClassMapRegressionTests(test(parsing): #204 Fallacy ClassMap regression — Optional localized columns guard (+2) #485),EntityClassMapRegressionTests(test(parsing): #204 ClassMap regression tests — Rule/ArgumentVirtue/Scenario/Virtue (+9, 159→168) #476),CsvBaseStrictContractTests.🤖 Generated with Claude Code