test(pdf): #204 extract Golden Master face->back matching to pure method + 8 tests - #523
Merged
Merged
Conversation
…hod + 8 tests Extract the face->back name-matching contract (Golden Master, 0087f0e) from AssembleCurrentCardImages (ImageFileGenerator.cs) into a pure, deterministic ResolveCardBack method and pin it with 8 unit tests. The contract decides which back art pairs behind a face when a card set ships several distinct backs: the back whose (lower-cased) key is contained in the (lower-cased) face key wins, with the LONGEST matching key taken first (tie-break), single-back sets share one back, no-back sets ship faces alone, and a non-matching face falls back to the first available back. A regression here silently pairs the WRONG back behind a face while leaving page count, geometry, and ordering correct. Extraction is output-neutral: the call site assembles the exact same CardImages Front/Back pair as before (the Logger warnings and targetList.Add stay at the call site, surfaced via out flags). Key subtlety pinned by the tests: back dictionary keys carry a leading hyphen (GenerateBacks strips to the suffix AFTER the last hyphen, keeping the hyphen: "scenarii-01-histoire" -> "-histoire"), so the Contains match looks for "-histoire", not the bare token "histoire". This was not obvious from a read of AssembleCurrentCardImages alone. This is the conceptual complement of #521 (PdfAlternateFaceAndBack): that contract ORDERS fronts/backs back-then-front; this one CHOOSES which back each face gets. Suite: 359 passed / 0 failed / 5 skipped (baseline 351 + 8 new). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
|
[NanoClaw] Good extraction: inlines the face->back name-matching logic from AssembleCurrentCardImages into a pure, deterministic ResolveCardBack method (output-neutral refactoring). 8 xUnit tests using FluentAssertions cover all branches: no-backs, single-back, substring matching, case-insensitivity, unrelated face, longest-key-first tie-break, shorter-only match, and fallback-to-first. The test names are descriptive and the helper Backs() factory keeps fixtures readable. The tie-break behavior (longest matching key wins) is the critical regression case — correctly tested. LGTM. |
jsboige
added a commit
that referenced
this pull request
Jun 18, 2026
… method + 7 tests (#529) Extract the back-name normalization rule (Golden Master, 0087f0e) from GenerateBacks (ImageFileGenerator.cs) into a pure, deterministic NormalizeBackKey method and pin it with 7 unit tests. The rule: given an already-lower-cased back key, if it contains a hyphen, strip everything BEFORE the last hyphen, keeping that hyphen as a leading prefix (e.g. "scenarii-01-histoire" -> "-histoire"); otherwise return it unchanged. The resulting key indexes the back-images dictionary and is what ResolveCardBack later substring-matches against face keys. This is the OTHER half of the face->back contract pinned in #523 (ResolveCardBack): that contract chooses which back each face gets; this one produces the keys matching runs against. A regression here (strip at the first hyphen, or drop the leading hyphen) silently realigns every back key so ResolveCardBack falls through to the first-back fallback for every multi-back card set -- wrong back behind every face, with correct count/geometry/ordering. Extraction is output-neutral: the image is still loaded/processed with the FULL lower-cased name; only the dictionary key is normalized. The leading hyphen is part of the matching contract (a face must contain "-histoire", not the bare "histoire") -- locked in by the #523 tests and reaffirmed here. Suite: 366 passed / 0 failed / 5 skipped (baseline 359 + 7 new). Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This was referenced Jun 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extract the Golden Master (commit
0087f0ec) face→back name-matching contract fromAssembleCurrentCardImages(ImageFileGenerator.cs) into a pure, deterministicResolveCardBackmethod and pin it with 8 unit tests.Lane #204 (test/contract extractions, cont. po-2024) — same spirit/output-neutral pattern as #512 (page-grid geometry) and #521 (alternate-face-and-back).
The contract
When a card set ships several distinct back arts, the harvest must pair EACH face with the correct back by name, not randomly.
ResolveCardBackencodes:nullback).OrderByDescending(Length)).Why it's a fragile contract
A regression here silently pairs the WRONG back art behind a face while leaving page count, geometry, and card ordering all correct — the defect surfaces only by inspecting which back sits behind which printed card. Previously inlined inside
AssembleCurrentCardImageswith zero unit coverage.Output-neutral extraction
The call site assembles the exact same
CardImagesFront/Back pair as before. TheLogger.LogWarningdiagnostics andtargetList.Addstay at the call site;ResolveCardBacksurfaces the branch taken viaout bool hadNoAvailableBack/out bool usedFallbackso the caller emits its existing warnings without duplicating the branch logic. Verified by build + full suite.Subtlety pinned by the tests
Back dictionary keys carry a leading hyphen:
GenerateBacks(lines 124-130) strips to the suffix after the last hyphen, keeping the hyphen — e.g. harvested"scenarii-01-histoire"→ key"-histoire". So theContainsmatch looks for"-histoire", not the bare token"histoire". This was not obvious from a read ofAssembleCurrentCardImagesalone; the tests now lock it in (e.g. a face"Face_histoire_titre"with barehistoirewould not match a"-histoire"key).Conceptual complement of #521
PdfAlternateFaceAndBackContractTests,OrderImagesForAlternateFaceAndBack) — ORDERS fronts/backs back-then-front for recto-verso alignment.FaceToBackMatchingContractTests,ResolveCardBack) — CHOOSES which back each face gets.Together they pin the full back contract: which back, then where it prints.
Tests (8)
NoBacks_ReturnsNull_AndFlagsNoAvailableBack,SingleBack_IsSharedByEveryFaceMultipleBacks_PicksBackWhoseKeyIsContainedInFaceKey,NameMatching_IsCaseInsensitive,MultipleBacks_PicksUnrelatedBackForUnrelatedFaceLongestMatchingKeyWins_WhenMultipleKeysMatch,ShorterOnlyMatch_StillPicked_WhenNoLongerMatchNoNameMatch_FallsBackToFirstBack_AndFlagsFallbackCI
ImageFileGenerator.cs(extraction) +FaceToBackMatchingContractTests.cs(new tests).RowsetNb/rscountchange, no.github/workflowsor rules edit.🤖 Worker po-2024 — gate-safe, ready for ai-01 review/merge.