68 lines
3.1 KiB
TypeScript
68 lines
3.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { adoptCastIdentity, buildRegularCast, REGULAR_CAST_SIZE } from '../src/patrons/regularCast';
|
|
import { disguisedFromMemory, recordDenialBy } from '../src/patrons/memory';
|
|
import { freshGameState } from '../src/core/save';
|
|
import { dollPlan } from '../src/patrons/dollPlan';
|
|
import { SeededRNG } from '../src/core/SeededRNG';
|
|
import { generatePatron, _resetPatronSerial } from '../src/patrons/generator';
|
|
|
|
const NIGHT = new Date('2026-07-16T00:00:00');
|
|
|
|
describe('the regular cast (design §4.1)', () => {
|
|
it('is stable per run seed — same six faces, every night, every venue', () => {
|
|
const a = buildRegularCast(4207, NIGHT);
|
|
const b = buildRegularCast(4207, NIGHT);
|
|
expect(a).toHaveLength(REGULAR_CAST_SIZE);
|
|
expect(a.map((p) => p.dollSeed)).toEqual(b.map((p) => p.dollSeed));
|
|
expect(a.map((p) => p.idCard.name)).toEqual(b.map((p) => p.idCard.name));
|
|
expect(new Set(a.map((p) => p.flags.regularId)).size).toBe(REGULAR_CAST_SIZE);
|
|
});
|
|
|
|
it('different runs meet different regulars', () => {
|
|
const a = buildRegularCast(1, NIGHT).map((p) => p.dollSeed);
|
|
const b = buildRegularCast(2, NIGHT).map((p) => p.dollSeed);
|
|
expect(a).not.toEqual(b);
|
|
});
|
|
|
|
it('cast members carry real, boring licences — regulars are not ID puzzles', () => {
|
|
for (const p of buildRegularCast(4207, NIGHT)) {
|
|
expect(p.idCard.fake).toBeUndefined();
|
|
expect(p.age).toBeGreaterThanOrEqual(18);
|
|
}
|
|
});
|
|
|
|
it('adoption keeps tonight\'s roll but wears the cast identity', () => {
|
|
_resetPatronSerial();
|
|
const rolled = generatePatron({ rng: new SeededRNG(9), nightDate: NIGHT }, 120, 'regular');
|
|
const member = buildRegularCast(4207, NIGHT)[0]!;
|
|
const adopted = adoptCastIdentity(rolled, member, 1, false);
|
|
expect(adopted.dollSeed).toBe(member.dollSeed);
|
|
expect(adopted.idCard.name).toBe(member.idCard.name);
|
|
expect(adopted.outfit).toBe(rolled.outfit); // tonight's clothes
|
|
expect(adopted.intoxication).toBe(rolled.intoxication); // tonight's drinks
|
|
expect(adopted.flags.regularId).toBe(member.flags.regularId);
|
|
expect(adopted.flags.timesDeniedBefore).toBe(1);
|
|
expect(adopted.flags.disguised).toBeUndefined();
|
|
});
|
|
|
|
it('two denials on the run save → the third appearance is disguised', () => {
|
|
const run = freshGameState(4207);
|
|
recordDenialBy(run, 'reg2', 0);
|
|
expect(disguisedFromMemory(run.regulars['reg2'])).toBe(false);
|
|
recordDenialBy(run, 'reg2', 1);
|
|
expect(disguisedFromMemory(run.regulars['reg2'])).toBe(true);
|
|
expect(run.regulars['reg2']!.timesDenied).toBe(2);
|
|
});
|
|
|
|
it('the disguise is on the man, never on his card photo', () => {
|
|
const member = buildRegularCast(4207, NIGHT)[3]!;
|
|
const plain = { ...member, flags: { ...member.flags } };
|
|
const disguised = { ...member, flags: { ...member.flags, disguised: true } };
|
|
expect(dollPlan(disguised, 'queue').rects.length).toBeGreaterThan(
|
|
dollPlan(plain, 'queue').rects.length,
|
|
);
|
|
// The idPhoto stays clean-shaven: the card shows Thursday's bloke.
|
|
expect(dollPlan(disguised, 'idPhoto')).toEqual(dollPlan(plain, 'idPhoto'));
|
|
});
|
|
});
|