import { describe, it, expect, beforeEach } from 'vitest'; import { useDisciplineStore } from '../stores/discipline-slice'; import { useManaStore } from '../stores/manaStore'; function resetDisciplineStore() { useDisciplineStore.setState({ disciplines: {}, activeIds: [], concurrentLimit: 1, totalXP: 0, processedPerks: [], practicingCallbacks: null, }); } describe('DisciplineStore — reactivate after deactivate', () => { beforeEach(resetDisciplineStore); it('should reactivate a raw discipline after deactivate when rawMana is sufficient', () => { // Set up mana store with sufficient raw mana useManaStore.setState({ rawMana: 1000, elements: {} }); // First activation succeeds (reads rawMana from mana store) useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); // Deactivate useDisciplineStore.getState().deactivate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).not.toContain('raw-mastery'); expect(useDisciplineStore.getState().disciplines['raw-mastery'].paused).toBe(true); // Reactivate — reads rawMana=1000 from mana store, should succeed useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); expect(useDisciplineStore.getState().disciplines['raw-mastery'].paused).toBe(false); }); it('should NOT reactivate a raw discipline when rawMana is insufficient', () => { // Activate with sufficient mana useManaStore.setState({ rawMana: 1000, elements: {} }); useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); // Build up XP via ticks useDisciplineStore.getState().processTick({ rawMana: 1000, elements: {} }); useDisciplineStore.getState().processTick({ rawMana: 1000, elements: {} }); const xp = useDisciplineStore.getState().disciplines['raw-mastery'].xp; expect(xp).toBeGreaterThan(0); // Deactivate useDisciplineStore.getState().deactivate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).not.toContain('raw-mastery'); // Set rawMana to 0 — reactivation should fail useManaStore.setState({ rawMana: 0, elements: {} }); useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).not.toContain('raw-mastery'); }); it('should reactivate a discipline with elements after deactivating it', () => { useManaStore.setState({ rawMana: 0, elements: { fire: { unlocked: true, current: 100, max: 100, baseMax: 100 } }, }); useDisciplineStore.getState().activate('attune-fire'); expect(useDisciplineStore.getState().activeIds).toContain('attune-fire'); useDisciplineStore.getState().deactivate('attune-fire'); expect(useDisciplineStore.getState().activeIds).not.toContain('attune-fire'); // Reactivate — reads elements from mana store useDisciplineStore.getState().activate('attune-fire'); expect(useDisciplineStore.getState().activeIds).toContain('attune-fire'); expect(useDisciplineStore.getState().disciplines['attune-fire'].paused).toBe(false); }); it('should reactivate after processTick auto-pauses due to no mana', () => { useManaStore.setState({ rawMana: 100, elements: {} }); useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); // Tick with no mana — discipline auto-pauses useDisciplineStore.getState().processTick({ rawMana: 0, elements: {} }); expect(useDisciplineStore.getState().activeIds).not.toContain('raw-mastery'); expect(useDisciplineStore.getState().disciplines['raw-mastery'].paused).toBe(true); // Restore mana and reactivate useManaStore.setState({ rawMana: 1000, elements: {} }); useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); expect(useDisciplineStore.getState().disciplines['raw-mastery'].paused).toBe(false); }); it('should preserve XP when deactivating and reactivating', () => { useManaStore.setState({ rawMana: 1000, elements: {} }); useDisciplineStore.getState().activate('raw-mastery'); useDisciplineStore.getState().processTick({ rawMana: 1000, elements: {} }); useDisciplineStore.getState().processTick({ rawMana: 1000, elements: {} }); useDisciplineStore.getState().processTick({ rawMana: 1000, elements: {} }); expect(useDisciplineStore.getState().disciplines['raw-mastery'].xp).toBe(3); useDisciplineStore.getState().deactivate('raw-mastery'); expect(useDisciplineStore.getState().disciplines['raw-mastery'].xp).toBe(3); expect(useDisciplineStore.getState().disciplines['raw-mastery'].paused).toBe(true); useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); expect(useDisciplineStore.getState().disciplines['raw-mastery'].xp).toBe(3); useDisciplineStore.getState().processTick({ rawMana: 1000, elements: {} }); expect(useDisciplineStore.getState().disciplines['raw-mastery'].xp).toBe(4); }); it('should use gameState overrides when explicitly provided (for test control)', () => { // Even though mana store has no mana, explicit override should work useManaStore.setState({ rawMana: 0, elements: {} }); // First activation: no state yet, so canProceed returns true (optimistic) useDisciplineStore.getState().activate('raw-mastery'); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); useDisciplineStore.getState().deactivate('raw-mastery'); // Reactivate with explicit override — bypasses store reads useDisciplineStore.getState().activate('raw-mastery', { rawMana: 1000, elements: {}, signedPacts: [], }); expect(useDisciplineStore.getState().activeIds).toContain('raw-mastery'); }); });