GEN-2: expand Farbschlag catalog 18->73 (baseportal.de) + images + collision-aware tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 00:55:35 +02:00
parent 263ba32e3c
commit a9778ad1d2
81 changed files with 141 additions and 44 deletions

View File

@@ -17,6 +17,7 @@ import {
wildType,
} from '../genotype'
import { combineLocus } from '../punnett'
import { LOCI, type LocusKey } from '../loci'
import {
farbschlagFor,
genotypeToFarbschlag,
@@ -25,6 +26,9 @@ import {
CATALOG,
CATALOG_SIZE,
} from '../catalog'
/** The first 18 entries are the frozen contract names (must round-trip exactly). */
const FROZEN_COUNT = 18
import { breed } from '../breed'
import { GeneticsWarningCode } from '../warnings'
@@ -173,18 +177,38 @@ describe('Farbschlag catalog', () => {
expect(match.name).toBe('Unbekannter Farbschlag')
})
it('has the expected catalogue coverage', () => {
expect(CATALOG_SIZE).toBe(18)
it('has the expected catalogue coverage (GEN-2: frozen 18 + 55 portal varieties)', () => {
expect(CATALOG_SIZE).toBe(73)
})
it('every catalog entry round-trips: its representative genotype maps back to its own name', () => {
// Guards the DB seed (DATA-2): a representative genotype that resolved to a
// DIFFERENT (earlier) variety would mean overlapping/mis-ordered patterns.
for (const entry of BASE_COLORS) {
it('frozen contract names round-trip to themselves (DB-key guard)', () => {
// The first 18 are the frozen ColorVariety keys — their representative
// genotype MUST resolve back to their own name, never a later variety.
for (const entry of BASE_COLORS.slice(0, FROZEN_COUNT)) {
expect(genotypeToFarbschlag(representativeGenotype(entry))).toBe(entry.name)
}
})
it('every entry resolves to a named variety (no self-Unbekannt)', () => {
// Each catalogued variety's representative genotype must resolve to SOME
// named variety — never "Unbekannt". Genotype-identical synonyms (e.g.
// "-hell") or entries that omit a locus may resolve to an earlier primary;
// that's expected first-match behaviour, not an error.
for (const entry of BASE_COLORS) {
expect(farbschlagFor(representativeGenotype(entry)).unknown).toBe(false)
}
})
it('has no duplicate variety names and only valid allele tokens', () => {
const names = BASE_COLORS.map((e) => e.name)
expect(new Set(names).size).toBe(names.length)
for (const entry of BASE_COLORS) {
for (const [locus, value] of Object.entries(entry.tokens)) {
expect(LOCI[locus as LocusKey].alleles).toContain(value)
}
}
})
it('CATALOG seed view mirrors the ColorVariety table shape', () => {
expect(CATALOG).toHaveLength(CATALOG_SIZE)
expect(CATALOG[0]).toMatchObject({ name: 'Pink Eyed White (PEW)', sortOrder: 0 })