import { describe, it, expect } from 'vitest'; import { generateFloorState } from '../utils/room-utils'; import { SWARM_CONFIG } from '../constants'; import { getGuardianForFloor } from '../data/guardian-encounters'; import { getFloorMaxHP } from '../utils/floor-utils'; import { getDodgeChance } from '../utils/room-utils'; // ─── generateFloorState ─────────────────────────────────────────────────────── describe('generateFloorState', () => { it('should return a FloorState object', () => { const state = generateFloorState(1); expect(typeof state).toBe('object'); expect(state).toHaveProperty('roomType'); expect(state).toHaveProperty('enemies'); }); it('should generate guardian state for guardian floor', () => { const state = generateFloorState(10); expect(state.roomType).toBe('guardian'); expect(state.enemies.length).toBe(1); const g10 = getGuardianForFloor(10)!; expect(state.enemies[0].name).toBe(g10.name); expect(state.enemies[0].hp).toBe(g10.hp); expect(state.enemies[0].element).toBe(g10.element); }); it('should generate combat state for non-guardian floor with combat', () => { const originalRandom = Math.random; Math.random = () => 0.5; // Won't trigger special rooms const state = generateFloorState(5); expect(state.roomType).toBe('combat'); expect(state.enemies.length).toBe(1); expect(state.enemies[0].hp).toBe(getFloorMaxHP(5)); Math.random = originalRandom; }); it('should generate swarm state for swarm room', () => { const originalRandom = Math.random; Math.random = () => 0.14; // < SWARM_ROOM_CHANCE (0.15) const state = generateFloorState(5); expect(state.roomType).toBe('swarm'); expect(Array.isArray(state.enemies)).toBe(true); expect(state.enemies.length).toBeGreaterThanOrEqual(SWARM_CONFIG.minEnemies); Math.random = originalRandom; }); it('should generate speed state for speed room', () => { // Note: In the current implementation, swarm is checked before speed. // Speed rooms can only be generated if random >= SWARM_ROOM_CHANCE (0.15) // AND random < SPEED_ROOM_CHANCE (0.10), which is impossible since 0.15 > 0.10. // This test verifies the speed code path exists by using a mock that // bypasses the swarm check. const originalRandom = Math.random; // First call (swarm check) returns >= 0.15, second call (speed check) returns < 0.10 let callCount = 0; Math.random = () => { callCount++; if (callCount === 1) return 0.16; // >= SWARM_ROOM_CHANCE, skip swarm if (callCount === 2) return 0.05; // < SPEED_ROOM_CHANCE, trigger speed return 0.5; }; const state = generateFloorState(5); expect(state.roomType).toBe('speed'); expect(state.enemies.length).toBe(1); Math.random = originalRandom; }); it('should generate puzzle state for puzzle room', () => { const originalRandom = Math.random; Math.random = () => 0.19; // < PUZZLE_ROOM_CHANCE (0.20) const state = generateFloorState(7); expect(state.roomType).toBe('puzzle'); expect(Array.isArray(state.enemies)).toBe(true); expect(state.enemies).toEqual([]); expect(state.puzzleProgress).toBe(0); expect(typeof state.puzzleRequired).toBe('number'); expect(typeof state.puzzleId).toBe('string'); expect(typeof state.puzzleAttunements).toBe('object'); Math.random = originalRandom; }); it('should fill puzzle attunements from PUZZLE_ROOMS', () => { const originalRandom = Math.random; Math.random = () => 0.19; const state = generateFloorState(7); expect(state.roomType).toBe('puzzle'); expect(state.puzzleAttunements!.length).toBeGreaterThan(0); expect(typeof state.puzzleAttunements![0]).toBe('string'); Math.random = originalRandom; }); it('should use correct element for floor', () => { const state = generateFloorState(1); expect(state.enemies[0].element).toBe('fire'); const state2 = generateFloorState(2); expect(state2.enemies[0].element).toBe('water'); }); it('combat enemy HP should match floor max HP', () => { const state = generateFloorState(50); expect(state.enemies[0].hp).toBe(getFloorMaxHP(50)); }); it('speed room should have correct dodge chance', () => { // Use a mock that bypasses swarm check and triggers speed const originalRandom = Math.random; let callCount = 0; Math.random = () => { callCount++; if (callCount === 1) return 0.16; // >= SWARM_ROOM_CHANCE, skip swarm if (callCount === 2) return 0.05; // < SPEED_ROOM_CHANCE, trigger speed return 0.5; }; const speedState = generateFloorState(51); expect(speedState.roomType).toBe('speed'); expect(speedState.enemies[0].dodgeChance).toBe(getDodgeChance(51)); Math.random = originalRandom; }); it('should handle very high floor number', () => { const state = generateFloorState(1000); expect(state.roomType).toBe('guardian'); }); it('should handle floor 0', () => { // Floor 0: getGuardianForFloor(0) returns null, 0 % 7 === 0 // With real random, it could be puzzle. Mock to ensure combat. const originalRandom = Math.random; Math.random = () => 0.5; // High random, won't trigger puzzle const state = generateFloorState(0); expect(state.roomType).toBe('combat'); Math.random = originalRandom; }); });