UNPKG

@idealic/poker-engine

Version:

Poker game engine and hand evaluator

148 lines (131 loc) 4.96 kB
import { describe, expect, it } from 'vitest'; import * as Poker from '../../../index'; import { BASE_HAND } from './fixtures/baseHand'; /** * Core Contract Tests for Hand API * * Purpose: Validate that Hand namespace methods follow core architectural contracts: * 1. Immutability - Never mutate input data * 2. Data Integrity - All transformations preserve information * 3. Determinism - Same inputs produce same outputs * * All tests use BASE_HAND as foundation for consistency */ describe('Hand Core Contracts', () => { describe('Immutability Contract', () => { it('should never mutate input Hand during operations', () => { const hand = Poker.Hand(BASE_HAND); const originalJson = JSON.stringify(hand); // Test constructor Poker.Hand(hand); expect(JSON.stringify(hand)).toBe(originalJson); // Test with nested modifications const modified = Poker.Hand({ ...hand, venue: 'TestVenue', players: [...hand.players, 'David'], actions: [...hand.actions, 'p1 f'], antes: [0, 0, 0, 0], startingStacks: [1000, 1000, 1000, 1000], blindsOrStraddles: [0, 10, 20, 0], }); expect(JSON.stringify(hand)).toBe(originalJson); expect(modified.venue).toBe('TestVenue'); expect(modified.players).toHaveLength(4); expect(modified.actions).toHaveLength(BASE_HAND.actions.length + 1); // Test with array modifications const withArrayMods = Poker.Hand({ ...hand, minBet: (hand.minBet as number) * 2, // Double minBet to match doubled blinds startingStacks: hand.startingStacks.map(s => s * 2), blindsOrStraddles: hand.blindsOrStraddles.map(b => b * 2), }); expect(JSON.stringify(hand)).toBe(originalJson); expect(withArrayMods.startingStacks).toEqual([2000, 2000, 2000]); expect(withArrayMods.blindsOrStraddles).toEqual([0, 20, 40]); // Original arrays should be unchanged expect(hand.startingStacks).toEqual(BASE_HAND.startingStacks); expect(hand.blindsOrStraddles).toEqual(BASE_HAND.blindsOrStraddles); // Test with private fields const withPrivates = Poker.Hand({ ...hand, _venueIds: ['id1', 'id2', 'id3'], _customField: 'test', }); expect(JSON.stringify(hand)).toBe(originalJson); expect(withPrivates._venueIds).toEqual(['id1', 'id2', 'id3']); expect(hand._venueIds).toBeUndefined(); }); }); describe('Data Integrity Contract', () => { it('should preserve all fields when constructing from partial data', () => { const partial = { variant: 'NT' as const, players: ['Alice', 'Bob'], startingStacks: [1000, 1000], blindsOrStraddles: [10, 20], minBet: 20, antes: [0, 0], }; const hand = Poker.Hand(partial); expect(hand.variant).toBe(partial.variant); expect(hand.players).toEqual(partial.players); expect(hand.startingStacks).toEqual(partial.startingStacks); expect(hand.blindsOrStraddles).toEqual(partial.blindsOrStraddles); expect(hand.minBet).toBe(partial.minBet); }); it('should maintain all original fields including private ones', () => { const handWithPrivates = { ...BASE_HAND, _venueIds: ['id1', 'id2', 'id3'], _customField: 'preserved', }; const result = Poker.Hand(handWithPrivates); expect(result._venueIds).toEqual(['id1', 'id2', 'id3']); expect(result._customField).toBe('preserved'); }); }); describe('Deterministic Behavior Contract', () => { it('should produce identical results for identical inputs', () => { const hand1 = Poker.Hand(BASE_HAND); const hand2 = Poker.Hand(BASE_HAND); expect(JSON.stringify(hand1)).toBe(JSON.stringify(hand2)); }); it('should consistently handle variant-specific fields', () => { const noLimit = Poker.Hand({ variant: 'NT', players: ['Alice', 'Bob'], startingStacks: [1000, 1000], blindsOrStraddles: [10, 20], minBet: 20, antes: [0, 0], }); const fixedLimit = Poker.Hand({ variant: 'FT', players: ['Alice', 'Bob'], startingStacks: [1000, 1000], blindsOrStraddles: [10, 20], smallBet: 20, bigBet: 40, antes: [0, 0], }); const stud = Poker.Hand({ variant: 'F7S', players: ['Alice', 'Bob'], blindsOrStraddles: [10, 20], startingStacks: [1000, 1000], antes: [5, 5], bringIn: 10, smallBet: 20, bigBet: 40, }); expect(noLimit.variant).toBe('NT'); expect(noLimit.minBet).toBe(20); expect(fixedLimit.variant).toBe('FT'); expect(fixedLimit.smallBet).toBe(20); expect(fixedLimit.bigBet).toBe(40); expect(stud.variant).toBe('F7S'); expect(stud.bringIn).toBe(10); }); }); });