import { describe, expect, it } from "vitest"; import { advanceState, applyAction, createTableState, type PokerTableState, } from "@/lib/minigames/poker"; import type { Card } from "@/lib/minigames/poker"; import { adjustStack, createTable, getSnapshotFor, joinTable, setBoardQueue, setHoleQueue, startGame, } from "@/lib/minigames/poker/tables"; function card(rank: Card["rank"], suit: Card["suit"]): Card { return { rank, suit }; } function withQueues( state: PokerTableState, queues: Partial>, ): PokerTableState { return { ...state, ...queues }; } /** Apply an action and unwrap the result; throws on illegal actions. */ function act( state: PokerTableState, seatId: number, action: Parameters[2], amount?: number, ): PokerTableState { const result = applyAction(state, seatId, action, amount); if (!result.success) throw new Error(result.error); return result.state; } describe("dev board queue", () => { it("deals the queued flop and turn cards in order before falling back to the deck", () => { let state = createTableState([{ name: "A" }, { name: "B" }], {}, () => 0.5); const queued = [card(14, "s"), card(13, "s"), card(12, "s"), card(11, "s"), card(10, "s")]; state = withQueues(state, { queuedBoard: queued }); // Preflop: the small blind calls to the big blind, the big blind checks, // which completes the round and deals the flop from the queue. state = act(state, 0, "call", undefined); state = act(state, 1, "check", undefined); expect(state.communityCards).toEqual([card(14, "s"), card(13, "s"), card(12, "s")]); expect(state.queuedBoard).toEqual([card(11, "s"), card(10, "s")]); // Flop: both check, dealing the turn from the queue. state = act(state, 1, "check", undefined); state = act(state, 0, "check", undefined); expect(state.communityCards[3]).toEqual(card(11, "s")); expect(state.queuedBoard).toEqual([card(10, "s")]); }); it("consumes queued cards inside runOutAndShowdown when betting is impossible", () => { let state = createTableState( [ { name: "A", stack: 50 }, { name: "B", stack: 50 }, ], {}, () => 0.5, ); state = withQueues(state, { queuedBoard: [card(5, "h"), card(6, "h"), card(7, "h"), card(8, "h"), card(9, "h")], }); // Two quick all-ins leave nobody able to act: the board runs out. state = act(state, 0, "raise", 50); state = act(state, 1, "call", undefined); expect(state.communityCards).toEqual([ card(5, "h"), card(6, "h"), card(7, "h"), card(8, "h"), card(9, "h"), ]); expect(state.queuedBoard).toEqual([]); }); }); describe("dev hole queues", () => { it("replaces the next hand's hole cards for queued seats only", () => { let state = createTableState([{ name: "A" }, { name: "B" }, { name: "C" }], {}, () => 0.5); // Queue while hand 1 is live, then fold it out so hand 2 deals fresh. state = withQueues(state, { queuedHoles: { 0: [card(14, "s"), card(14, "h")], 1: [card(2, "c"), card(3, "c")], }, }); state = act(state, 0, "fold", undefined); state = act(state, 1, "fold", undefined); const dealt = advanceState(state); expect(dealt.players[0].holeCards).toEqual([card(14, "s"), card(14, "h")]); expect(dealt.players[1].holeCards).toEqual([card(2, "c"), card(3, "c")]); expect(dealt.players[2].holeCards.length).toBe(2); expect(dealt.queuedHoles).toEqual({}); }); }); describe("dev stack adjustment", () => { it("adds and subtracts chips, clamped at zero", () => { const tableId = createTable(9601, "Host"); joinTable(tableId, 9602, "Guest"); startGame(tableId, 9601); let snapshot = getSnapshotFor(tableId, 9601); const hostSeat = snapshot.you?.viewerSeat ?? 0; const before = snapshot.you?.players.find((p) => p.seatId === hostSeat)?.stack ?? 0; adjustStack(tableId, 9601, hostSeat, 250); snapshot = getSnapshotFor(tableId, 9601); expect(snapshot.you?.players.find((p) => p.seatId === hostSeat)?.stack).toBe(before + 250); adjustStack(tableId, 9601, hostSeat, -(before + 250) - 500); snapshot = getSnapshotFor(tableId, 9601); expect(snapshot.you?.players.find((p) => p.seatId === hostSeat)?.stack).toBe(0); }); it("rejects invalid seats and oversized queues", () => { const tableId = createTable(9611, "Host"); joinTable(tableId, 9612, "Guest"); startGame(tableId, 9611); expect(() => setBoardQueue(tableId, 9611, [ card(2, "s"), card(3, "s"), card(4, "s"), card(5, "s"), card(6, "s"), card(7, "s"), ]), ).toThrow(); expect(() => setHoleQueue(tableId, 9611, 9, [card(2, "s"), card(3, "s")])).toThrow(); expect(() => adjustStack(tableId, 9611, 9, 100)).toThrow(); }); });