import { beforeEach, describe, expect, test, vi } from 'vitest'; import { useLicenseStore } from './licenseStore'; function mockElectronAPI(overrides: Record = {}) { (window as any).electronAPI = { getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }), activateLicense: vi.fn().mockResolvedValue(null), deactivateLicense: vi.fn().mockResolvedValue(undefined), hasLicenseFeature: vi.fn().mockResolvedValue(false), ...overrides, }; } describe('licenseStore', () => { beforeEach(() => { mockElectronAPI(); useLicenseStore.setState({ status: null, isLoaded: false, canEdit: true, canUseAI: false, showDialog: false }); }); describe('canEdit', () => { test('is true for Licensed status', async () => { mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Licensed', license: { license_id: 'x', tier: 'pro' } }), }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().canEdit).toBe(true); }); test('is true for Trial status', async () => { mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Trial', days_remaining: 20, started_at: Date.now() }), }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().canEdit).toBe(true); }); test('is false for Expired status', async () => { mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }), }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().canEdit).toBe(false); }); test('is false when status is null', () => { useLicenseStore.setState({ status: null, canEdit: true, canUseAI: false }); useLicenseStore.getState().setStatus(null); expect(useLicenseStore.getState().canEdit).toBe(false); }); test('is true for Licensed status', () => { useLicenseStore.getState().setStatus({ tag: 'Licensed', license: { license_id: 'x', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 } }); expect(useLicenseStore.getState().canEdit).toBe(true); expect(useLicenseStore.getState().canUseAI).toBe(true); }); test('is false for Trial status', () => { useLicenseStore.getState().setStatus({ tag: 'Trial', days_remaining: 5, started_at: Date.now() }); expect(useLicenseStore.getState().canEdit).toBe(true); expect(useLicenseStore.getState().canUseAI).toBe(false); }); test('is false for Expired status', () => { useLicenseStore.getState().setStatus({ tag: 'Expired' }); expect(useLicenseStore.getState().canEdit).toBe(false); expect(useLicenseStore.getState().canUseAI).toBe(false); }); }); describe('checkStatus', () => { test('sets status to Licensed when backend returns Licensed', async () => { const license = { license_id: 'l1', tier: 'pro', customer_email: 'a@b.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 1 }; mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Licensed', license }) }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().status?.tag).toBe('Licensed'); }); test('sets status to Trial when backend returns Trial', async () => { mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Trial', days_remaining: 15, started_at: Date.now() }) }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().status?.tag).toBe('Trial'); }); test('sets status to Expired when backend returns Expired', async () => { mockElectronAPI({ getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }) }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().status?.tag).toBe('Expired'); }); test('handles API error gracefully', async () => { mockElectronAPI({ getAppStatus: vi.fn().mockRejectedValue(new Error('network error')) }); await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().status?.tag).toBe('Expired'); expect(useLicenseStore.getState().canEdit).toBe(false); expect(useLicenseStore.getState().canUseAI).toBe(false); }); test('handles missing electronAPI', async () => { delete (window as any).electronAPI; await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().status?.tag).toBe('Expired'); expect(useLicenseStore.getState().canEdit).toBe(false); expect(useLicenseStore.getState().canUseAI).toBe(false); }); test('sets isLoaded to true after check', async () => { await useLicenseStore.getState().checkStatus(); expect(useLicenseStore.getState().isLoaded).toBe(true); }); }); describe('activateLicense', () => { test('sets Licensed on valid key', async () => { const license = { license_id: 'l2', tier: 'pro', customer_email: 'x@y.com', expires_at: 9999999999, features: ['bg_removal'], issued_at: 1, max_activations: 1 }; mockElectronAPI({ activateLicense: vi.fn().mockResolvedValue(license) }); const result = await useLicenseStore.getState().activateLicense('talkedit_v1_validKey'); expect(result).toBe(true); expect(useLicenseStore.getState().status?.tag).toBe('Licensed'); expect(useLicenseStore.getState().canEdit).toBe(true); expect(useLicenseStore.getState().canUseAI).toBe(true); }); test('returns false on invalid key', async () => { mockElectronAPI({ activateLicense: vi.fn().mockResolvedValue(null) }); const result = await useLicenseStore.getState().activateLicense('invalid-key'); expect(result).toBe(false); }); test('returns false on API error', async () => { mockElectronAPI({ activateLicense: vi.fn().mockRejectedValue(new Error('bad key')) }); const result = await useLicenseStore.getState().activateLicense('bad-key'); expect(result).toBe(false); }); test('closes dialog on success', async () => { useLicenseStore.setState({ showDialog: true }); const license = { license_id: 'l3', tier: 'business', customer_email: 'z@z.com', expires_at: 9999999999, features: [], issued_at: 1, max_activations: 5 }; mockElectronAPI({ activateLicense: vi.fn().mockResolvedValue(license) }); await useLicenseStore.getState().activateLicense('talkedit_v1_key'); expect(useLicenseStore.getState().showDialog).toBe(false); }); }); describe('deactivateLicense', () => { test('sets Expired when trial is over', async () => { mockElectronAPI({ deactivateLicense: vi.fn().mockResolvedValue(undefined), getAppStatus: vi.fn().mockResolvedValue({ tag: 'Expired' }), }); await useLicenseStore.getState().deactivateLicense(); expect(useLicenseStore.getState().status?.tag).toBe('Expired'); expect(useLicenseStore.getState().canEdit).toBe(false); expect(useLicenseStore.getState().canUseAI).toBe(false); }); test('restores Trial when trial is still valid', async () => { mockElectronAPI({ deactivateLicense: vi.fn().mockResolvedValue(undefined), getAppStatus: vi.fn().mockResolvedValue({ tag: 'Trial', days_remaining: 5, started_at: Date.now() }), }); await useLicenseStore.getState().deactivateLicense(); expect(useLicenseStore.getState().status?.tag).toBe('Trial'); expect(useLicenseStore.getState().canEdit).toBe(true); expect(useLicenseStore.getState().canUseAI).toBe(false); }); test('handles API error', async () => { mockElectronAPI({ deactivateLicense: vi.fn().mockRejectedValue(new Error('fail')) }); useLicenseStore.setState({ status: { tag: 'Licensed', license: {} as any }, canEdit: true, canUseAI: true }); await useLicenseStore.getState().deactivateLicense(); expect(useLicenseStore.getState().status?.tag).toBe('Expired'); expect(useLicenseStore.getState().canEdit).toBe(false); expect(useLicenseStore.getState().canUseAI).toBe(false); }); }); describe('hasFeature', () => { test('returns true when feature exists', async () => { mockElectronAPI({ hasLicenseFeature: vi.fn().mockResolvedValue(true) }); const result = await useLicenseStore.getState().hasFeature('bg_removal'); expect(result).toBe(true); }); test('returns false when feature missing', async () => { mockElectronAPI({ hasLicenseFeature: vi.fn().mockResolvedValue(false) }); const result = await useLicenseStore.getState().hasFeature('nonexistent'); expect(result).toBe(false); }); test('returns false on API error', async () => { mockElectronAPI({ hasLicenseFeature: vi.fn().mockRejectedValue(new Error('fail')) }); const result = await useLicenseStore.getState().hasFeature('bg_removal'); expect(result).toBe(false); }); }); describe('setShowDialog', () => { test('toggles dialog', () => { useLicenseStore.getState().setShowDialog(true); expect(useLicenseStore.getState().showDialog).toBe(true); useLicenseStore.getState().setShowDialog(false); expect(useLicenseStore.getState().showDialog).toBe(false); }); }); });