test(fallacy): pin 8 LinkXxFallback cascade contracts + null-floor discovery - #558
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>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] — #558 (test(fallacy): 8 LinkXxFallback cascade contracts + null-floor discovery)
Verdict: clean additive contract suite, mergeable as-is.
Tests are real assertions, not stubs. All 29 use FluentAssertions on actual Fallacy.LinkXxFallback resolution — e.g. LinkFrFallback_PrimaryNull_FallsBackToEn asserts .Should().Be("en-link") against a Fallacy with LinkFr = null, and NonSource_TargetAndEnNull_FrPresent_ReturnsFr (Theory ×6) asserts the fragile French floor .Should().Be("fr-link"). No Assert.True(true), no empty bodies.
8 cascades are coherent and match the body. Fr/En are 2-deep (5 [Fact]s: Fr→En primary/fallback/null + En→Fr primary/fallback), the 6 non-source langs (Ru/Pt/Es/Ar/Fa/Zh) are 3-deep via 4 [Theory] branches ×6 = 24. Spot-checked the 3-deep chain: when LinkRu=null, LinkEn="en-link", the test asserts the fallback returns "en-link" (target→En), and when only LinkFr is set it asserts "fr-link" (the source-language floor) — that's the real target→En→Fr cascade, not a symmetric 2-deep.
Null-floor discovery is genuine and well-documented. The body's claim holds: IsNullOrEmpty(LinkFr) ? LinkEn : LinkFr with both null returns LinkEn which is null — NOT string.Empty. The LinkFrFallback_BothNull_ReturnsNull and NonSource_AllNull_ReturnsNull (Theory ×6) tests pin this real behavior. The author notes 7 initial assertions had to flip from BeEmpty() to BeNull() — i.e. the tests were corrected to match the actual contract rather than an idealized one. Worth knowing because that null propagates to Mustache as empty text but stays null to any C# consumer — a silent-wrong-output hazard the contract now documents.
Additive-only confirmed: single new file FallacyLinkFallbackTests.cs, +167/-0, zero production .cs modified. Class compiles-coherent: proper using (xunit, FluentAssertions, Entities), correct [Fact]/[Theory]/[InlineData] attributes, namespace matches folder structure, reflection helpers (SetLink/GetFallback) are well-formed for the 6-lang Theory fan-out. Security scan clean — no secrets/keys/paths, all test data synthetic ("fr-link", "xx-link").
Comment-only (self-review cap).
What
Pin the 8 per-language
LinkXxFallbackcascades onFallacy(Fr/En = 2-deep, Ru/Pt/Es/Ar/Fa/Zh = 3-deep: target → En → Fr) — the mind-map link resolution that feedsFallacyMindMapDocumentConfig.LinkExpression({item.LinkFrFallback}). 29 contract tests, additive only, no production code change. Full suite 417/0/5.This is the ai-01 "couverture tests" runway (dispatched at the 05:08Z tick after merging #555/#556/#557).
Why
French is the source language. The cascade design is intentionally asymmetric — a subtle fragility that had zero unit coverage despite being referenced in
MmGeneratorTestsandMindMapLocalizationRegressionTests:LinkFrFallback/LinkEnFallbackare 2-deep (Fr↔En: the two source-ish languages).LinkRu/Pt/Es/Ar/Fa/Zhare 3-deep: target → En → Fr.The silent-wrong-output risk: a fallacy that ships with only a French link must still resolve to French in every non-source export. A refactor that "symmetrizes" the cascade (e.g. makes every language a 2-deep target→Fr, or reorders En/Fr) would drop the French floor — producing a mind-map node with an empty link, no exception, no log, invisible except by opening every generated mind-map.
Discovery surfaced by writing the tests
The cascade floor is
null, notstring.Empty. When all three link sources are null, the expressionIsNullOrEmpty(LinkEn) ? LinkFr : LinkEnreturnsLinkFrwhich is null. 7 of the initial assertions had to flip fromBeEmpty()toBeNull()— the tests now pin the real contract, not an idealized one. Thisnull-propagation (empty text in Mustache, but null to any C# consumer) is itself a silent-wrong-output hazard worth documenting.The 29 tests (
FallacyLinkFallbackTests)Reflection helpers (
SetLink/GetFallback) let one[Theory]cover all 6 non-source languages uniformly — 4 branches × 6 languages = 24 assertions from 4 methods.Verification
Related
d09778ea).MmGeneratorTests/MindMapLocalizationRegressionTests.🤖 Generated with Claude Code