1
0
Fork 0
polaris-task-force/tests/int/blackjack-stats.int.spec.ts
Z8MB1E 474aff734d feat(blackjack): add blackjack minigame with realtime table wiring
Engine, felt, controls, sounds, card skins, and dev popups; SSE events in the realtime hook, nav and command palette entries, stats migration, and the six blackjack test suites.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-14 15:38:05 -04:00

133 lines
3.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
BLACKJACK_XP_ENABLED,
BLACKJACK_XP_CAP,
computeBlackjackStatsDelta,
readBlackjackStats,
xpForProfit,
type BlackjackSessionResult,
type BlackjackStats,
} from "@/lib/minigames/blackjack/stats";
function result(overrides: Partial<BlackjackSessionResult> = {}): BlackjackSessionResult {
return {
userId: 1,
roundsPlayed: 10,
roundsWon: 4,
blackjacksHit: 1,
finalBankroll: 1250,
...overrides,
};
}
function current(overrides: Partial<BlackjackStats> = {}): BlackjackStats {
return {
gamesPlayed: 0,
roundsWon: 0,
blackjacksHit: 0,
bestScore: 0,
xpEarned: 0,
...overrides,
};
}
describe("xpForProfit", () => {
it("awards the base XP for a breakeven or losing session", () => {
expect(xpForProfit(1000)).toBe(5);
expect(xpForProfit(500)).toBe(5);
});
it("awards no bracket bonus below a full 25% profit", () => {
expect(xpForProfit(1100)).toBe(5);
expect(xpForProfit(1249)).toBe(5);
});
it("awards 10 XP per full 25% profit bracket", () => {
expect(xpForProfit(1250)).toBe(15);
expect(xpForProfit(1500)).toBe(25);
expect(xpForProfit(1750)).toBe(35);
expect(xpForProfit(2000)).toBe(45);
});
it("caps the payout at the session cap", () => {
expect(BLACKJACK_XP_CAP).toBe(50);
expect(xpForProfit(2500)).toBe(BLACKJACK_XP_CAP);
expect(xpForProfit(100000)).toBe(BLACKJACK_XP_CAP);
});
});
describe("BLACKJACK_XP_ENABLED", () => {
it("is disabled by default until the XP system is reworked", () => {
expect(BLACKJACK_XP_ENABLED).toBe(false);
});
});
describe("computeBlackjackStatsDelta", () => {
it("increments gamesPlayed by exactly one", () => {
const next = computeBlackjackStatsDelta(current({ gamesPlayed: 7 }), result());
expect(next.gamesPlayed).toBe(8);
});
it("accumulates rounds won and blackjacks hit", () => {
const next = computeBlackjackStatsDelta(
current({ roundsWon: 10, blackjacksHit: 2 }),
result({ roundsWon: 4, blackjacksHit: 1 }),
);
expect(next.roundsWon).toBe(14);
expect(next.blackjacksHit).toBe(3);
});
it("keeps bestScore when the final bankroll is lower", () => {
const next = computeBlackjackStatsDelta(
current({ bestScore: 9000 }),
result({ finalBankroll: 1250 }),
);
expect(next.bestScore).toBe(9000);
});
it("raises bestScore when the final bankroll is higher", () => {
const next = computeBlackjackStatsDelta(
current({ bestScore: 1000 }),
result({ finalBankroll: 2400 }),
);
expect(next.bestScore).toBe(2400);
});
it("adds the profit XP to the existing xpEarned total", () => {
const next = computeBlackjackStatsDelta(
current({ xpEarned: 100 }),
result({ finalBankroll: 1500 }),
);
expect(next.xpEarned).toBe(125);
});
it("does not mutate the current stats object", () => {
const before = current({ gamesPlayed: 1, xpEarned: 125 });
computeBlackjackStatsDelta(before, result());
expect(before).toEqual(current({ gamesPlayed: 1, xpEarned: 125 }));
});
});
describe("readBlackjackStats", () => {
it("reads blackjack stats from a profile that also carries sibling games", () => {
const profile = {
progression: {
minigames: {
poker: { gamesPlayed: 12, xpEarned: 40 },
blackjack: { gamesPlayed: 3, roundsWon: 5, blackjacksHit: 2, bestScore: 1500, xpEarned: 15 },
},
},
};
expect(readBlackjackStats(profile)).toEqual(
current({ gamesPlayed: 3, roundsWon: 5, blackjacksHit: 2, bestScore: 1500, xpEarned: 15 }),
);
});
it("returns all zeros when the blackjack group is missing", () => {
expect(readBlackjackStats({ progression: { minigames: {} } })).toEqual(current());
});
it("returns all zeros for a nullish profile", () => {
expect(readBlackjackStats(undefined)).toEqual(current());
});
});