Leavers now flip a left flag on the very next snapshot, the felt announces who left, and a game left with a single seated player resolves at once with a bell notification instead of waiting out the hand timer. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
101 lines
3.5 KiB
TypeScript
101 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
createTable,
|
|
getSnapshotFor,
|
|
joinTable,
|
|
leaveTable,
|
|
startGame,
|
|
} from "@/lib/minigames/poker/tables";
|
|
|
|
/**
|
|
* Regression tests for the registry's game-start path. Guards against the
|
|
* seat-map aliasing bug that once spawned phantom "Player null" engine seats
|
|
* when a table started with fewer than four players.
|
|
*/
|
|
|
|
describe("poker table registry: startGame", () => {
|
|
it("starts a heads-up game with exactly the two seated players", () => {
|
|
const tableId = createTable(9001, "Host");
|
|
joinTable(tableId, 9002, "Guest");
|
|
|
|
const snapshot = startGame(tableId, 9001);
|
|
|
|
const seatedIds = snapshot.seats.filter((id) => id !== null);
|
|
expect(seatedIds).toHaveLength(2);
|
|
const players = snapshot.you?.players ?? [];
|
|
expect(players).toHaveLength(2);
|
|
for (const player of players) {
|
|
expect(player.name).not.toBe("Player null");
|
|
expect(player.name.length).toBeGreaterThan(0);
|
|
expect(player.eliminated).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("starts a three-handed game with exactly the three seated players", () => {
|
|
const tableId = createTable(9101, "Host");
|
|
joinTable(tableId, 9102, "Second");
|
|
joinTable(tableId, 9103, "Third");
|
|
|
|
const snapshot = startGame(tableId, 9101);
|
|
|
|
const seatedIds = snapshot.seats.filter((id) => id !== null);
|
|
expect(seatedIds).toHaveLength(3);
|
|
const players = snapshot.you?.players ?? [];
|
|
expect(players).toHaveLength(3);
|
|
const names = players.map((player) => player.name);
|
|
expect(names).not.toContain("Player null");
|
|
expect(new Set(names).size).toBe(3);
|
|
});
|
|
|
|
it("keeps every engine player backed by a real seated user", () => {
|
|
const tableId = createTable(9201, "Host");
|
|
joinTable(tableId, 9202, "Second");
|
|
|
|
const snapshot = startGame(tableId, 9201);
|
|
const players = snapshot.you?.players ?? [];
|
|
|
|
for (const player of players) {
|
|
const userId = snapshot.seats[player.seatId];
|
|
expect(userId).not.toBeNull();
|
|
expect(typeof userId).toBe("number");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("poker table registry: last player standing", () => {
|
|
it("ends the game immediately when everyone else leaves mid-hand", () => {
|
|
const tableId = createTable(9301, "Host");
|
|
joinTable(tableId, 9302, "Guest");
|
|
startGame(tableId, 9301);
|
|
|
|
leaveTable(tableId, 9302);
|
|
|
|
const remaining = getSnapshotFor(tableId, 9301);
|
|
expect(remaining.you?.phase).toBe("game-complete");
|
|
const players = remaining.you?.players ?? [];
|
|
const hostPlayer = players.find((player) => player.seatId === remaining.you?.viewerSeat);
|
|
const guestPlayer = players.find((player) => player.seatId !== remaining.you?.viewerSeat);
|
|
expect(hostPlayer?.eliminated).toBe(false);
|
|
expect(hostPlayer?.left).toBe(false);
|
|
// The leaver's committed blind is forfeited (leavers take nothing from
|
|
// the pot), so the host only receives their own small blind back.
|
|
expect(hostPlayer?.stack).toBe(1000);
|
|
expect(guestPlayer?.eliminated).toBe(true);
|
|
expect(guestPlayer?.left).toBe(true);
|
|
});
|
|
|
|
it("does not end the game while two or more players remain seated", () => {
|
|
const tableId = createTable(9401, "Host");
|
|
joinTable(tableId, 9402, "Second");
|
|
joinTable(tableId, 9403, "Third");
|
|
startGame(tableId, 9401);
|
|
|
|
leaveTable(tableId, 9403);
|
|
|
|
const snapshot = getSnapshotFor(tableId, 9401);
|
|
expect(snapshot.you?.phase).not.toBe("game-complete");
|
|
const third = getSnapshotFor(tableId, 9403);
|
|
expect(third.you).toBeNull();
|
|
});
|
|
});
|