import { describe, it, expect, beforeEach } from 'vitest'; import { useCombatStore } from '../stores/combatStore'; import { usePrestigeStore } from '../stores/prestigeStore'; import { getFloorMaxHP } from '../utils'; import { ErrorCode } from '../utils/result'; function resetCombatStore() { useCombatStore.setState({ currentFloor: 1, floorHP: getFloorMaxHP(1), floorMaxHP: getFloorMaxHP(1), maxFloorReached: 1, activeSpell: 'manaBolt', currentAction: 'meditate', castProgress: 0, spireMode: false, currentRoom: { roomType: 'combat', enemies: [] }, clearedFloors: {}, climbDirection: null, isDescending: false, golemancy: { enabledGolems: [], summonedGolems: [], lastSummonFloor: 0 }, equipmentSpellStates: [], comboHitCount: 0, floorHitCount: 0, spells: { manaBolt: { learned: true, level: 1, studyProgress: 0 } }, activityLog: [], achievements: { unlocked: [], progress: {} }, totalSpellsCast: 0, totalDamageDealt: 0, totalCraftsCompleted: 0, }); } function resetPrestigeStore() { usePrestigeStore.setState({ loopCount: 0, insight: 500, totalInsight: 500, loopInsight: 0, prestigeUpgrades: {}, pactSlots: 1, defeatedGuardians: [], signedPacts: [], signedPactDetails: {}, pactRitualFloor: null, pactRitualProgress: 0, }); } describe('CombatStore', () => { beforeEach(resetCombatStore); describe('setCurrentFloor', () => { it('should set floor and update HP', () => { useCombatStore.getState().setCurrentFloor(5); expect(useCombatStore.getState().currentFloor).toBe(5); expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(5)); expect(useCombatStore.getState().floorMaxHP).toBe(getFloorMaxHP(5)); }); }); describe('advanceFloor', () => { it('should increment floor and update HP', () => { useCombatStore.getState().advanceFloor(); expect(useCombatStore.getState().currentFloor).toBe(2); expect(useCombatStore.getState().floorHP).toBe(getFloorMaxHP(2)); }); it('should cap at floor 100', () => { useCombatStore.setState({ currentFloor: 100 }); useCombatStore.getState().advanceFloor(); expect(useCombatStore.getState().currentFloor).toBe(100); }); it('should update maxFloorReached', () => { useCombatStore.getState().advanceFloor(); expect(useCombatStore.getState().maxFloorReached).toBe(2); }); it('should reset cast progress', () => { useCombatStore.setState({ castProgress: 0.5 }); useCombatStore.getState().advanceFloor(); expect(useCombatStore.getState().castProgress).toBe(0); }); }); describe('setFloorHP', () => { it('should set floor HP', () => { useCombatStore.getState().setFloorHP(50); expect(useCombatStore.getState().floorHP).toBe(50); }); it('should clamp negative to 0', () => { useCombatStore.getState().setFloorHP(-10); expect(useCombatStore.getState().floorHP).toBe(0); }); }); describe('setMaxFloorReached', () => { it('should update max floor reached', () => { useCombatStore.getState().setMaxFloorReached(10); expect(useCombatStore.getState().maxFloorReached).toBe(10); }); it('should only increase, never decrease', () => { useCombatStore.setState({ maxFloorReached: 10 }); useCombatStore.getState().setMaxFloorReached(5); expect(useCombatStore.getState().maxFloorReached).toBe(10); }); }); describe('setAction / setSpell', () => { it('should set current action', () => { useCombatStore.getState().setAction('climb'); expect(useCombatStore.getState().currentAction).toBe('climb'); }); it('should set active spell when learned', () => { useCombatStore.getState().setSpell('manaBolt'); expect(useCombatStore.getState().activeSpell).toBe('manaBolt'); }); it('should not set spell when not learned', () => { useCombatStore.getState().setSpell('fireball'); expect(useCombatStore.getState().activeSpell).toBe('manaBolt'); }); }); describe('learnSpell', () => { it('should add a new learned spell', () => { useCombatStore.getState().learnSpell('fireball'); expect(useCombatStore.getState().spells.fireball.learned).toBe(true); }); }); describe('resetCombat', () => { it('should reset to starting floor', () => { useCombatStore.setState({ currentFloor: 50, maxFloorReached: 50 }); useCombatStore.getState().resetCombat(1); expect(useCombatStore.getState().currentFloor).toBe(1); expect(useCombatStore.getState().maxFloorReached).toBe(1); }); }); describe('climbDownFloor', () => { it('should decrement floor', () => { useCombatStore.setState({ currentFloor: 5 }); useCombatStore.getState().climbDownFloor(); expect(useCombatStore.getState().currentFloor).toBe(4); }); it('should not go below floor 1', () => { useCombatStore.setState({ currentFloor: 1 }); useCombatStore.getState().climbDownFloor(); expect(useCombatStore.getState().currentFloor).toBe(1); }); }); describe('exitSpireMode', () => { it('should reset spire state', () => { useCombatStore.setState({ spireMode: true, climbDirection: 'up', currentAction: 'climb' }); useCombatStore.getState().exitSpireMode(); expect(useCombatStore.getState().spireMode).toBe(false); expect(useCombatStore.getState().climbDirection).toBeNull(); expect(useCombatStore.getState().currentAction).toBe('meditate'); }); }); }); describe('PrestigeStore', () => { beforeEach(resetPrestigeStore); describe('doPrestige', () => { it('should purchase upgrade when affordable', () => { usePrestigeStore.setState({ insight: 2000 }); const result = usePrestigeStore.getState().doPrestige('insightAmp'); expect(result.success).toBe(true); expect(usePrestigeStore.getState().prestigeUpgrades.insightAmp).toBe(1); expect(usePrestigeStore.getState().insight).toBeLessThan(2000); }); it('should return false when cannot afford', () => { usePrestigeStore.setState({ insight: 0 }); const result = usePrestigeStore.getState().doPrestige('insightAmp'); expect(result.success).toBe(false); if (!result.success) { expect(result.code).toBe(ErrorCode.INSUFFICIENT_INSIGHT); } }); it('should fail for invalid upgrade id', () => { const result = usePrestigeStore.getState().doPrestige('nonexistent'); expect(result.success).toBe(false); if (!result.success) { expect(result.code).toBe(ErrorCode.INVALID_PRESTIGE_ID); } }); }); describe('defeatGuardian / signedPacts', () => { it('should add defeated guardian', () => { usePrestigeStore.getState().defeatGuardian(10); expect(usePrestigeStore.getState().defeatedGuardians).toContain(10); }); it('should not duplicate defeated guardian', () => { usePrestigeStore.getState().defeatGuardian(10); usePrestigeStore.getState().defeatGuardian(10); expect(usePrestigeStore.getState().defeatedGuardians.filter(f => f === 10).length).toBe(1); }); it('should not defeat already signed guardian', () => { usePrestigeStore.setState({ signedPacts: [10] }); usePrestigeStore.getState().defeatGuardian(10); expect(usePrestigeStore.getState().defeatedGuardians).not.toContain(10); }); it('should add signed pact', () => { usePrestigeStore.getState().addSignedPact(10); expect(usePrestigeStore.getState().signedPacts).toContain(10); }); it('should remove pact', () => { usePrestigeStore.setState({ signedPacts: [10, 20] }); usePrestigeStore.getState().removePact(10); expect(usePrestigeStore.getState().signedPacts).not.toContain(10); expect(usePrestigeStore.getState().signedPacts).toContain(20); }); }); describe('startPactRitual', () => { it('should start ritual when conditions met', () => { usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [], insight: 10000 }); const result = usePrestigeStore.getState().startPactRitual(10, 10000); expect(result.success).toBe(true); expect(usePrestigeStore.getState().pactRitualFloor).toBe(10); }); it('should return false when guardian not defeated', () => { const result = usePrestigeStore.getState().startPactRitual(10, 10000); expect(result.success).toBe(false); if (!result.success) { expect(result.code).toBe(ErrorCode.GUARDIAN_NOT_DEFEATED); } }); it('should fail when already signed', () => { usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [10] }); const result = usePrestigeStore.getState().startPactRitual(10, 10000); expect(result.success).toBe(false); if (!result.success) { expect(result.code).toBe(ErrorCode.PACT_ALREADY_SIGNED); } }); it('should fail when pact slots full', () => { usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [20], pactSlots: 1 }); const result = usePrestigeStore.getState().startPactRitual(10, 10000); expect(result.success).toBe(false); if (!result.success) { expect(result.code).toBe(ErrorCode.PACT_SLOTS_FULL); } }); it('should fail when insufficient mana', () => { usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [] }); const result = usePrestigeStore.getState().startPactRitual(10, 0); expect(result.success).toBe(false); if (!result.success) { expect(result.code).toBe(ErrorCode.INSUFFICIENT_MANA); } }); }); describe('cancelPactRitual', () => { it('should cancel active ritual', () => { usePrestigeStore.setState({ pactRitualFloor: 10, pactRitualProgress: 1 }); usePrestigeStore.getState().cancelPactRitual(); expect(usePrestigeStore.getState().pactRitualFloor).toBeNull(); expect(usePrestigeStore.getState().pactRitualProgress).toBe(0); }); }); describe('startNewLoop', () => { it('should increment loop count and add insight', () => { usePrestigeStore.setState({ insight: 100, totalInsight: 100 }); usePrestigeStore.getState().startNewLoop(50); expect(usePrestigeStore.getState().loopCount).toBe(1); expect(usePrestigeStore.getState().insight).toBe(150); expect(usePrestigeStore.getState().totalInsight).toBe(150); }); it('should reset loop-specific state', () => { usePrestigeStore.setState({ defeatedGuardians: [10], signedPacts: [20], pactRitualFloor: 10, pactRitualProgress: 5, }); usePrestigeStore.getState().startNewLoop(0); expect(usePrestigeStore.getState().defeatedGuardians).toEqual([]); expect(usePrestigeStore.getState().signedPacts).toEqual([]); expect(usePrestigeStore.getState().pactRitualFloor).toBeNull(); }); }); describe('resetPrestigeForNewLoop', () => { it('should preserve insight and upgrades, reset loop state', () => { usePrestigeStore.getState().resetPrestigeForNewLoop(200, { insightAmp: 2 }); expect(usePrestigeStore.getState().insight).toBe(200); expect(usePrestigeStore.getState().prestigeUpgrades).toEqual({ insightAmp: 2 }); expect(usePrestigeStore.getState().defeatedGuardians).toEqual([]); }); }); describe('resetPrestige', () => { it('should reset everything to initial state', () => { usePrestigeStore.setState({ insight: 1000, loopCount: 5, prestigeUpgrades: { insightAmp: 3 } }); usePrestigeStore.getState().resetPrestige(); expect(usePrestigeStore.getState().insight).toBe(0); expect(usePrestigeStore.getState().loopCount).toBe(0); expect(usePrestigeStore.getState().prestigeUpgrades).toEqual({}); }); }); });