import { describe, it, expect } from 'vitest'; import { createPart, createParts, createPartPool, mergePartPools, PartPool } from '@/core/part-factory'; describe('createPart', () => { it('should create a part with given template and id', () => { const part = createPart<{ player: string }>( { regionId: 'board', position: [1, 2], player: 'X' }, 'piece-1' ); expect(part.id).toBe('piece-1'); expect(part.regionId).toBe('board'); expect(part.position).toEqual([1, 2]); expect(part.player).toBe('X'); }); it('should apply default values for regionId and position when not provided', () => { const part = createPart({}, 'piece-1'); expect(part.regionId).toBe(''); expect(part.position).toEqual([]); }); it('should allow overriding default values', () => { const part = createPart({ regionId: 'custom', position: [0] }, 'piece-1'); expect(part.regionId).toBe('custom'); expect(part.position).toEqual([0]); }); it('should preserve metadata fields', () => { type TestMeta = { type: string; count: number }; const part = createPart( { regionId: 'board', position: [0], type: 'kitten', count: 5 }, 'piece-1' ); expect(part.type).toBe('kitten'); expect(part.count).toBe(5); }); }); describe('createParts', () => { it('should create multiple parts with auto-generated IDs', () => { const parts = createParts( { regionId: 'deck', position: [] }, 3, 'card' ); expect(parts.length).toBe(3); expect(parts[0].id).toBe('card-1'); expect(parts[1].id).toBe('card-2'); expect(parts[2].id).toBe('card-3'); }); it('should create parts with identical properties', () => { const parts = createParts( { regionId: 'deck', position: [], type: 'token' }, 2, 'token' ); expect(parts[0].regionId).toBe('deck'); expect(parts[1].regionId).toBe('deck'); expect(parts[0].type).toBe('token'); expect(parts[1].type).toBe('token'); }); it('should create zero parts when count is 0', () => { const parts = createParts({}, 0, 'empty'); expect(parts.length).toBe(0); }); }); describe('createPartPool', () => { it('should create a pool with specified count', () => { const pool = createPartPool( { regionId: 'supply', position: [] }, 5, 'token' ); expect(pool.remaining()).toBe(5); expect(Object.keys(pool.parts).length).toBe(5); }); it('should generate parts with correct IDs', () => { const pool = createPartPool({}, 3, 'piece'); expect(pool.parts['piece-1']).toBeDefined(); expect(pool.parts['piece-2']).toBeDefined(); expect(pool.parts['piece-3']).toBeDefined(); }); it('should draw parts from the pool', () => { const pool = createPartPool({}, 2, 'card'); const drawn = pool.draw(); expect(drawn).toBeDefined(); expect(drawn!.id).toBe('card-2'); expect(pool.remaining()).toBe(1); }); it('should return undefined when pool is empty', () => { const pool = createPartPool({}, 1, 'card'); pool.draw(); const drawn = pool.draw(); expect(drawn).toBeUndefined(); }); it('should return part to pool', () => { const pool = createPartPool({ regionId: 'board', position: [0, 0] }, 1, 'card'); const drawn = pool.draw(); expect(pool.remaining()).toBe(0); pool.return(drawn!); expect(pool.remaining()).toBe(1); expect(drawn!.regionId).toBe(''); expect(drawn!.position).toEqual([]); }); it('should store parts as Record keyed by ID', () => { const pool = createPartPool({}, 2, 'piece'); expect(typeof pool.parts).toBe('object'); expect(pool.parts['piece-1']).toBeDefined(); expect(pool.parts['piece-2']).toBeDefined(); }); }); describe('mergePartPools', () => { it('should merge multiple pools', () => { const pool1 = createPartPool({ regionId: 'deck1', position: [] }, 2, 'card1'); const pool2 = createPartPool({ regionId: 'deck2', position: [] }, 3, 'card2'); const merged = mergePartPools(pool1, pool2); expect(Object.keys(merged.parts).length).toBe(5); // Parts with regionId: '' are available; pool parts have regionId from template // After merge, available = parts with regionId === '' expect(merged.remaining()).toBe(0); // parts have regionId: 'deck1' or 'deck2' expect(merged.parts['card1-1']).toBeDefined(); expect(merged.parts['card2-3']).toBeDefined(); }); it('should use first pool template', () => { const pool1 = createPartPool({ regionId: 'deck1', position: [] }, 1, 'card1'); const pool2 = createPartPool({ regionId: 'deck2', position: [] }, 1, 'card2'); const merged = mergePartPools(pool1, pool2); expect(merged.template.regionId).toBe('deck1'); }); it('should return empty pool when no pools provided', () => { const merged = mergePartPools(); expect(Object.keys(merged.parts).length).toBe(0); expect(merged.remaining()).toBe(0); }); it('should only count available parts for remaining()', () => { const pool1 = createPartPool({}, 2, 'card'); const pool2 = createPartPool({}, 2, 'token'); const drawn = pool1.draw(); drawn!.regionId = 'board'; drawn!.position = [0, 0]; const merged = mergePartPools(pool1, pool2); expect(merged.remaining()).toBe(3); }); it('should handle drawing from merged pool', () => { const pool1 = createPartPool({}, 2, 'card'); const pool2 = createPartPool({}, 2, 'token'); const merged = mergePartPools(pool1, pool2); const drawn = merged.draw(); expect(drawn).toBeDefined(); expect(merged.remaining()).toBe(3); }); });