const { test, describe } = require("node:test"); const assert = require("node:assert/strict"); const { HoldemState, makeTable } = require("./load"); describe("HoldemState.advanceButton", () => { test("the button moves to the next player, not the next empty chair", () => { // Four players spread around a ten-seat table: 0, 3, 4, 8. const state = makeTable({ seatCount: 10, seated: [0, 3, 4, 8], dealerSeatIndex: 0 }); HoldemState.advanceButton(state); assert.equal(state.dealerSeatIndex, 3, "skips the three empty chairs between"); HoldemState.advanceButton(state); assert.equal(state.dealerSeatIndex, 4); HoldemState.advanceButton(state); assert.equal(state.dealerSeatIndex, 8); HoldemState.advanceButton(state); assert.equal(state.dealerSeatIndex, 0, "wraps back round to the first player"); }); test("every player gets the button once per orbit", () => { const seated = [1, 2, 6, 7, 9]; const state = makeTable({ seatCount: 12, seated, dealerSeatIndex: 1 }); const visited = []; for (let i = 0; i < seated.length; i++) { visited.push(state.dealerSeatIndex); HoldemState.advanceButton(state); } assert.deepEqual(visited.slice().sort((a, b) => a - b), seated); }); test("it still moves when nobody is dealt in, rather than looking stuck", () => { const state = makeTable({ seatCount: 6, seated: [], dealerSeatIndex: 2 }); HoldemState.advanceButton(state); assert.equal(state.dealerSeatIndex, 3); }); test("counterclockwise play runs the button the other way", () => { const state = makeTable({ seatCount: 6, seated: [0, 2, 4], dealerSeatIndex: 4 }); state.direction = "counterclockwise"; HoldemState.advanceButton(state); assert.equal(state.dealerSeatIndex, 2); }); }); describe("HoldemState.getBlindSeats", () => { test("blinds follow the players, not the chairs left of the button", () => { // Seats 1 and 2 are empty, so the blinds belong on 3 and 7. const state = makeTable({ seatCount: 9, seated: [0, 3, 7], dealerSeatIndex: 0 }); const { sbIndex, bbIndex, headsUp } = HoldemState.getBlindSeats(state); assert.equal(headsUp, false); assert.equal(sbIndex, 3); assert.equal(bbIndex, 7); }); test("heads-up, the button posts the small blind", () => { const state = makeTable({ seatCount: 6, seated: [2, 5], dealerSeatIndex: 2 }); const { sbIndex, bbIndex, headsUp } = HoldemState.getBlindSeats(state); assert.equal(headsUp, true); assert.equal(sbIndex, 2, "the button is the small blind heads-up"); assert.equal(bbIndex, 5); }); test("heads-up with the button on an empty chair still posts both blinds", () => { const state = makeTable({ seatCount: 6, seated: [1, 4], dealerSeatIndex: 0 }); const { sbIndex, bbIndex, headsUp } = HoldemState.getBlindSeats(state); assert.equal(headsUp, true); assert.equal(sbIndex, 1); assert.equal(bbIndex, 4); assert.notEqual(sbIndex, bbIndex, "two blinds never land on one player"); }); test("fewer than two players means no blinds to post", () => { const state = makeTable({ seatCount: 6, seated: [3], dealerSeatIndex: 0 }); const { sbIndex, bbIndex } = HoldemState.getBlindSeats(state); assert.equal(sbIndex, null); assert.equal(bbIndex, null); }); }); describe("HoldemState.sessionStandings", () => { test("a player's cash position counts chips in front plus chips in the pot", () => { const state = makeTable({ seatCount: 3, seated: [0, 1, 2], stack: 0 }); const [a] = state.seats; a.buyIns = 100; a.stack = 60; a.amountOverride = 25; // banked into the hand in progress a.committed = 15; // still in front of them on this street HoldemState.recordHand(state, [{ id: a.id, label: "P1", amount: 0 }]); const standings = HoldemState.sessionStandings(state); const row = standings.find(s => s.id === a.id); // 60 in front + 40 in the pot - 100 bought in = level. assert.equal(row.cashPosition, 0, "money in the pot is still theirs until it's won"); assert.equal(row.boughtIn, 100); assert.equal(row.chipsNow, 60); }); test("hand results accumulate across the session", () => { const state = makeTable({ seatCount: 2, seated: [0, 1], stack: 0 }); const [a, b] = state.seats; a.amountOverride = 10; b.amountOverride = 10; HoldemState.recordHand(state, [{ id: a.id, label: "P1", amount: 20 }]); a.amountOverride = 5; b.amountOverride = 5; HoldemState.recordHand(state, [{ id: b.id, label: "P2", amount: 10 }]); const standings = HoldemState.sessionStandings(state); assert.equal(standings.find(s => s.id === a.id).net, 5); assert.equal(standings.find(s => s.id === b.id).net, -5); assert.equal(state.handNumber, 3, "the counter moved on twice"); }); test("the history is capped so a long session can still be saved", () => { const state = makeTable({ seatCount: 2, seated: [0, 1], stack: 0 }); for (let i = 0; i < HoldemState.MAX_HISTORY_ENTRIES + 25; i++) { HoldemState.recordHand(state, [{ id: state.seats[0].id, label: "P1", amount: 1 }]); } assert.equal(state.handHistory.length, HoldemState.MAX_HISTORY_ENTRIES); assert.equal(state.handHistory[0].handNumber, HoldemState.MAX_HISTORY_ENTRIES + 25, "newest first"); }); }); describe("HoldemState.resizeSeats", () => { test("the table is clamped to a size a dealer could actually run", () => { const state = makeTable({ seatCount: 6 }); HoldemState.resizeSeats(state, 100); assert.equal(state.seatCount, 24); HoldemState.resizeSeats(state, 1); assert.equal(state.seatCount, 2); }); test("an unreadable seat count falls back to a standard table", () => { // Empty or non-numeric input (and zero, which is equally meaningless) // lands on the default rather than a two-seat table. const state = makeTable({ seatCount: 6 }); HoldemState.resizeSeats(state, NaN); assert.equal(state.seatCount, HoldemState.DEFAULT_SEAT_COUNT); }); test("shrinking past the button brings it back onto the table", () => { const state = makeTable({ seatCount: 9, dealerSeatIndex: 8 }); HoldemState.resizeSeats(state, 4); assert.ok(state.dealerSeatIndex < 4); }); });