import test from 'node:test'; import assert from 'node:assert/strict'; import { cardPresentation, richeseCardActionBlock } from '../game/card-presentation'; import type { Card } from '../game/cards'; import { RICHESE_CARD_DEFINITIONS, richeseCards } from '../game/richese-cards'; void test('presentation does not export source-verification metadata or unverified interaction guesses', () => { for (const definition of RICHESE_CARD_DEFINITIONS) { const presentation = cardPresentation(definition.card); assert.deepEqual(Object.keys(presentation).sort(), [ 'availability', 'category', 'gameplay', 'guidance', 'role', 'topics', ]); const serialized = JSON.stringify(presentation); for (const note of definition.verification.unresolved) assert.equal(serialized.includes(note), false); for (const metadata of [ 'physical-faces-verified', 'original-paraphrase-of-physical-faces', 'combinedInteractions', 'https://cdn.anyfinder', ]) assert.equal(serialized.includes(metadata), false); } }); void test('canonical Richese inspectors describe available previews and retain card-specific limits', () => { const sapho = RICHESE_CARD_DEFINITIONS.find(row => row.card.effect === 'juiceOfSapho'); const karama = RICHESE_CARD_DEFINITIONS.find(row => row.card.effect === 'karama'); assert.ok(sapho && karama); const saphoAvailability = cardPresentation(sapho.card).availability; const karamaAvailability = cardPresentation(karama.card).availability; assert.match(saphoAvailability ?? '', /supported Richese and expansion starts are available as unfinished previews/i); assert.match(saphoAvailability ?? '', /Once Around first or last/); assert.match(saphoAvailability ?? '', /both combatants finish final battle actions/); assert.match(saphoAvailability ?? '', /older battles retain their pre-plan opportunity/); const saphoAction = richeseCardActionBlock(sapho.card); assert.match(saphoAction ?? '', /both combatants finish their final battle actions/); assert.match(saphoAction ?? '', /older battles retain their pre-plan opportunity/); assert.match(karamaAvailability ?? '', /supported Richese and expansion starts are available as unfinished previews/i); assert.match(karamaAvailability ?? '', /bounded card handlers/); for (const definition of RICHESE_CARD_DEFINITIONS) assert.doesNotMatch(cardPresentation(definition.card).availability ?? '', /expansion starts remain disabled/i); }); void test('Semuta inspector explains the supported Basic and Advanced previews', () => { const semuta = RICHESE_CARD_DEFINITIONS.find(row => row.card.effect === 'semutaDrug'); assert.ok(semuta); const guide = richeseCardActionBlock(semuta.card) ?? ''; assert.match(guide, /fresh Basic Richese games/); assert.match(guide, /Advanced games with Richese, the CHOAM deck and classic or CHOAM opponents/); assert.match(guide, /some discard reactions/); assert.match(guide, /Older games and other module previews keep their existing scope/); assert.match(guide, /Commit before inspecting private discarded faces/); }); void test('missing, altered and merely similar Richese identities receive no canonical full guide', () => { for (const card of richeseCards()) { const impostors: Card[] = [ { ...card, id: 'other-card' }, { ...card, name: 'Other name' }, { ...card, kind: 'poison' }, { ...card, effect: 'unknown-effect' }, { ...card, effect: undefined }, { ...card, id: card.id.toUpperCase() }, ]; for (const impostor of impostors) { const result = cardPresentation(impostor); assert.equal(result.gameplay, undefined); assert.equal(result.availability, undefined); assert.equal( result.topics.some((topic) => topic.id === 'richese-cards'), false, ); } } });