73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { SeededRNG } from '../src/core/SeededRNG';
|
|
import { dollPlan, DOLL_SIZES } from '../src/patrons/dollPlan';
|
|
import { generatePatron, _resetPatronSerial } from '../src/patrons/generator';
|
|
import type { Patron } from '../src/data/types';
|
|
|
|
const NIGHT = new Date('2026-07-18T00:00:00');
|
|
const somePatrons = (seed: number, n: number): Patron[] => {
|
|
_resetPatronSerial();
|
|
const ctx = { rng: new SeededRNG(seed), nightDate: NIGHT };
|
|
return Array.from({ length: n }, () => generatePatron(ctx, 0));
|
|
};
|
|
|
|
describe('dollPlan', () => {
|
|
it('is deterministic per (patron, pose)', () => {
|
|
for (const p of somePatrons(5, 20)) {
|
|
expect(dollPlan(p, 'queue')).toEqual(dollPlan(p, 'queue'));
|
|
expect(dollPlan(p, 'idPhoto')).toEqual(dollPlan(p, 'idPhoto'));
|
|
expect(dollPlan(p, 'floorTopDown')).toEqual(dollPlan(p, 'floorTopDown'));
|
|
}
|
|
});
|
|
|
|
it('respects the pose size contract and stays in bounds', () => {
|
|
for (const p of somePatrons(9, 30)) {
|
|
for (const pose of ['queue', 'idPhoto', 'floorTopDown'] as const) {
|
|
const plan = dollPlan(p, pose);
|
|
expect(plan.width).toBe(DOLL_SIZES[pose].width);
|
|
expect(plan.height).toBe(DOLL_SIZES[pose].height);
|
|
for (const r of plan.rects) {
|
|
expect(r.x).toBeGreaterThanOrEqual(-2); // jacket edges may hug the border
|
|
expect(r.x + r.w).toBeLessThanOrEqual(plan.width + 2);
|
|
expect(r.y).toBeGreaterThanOrEqual(0);
|
|
expect(r.y + r.h).toBeLessThanOrEqual(plan.height);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('drunk stages produce sway + bloodshot tells; sober does not', () => {
|
|
const [p] = somePatrons(3, 1);
|
|
const sober = dollPlan({ ...p!, intoxication: 0.1 }, 'queue');
|
|
const maggot = dollPlan({ ...p!, intoxication: 0.95 }, 'queue');
|
|
expect(sober.swayPx).toBe(0);
|
|
expect(maggot.swayPx).toBe(3);
|
|
expect(maggot.rects.length).toBeGreaterThan(sober.rects.length); // eye pixels
|
|
});
|
|
|
|
it('photoMismatch renders the ID photo from the WRONG seed (visible tell)', () => {
|
|
const [p] = somePatrons(21, 1);
|
|
const honest: Patron = { ...p!, idCard: { ...p!.idCard, photoSeed: p!.dollSeed, fake: undefined } };
|
|
const mismatched: Patron = {
|
|
...p!,
|
|
idCard: { ...p!.idCard, photoSeed: p!.dollSeed + 7777, fake: { photoMismatch: true } },
|
|
};
|
|
expect(dollPlan(mismatched, 'idPhoto')).not.toEqual(dollPlan(honest, 'idPhoto'));
|
|
// and the patron themselves renders identically — only the card lies
|
|
expect(dollPlan(mismatched, 'queue')).toEqual(dollPlan(honest, 'queue'));
|
|
});
|
|
|
|
it('outfit data drives visible pixels: white vs black sneakers differ', () => {
|
|
const [p] = somePatrons(33, 1);
|
|
const white: Patron = {
|
|
...p!,
|
|
outfit: p!.outfit.map((l) => (l.slot === 'shoes' ? { ...l, type: 'sneaker', colour: 'white' } : l)),
|
|
};
|
|
const black: Patron = {
|
|
...p!,
|
|
outfit: p!.outfit.map((l) => (l.slot === 'shoes' ? { ...l, type: 'sneaker', colour: 'black' } : l)),
|
|
};
|
|
expect(dollPlan(white, 'queue')).not.toEqual(dollPlan(black, 'queue'));
|
|
});
|
|
});
|