Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
169 lines
4.9 KiB
TypeScript
169 lines
4.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
computePokerStatsDelta,
|
|
readPokerStats,
|
|
xpForPlacement,
|
|
type PokerPlacementResult,
|
|
type PokerStats,
|
|
} from "@/lib/minigames/poker/stats";
|
|
|
|
function result(overrides: Partial<PokerPlacementResult> = {}): PokerPlacementResult {
|
|
return { userId: 1, placement: 1, finalChips: 5000, handsWon: 3, ...overrides };
|
|
}
|
|
|
|
function current(overrides: Partial<PokerStats> = {}): PokerStats {
|
|
return {
|
|
gamesPlayed: 0,
|
|
wins: 0,
|
|
secondPlaces: 0,
|
|
thirdPlaces: 0,
|
|
fourthPlaces: 0,
|
|
handsWon: 0,
|
|
bestScore: 0,
|
|
xpEarned: 0,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("xpForPlacement", () => {
|
|
it("scales 1st place at 40x the player count", () => {
|
|
expect(xpForPlacement(1, 2)).toBe(80);
|
|
expect(xpForPlacement(1, 3)).toBe(120);
|
|
expect(xpForPlacement(1, 4)).toBe(160);
|
|
});
|
|
|
|
it("scales 2nd place at 20x the player count", () => {
|
|
expect(xpForPlacement(2, 2)).toBe(40);
|
|
expect(xpForPlacement(2, 3)).toBe(60);
|
|
expect(xpForPlacement(2, 4)).toBe(80);
|
|
});
|
|
|
|
it("scales 3rd place at 10x the player count", () => {
|
|
expect(xpForPlacement(3, 2)).toBe(20);
|
|
expect(xpForPlacement(3, 3)).toBe(30);
|
|
expect(xpForPlacement(3, 4)).toBe(40);
|
|
});
|
|
|
|
it("scales 4th place at 5x the player count", () => {
|
|
expect(xpForPlacement(4, 2)).toBe(10);
|
|
expect(xpForPlacement(4, 3)).toBe(15);
|
|
expect(xpForPlacement(4, 4)).toBe(20);
|
|
});
|
|
});
|
|
|
|
describe("computePokerStatsDelta", () => {
|
|
it("increments gamesPlayed by exactly one", () => {
|
|
const next = computePokerStatsDelta(current({ gamesPlayed: 7 }), result(), 4);
|
|
expect(next.gamesPlayed).toBe(8);
|
|
});
|
|
|
|
it("increments the wins counter for 1st place only", () => {
|
|
const next = computePokerStatsDelta(current({ wins: 2 }), result({ placement: 1 }), 4);
|
|
expect(next.wins).toBe(3);
|
|
expect(next.secondPlaces).toBe(0);
|
|
expect(next.thirdPlaces).toBe(0);
|
|
expect(next.fourthPlaces).toBe(0);
|
|
});
|
|
|
|
it("increments the secondPlaces counter for 2nd place only", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ secondPlaces: 1 }),
|
|
result({ placement: 2 }),
|
|
4,
|
|
);
|
|
expect(next.secondPlaces).toBe(2);
|
|
expect(next.wins).toBe(0);
|
|
expect(next.thirdPlaces).toBe(0);
|
|
expect(next.fourthPlaces).toBe(0);
|
|
});
|
|
|
|
it("increments the thirdPlaces counter for 3rd place only", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ thirdPlaces: 4 }),
|
|
result({ placement: 3 }),
|
|
4,
|
|
);
|
|
expect(next.thirdPlaces).toBe(5);
|
|
expect(next.wins).toBe(0);
|
|
expect(next.secondPlaces).toBe(0);
|
|
expect(next.fourthPlaces).toBe(0);
|
|
});
|
|
|
|
it("increments the fourthPlaces counter for 4th place only", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ fourthPlaces: 1 }),
|
|
result({ placement: 4 }),
|
|
4,
|
|
);
|
|
expect(next.fourthPlaces).toBe(2);
|
|
expect(next.wins).toBe(0);
|
|
expect(next.secondPlaces).toBe(0);
|
|
expect(next.thirdPlaces).toBe(0);
|
|
});
|
|
|
|
it("accumulates handsWon on top of the existing total", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ handsWon: 10 }),
|
|
result({ handsWon: 4 }),
|
|
4,
|
|
);
|
|
expect(next.handsWon).toBe(14);
|
|
});
|
|
|
|
it("keeps bestScore when the final chip total is lower", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ bestScore: 9000 }),
|
|
result({ finalChips: 5000 }),
|
|
4,
|
|
);
|
|
expect(next.bestScore).toBe(9000);
|
|
});
|
|
|
|
it("raises bestScore when the final chip total is higher", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ bestScore: 3000 }),
|
|
result({ finalChips: 7500 }),
|
|
4,
|
|
);
|
|
expect(next.bestScore).toBe(7500);
|
|
});
|
|
|
|
it("adds the placement XP to the existing xpEarned total", () => {
|
|
const next = computePokerStatsDelta(
|
|
current({ xpEarned: 100 }),
|
|
result({ placement: 2 }),
|
|
3,
|
|
);
|
|
expect(next.xpEarned).toBe(160);
|
|
});
|
|
|
|
it("does not mutate the current stats object", () => {
|
|
const before = current({ gamesPlayed: 1, wins: 1, xpEarned: 160 });
|
|
computePokerStatsDelta(before, result(), 4);
|
|
expect(before).toEqual(current({ gamesPlayed: 1, wins: 1, xpEarned: 160 }));
|
|
});
|
|
});
|
|
|
|
describe("readPokerStats", () => {
|
|
it("reads poker stats from a profile that also carries sibling game stats", () => {
|
|
const profile = {
|
|
progression: {
|
|
minigames: {
|
|
flappyBird: { gamesPlayed: 12, xpEarned: 40 },
|
|
poker: { gamesPlayed: 3, wins: 1, xpEarned: 200 },
|
|
},
|
|
},
|
|
};
|
|
expect(readPokerStats(profile)).toEqual(
|
|
current({ gamesPlayed: 3, wins: 1, xpEarned: 200 }),
|
|
);
|
|
});
|
|
|
|
it("returns all zeros when the poker group is missing", () => {
|
|
expect(readPokerStats({ progression: { minigames: {} } })).toEqual(current());
|
|
});
|
|
|
|
it("returns all zeros for a nullish profile", () => {
|
|
expect(readPokerStats(undefined)).toEqual(current());
|
|
});
|
|
});
|