컴퓨터 방 자동 정리 + 게스트 계정 삭제(로그아웃·하루 미접속) + 테스트 10분→1분
- 방 화면을 벗어나면(홈·규칙) detach: 자리·접속 상태는 유지, 방 접속 수에서 빠짐 - 컴퓨터가 있는 방: 사람이 모두 [방 나가기]하면 바로 닫고, 아무도 안 보면 대기실·끝난 방 1분, 게임 중 30분 뒤 정리 - 게스트 로그아웃 = 계정 삭제(자리·전적·세션·소켓), 사이트를 닫고 24시간 지난 게스트도 1분 주기로 삭제 - users.last_seen_at을 요청·소켓 종료 때 갱신(이전엔 갱신 안 됨), 003 마이그레이션에서 기존 게스트 시계 초기화 - 테스트: 무작위 대국 시드를 playoutSeeds()로 평소 1/10, test:full은 전부. bun test --parallel=4, 테스트 서버 종료 대기 1s→50ms Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { SeededRng, randomPlayout, seedN, type Rng } from '@bg/engine';
|
||||
import { SeededRng, playoutSeeds, randomPlayout, seedN, type Rng } from '@bg/engine';
|
||||
import {
|
||||
CARD_IDS,
|
||||
GROUPS,
|
||||
@@ -1044,10 +1044,11 @@ function invariant(s: MarbleState): void {
|
||||
}
|
||||
|
||||
describe('random playouts', () => {
|
||||
test('1,000 seeds (fast loop): invariants, onTimeout legal everywhere, games end, results rank everyone', () => {
|
||||
const SEEDS = playoutSeeds(1000);
|
||||
test(`${SEEDS} seeds (fast loop): invariants, onTimeout legal everywhere, games end, results rank everyone`, () => {
|
||||
let steps = 0;
|
||||
const reasons: Record<string, number> = {};
|
||||
for (let i = 1; i <= 1000; i++) {
|
||||
for (let i = 1; i <= SEEDS; i++) {
|
||||
const cfg = CONFIGS[i % CONFIGS.length]!;
|
||||
const options = marble.optionsSchema.parse(cfg.opts);
|
||||
const players = PLAYERS.slice(0, cfg.n);
|
||||
@@ -1082,13 +1083,15 @@ describe('random playouts', () => {
|
||||
const res = marble.result(s)!;
|
||||
expect(res.ranking.flat().sort()).toEqual([...players].sort());
|
||||
}
|
||||
expect(steps).toBeGreaterThan(1000 * 50);
|
||||
// Every way of ending shows up.
|
||||
expect(Object.keys(reasons).sort()).toEqual(['lastSurvivor', 'monopoly', 'roundLimit']);
|
||||
expect(steps).toBeGreaterThan(SEEDS * 50);
|
||||
// Every way of ending shows up (monopoly only once in the full 1,000 seeds, at seed 767).
|
||||
expect(Object.keys(reasons).sort()).toEqual(SEEDS === 1000 ? ['lastSurvivor', 'monopoly', 'roundLimit'] : ['lastSurvivor', 'roundLimit']);
|
||||
}, 60_000);
|
||||
|
||||
test('engine randomPlayout (round trip, leak check, 5 ms apply), deterministic and replayable', () => {
|
||||
for (let i = 1; i <= 40; i++) {
|
||||
// Every config at least once even in the quick run (seed 8 also does the replay check).
|
||||
const games = Math.max(CONFIGS.length, playoutSeeds(40));
|
||||
for (let i = 1; i <= games; i++) {
|
||||
const cfg = CONFIGS[i % CONFIGS.length]!;
|
||||
const options = marble.optionsSchema.parse(cfg.opts);
|
||||
const players = PLAYERS.slice(0, cfg.n);
|
||||
|
||||
Reference in New Issue
Block a user