diff --git a/Generation/Converters/Argumentum.AssetConverter.Tests/WebBasedGenerator/DocumentCardSetCmykContractTests.cs b/Generation/Converters/Argumentum.AssetConverter.Tests/WebBasedGenerator/DocumentCardSetCmykContractTests.cs new file mode 100644 index 000000000..0b720b8ea --- /dev/null +++ b/Generation/Converters/Argumentum.AssetConverter.Tests/WebBasedGenerator/DocumentCardSetCmykContractTests.cs @@ -0,0 +1,165 @@ +using Argumentum.AssetConverter; +using FluentAssertions; +using Xunit; + +namespace Argumentum.AssetConverter.Tests.WebBasedGenerator +{ + /// + /// Contract pin for the CMYK Debug/Release resolver on . + /// + /// The pipeline produces images with different color spaces per build mode (documented in the + /// project CLAUDE.md "Debug vs Release Builds" table): + /// + /// ModeCMYK conversion + /// Debug (dotnet run)Disabled — RGB, preview-friendly, smaller files + /// Release (-c Release)Enabled — CMYK, printer quality + /// + /// carries a XxxDebug/XxxRelease property pair and a + /// resolver: + /// config.UseDebugParams ? ConvertToCmykDebug : ConvertToCmykRelease, where + /// UseDebugParams = (isInDebugMode || ForceDebugParams) && !ForceReleaseParams. + /// + /// No test exercised this resolver before. A swapped ternary, drifted default, or a regression + /// making the legacy field leak into the resolution + /// would silently flip the color space per build mode — Debug previews would balloon to CMYK + /// size, or Release print output would ship as RGB. These tests pin the contract additively. + /// + /// Deterministic across build modes: the ForceDebugParams/ForceReleaseParams flags + /// drive UseDebugParams directly, so the assertions hold whether the test assembly is + /// compiled Debug or Release (independent of the #if DEBUG isInDebugMode term). + /// Additive only: no production code or existing test is modified. Dispatch #204 primaire. + /// + public class DocumentCardSetCmykContractTests + { + /// + /// Config forced into Debug-params resolution: ForceDebugParams sets the first term + /// of UseDebugParams true, so UseDebugParams is true regardless of the + /// compile-time #if DEBUG flag. + /// + private static AssetConverterConfig ForcedDebug() => new AssetConverterConfig + { + ForceDebugParams = true, + ForceReleaseParams = false + }; + + /// + /// Config forced into Release-params resolution: ForceReleaseParams makes the + /// && !ForceReleaseParams term false, so UseDebugParams is false. + /// This is the documented JSON override ("ForceReleaseParams = true to use Release params + /// in Debug builds"). + /// + private static AssetConverterConfig ForcedRelease() => new AssetConverterConfig + { + ForceReleaseParams = true + }; + + // ───────────────────────────────────────────────────────────────────────────── + // (1) DEFAULTS — the documented Debug/Release color-space contract. A fresh + // DocumentCardSet resolves to RGB in Debug and CMYK in Release. This is the table + // in CLAUDE.md; pinning it catches a drifted default (e.g. ConvertToCmykDebug=true) + // that would silently ship CMYK-sized Debug previews. + // ───────────────────────────────────────────────────────────────────────────── + + [Fact] + public void Defaults_DebugResolution_YieldsRgb_NoCmykConversion() + { + var cardSet = new DocumentCardSet(); + cardSet.ConvertToCmykDebug.Should().BeFalse( + "Debug builds use RGB (preview-friendly, smaller files) — the documented default"); + + cardSet.GetConvertToCmyk(ForcedDebug()).Should().BeFalse( + "in Debug-params resolution the resolver returns ConvertToCmykDebug, which defaults " + + "to false (RGB); a regression here would balloon Debug previews to CMYK size"); + } + + [Fact] + public void Defaults_ReleaseResolution_YieldsCmyk() + { + var cardSet = new DocumentCardSet(); + cardSet.ConvertToCmykRelease.Should().BeTrue( + "Release builds enable CMYK (printer quality) — the documented default"); + + cardSet.GetConvertToCmyk(ForcedRelease()).Should().BeTrue( + "in Release-params resolution the resolver returns ConvertToCmykRelease, which " + + "defaults to true (CMYK); a regression here would ship RGB print output"); + } + + // ───────────────────────────────────────────────────────────────────────────── + // (2) The resolver is a PURE PASSTHROUGH — it forwards the per-mode field verbatim, + // not a hardcoded color-space decision. Custom (even inverted) values are respected + // per mode. Catches a regression that hardcodes the result instead of reading the pair. + // ───────────────────────────────────────────────────────────────────────────── + + [Fact] + public void Resolver_ForwardsCustomInvertedValues_PerMode() + { + var cardSet = new DocumentCardSet + { + ConvertToCmykDebug = true, // inverted: Debug wants CMYK + ConvertToCmykRelease = false // inverted: Release wants RGB + }; + + cardSet.GetConvertToCmyk(ForcedDebug()).Should().BeTrue( + "the resolver must forward ConvertToCmykDebug verbatim in Debug mode, even when " + + "custom-inverted — it is a passthrough, not a hardcoded RGB decision"); + cardSet.GetConvertToCmyk(ForcedRelease()).Should().BeFalse( + "the resolver must forward ConvertToCmykRelease verbatim in Release mode, even when " + + "custom-inverted — it is a passthrough, not a hardcoded CMYK decision"); + } + + // ───────────────────────────────────────────────────────────────────────────── + // (3) The legacy field is DECOUPLED from the + // resolver. GetConvertToCmyk keys only on the Debug/Release pair — setting the legacy + // field has no effect on the resolved color space. This pins the decoupling so a future + // change cannot silently reintroduce the legacy field into the resolution path. + // ───────────────────────────────────────────────────────────────────────────── + + [Fact] + public void Legacy_ConvertToCmyk_Field_DoesNotAffectResolver() + { + // Legacy field says "convert", but the per-mode pair says "don't" — the resolver must + // follow the pair and ignore the legacy field. + var cardSet = new DocumentCardSet + { + ConvertToCmyk = true, // legacy — must be ignored + ConvertToCmykDebug = false, + ConvertToCmykRelease = false + }; + + cardSet.GetConvertToCmyk(ForcedDebug()).Should().BeFalse( + "the legacy ConvertToCmyk field must not feed the resolver; only ConvertToCmykDebug " + + "governs Debug resolution"); + cardSet.GetConvertToCmyk(ForcedRelease()).Should().BeFalse( + "the legacy ConvertToCmyk field must not feed the resolver; only ConvertToCmykRelease " + + "governs Release resolution"); + } + + // ───────────────────────────────────────────────────────────────────────────── + // (4) OVERRIDE PRIORITY — the documented "ForceReleaseParams = true to use Release params + // in Debug builds". With BOTH force flags set, Release wins because UseDebugParams is + // gated by `&& !ForceReleaseParams`. This pins the override priority so a + // forced Release run really yields the Release color space even in a Debug build. + // ───────────────────────────────────────────────────────────────────────────── + + [Fact] + public void ForceReleaseParams_OverridesForceDebugParams_YieldsReleaseValue() + { + var cardSet = new DocumentCardSet + { + ConvertToCmykDebug = false, + ConvertToCmykRelease = true + }; + var bothForced = new AssetConverterConfig + { + ForceDebugParams = true, + ForceReleaseParams = true + }; + + // ForceReleaseParams dominates: UseDebugParams = (… || ForceDebugParams) && !ForceReleaseParams = false. + cardSet.GetConvertToCmyk(bothForced).Should().BeTrue( + "ForceReleaseParams must win over ForceDebugParams (UseDebugParams is gated by " + + "'&& !ForceReleaseParams'), so a forced Release run yields the Release CMYK value " + + "even in a Debug build — the documented override"); + } + } +}