import { beforeEach, describe, expect, it, vi } from 'vitest'; import { safeStorage } from 'electron'; const fakeStore = vi.hoisted(() => ({ options: null as Record | null, values: new Map(), })); vi.mock('electron-store', () => ({ default: class FakeElectronStore { constructor(options: Record) { fakeStore.options = options; } get(key: string) { return fakeStore.values.get(key); } set(key: string, value: unknown) { fakeStore.values.set(key, value); } delete(key: string) { fakeStore.values.delete(key); } }, })); describe('remembered password persistence', () => { beforeEach(() => { vi.resetModules(); fakeStore.options = null; fakeStore.values.clear(); }); it('stores only an OS-encrypted Main record and restores the credentials', async () => { const firstModule = await import('@electron/services/remembered-password'); await firstModule.initializeRememberedPassword({ secureStorage: safeStorage }); await expect(firstModule.updateRememberedPassword({ username: 'zhangsan', password: 'plain-password', })).resolves.toBe(true); expect(fakeStore.options).toMatchObject({ name: 'remembered-password', configFileMode: 0o600, }); const serializedStore = JSON.stringify(Object.fromEntries(fakeStore.values)); expect(serializedStore).not.toContain('zhangsan'); expect(serializedStore).not.toContain('plain-password'); vi.resetModules(); const restoredModule = await import('@electron/services/remembered-password'); await restoredModule.initializeRememberedPassword({ secureStorage: safeStorage }); await expect(restoredModule.getRememberedPasswordState()).resolves.toEqual({ available: true, credentials: { username: 'zhangsan', password: 'plain-password', }, }); }); it('does not offer persistence without OS credential encryption', async () => { const passwordModule = await import('@electron/services/remembered-password'); await passwordModule.initializeRememberedPassword(); await expect(passwordModule.updateRememberedPassword({ username: 'zhangsan', password: 'plain-password', })).resolves.toBe(false); await expect(passwordModule.getRememberedPasswordState()).resolves.toEqual({ available: false, credentials: null, }); expect(fakeStore.options).toBeNull(); }); it('discards an unreadable record without permanently disabling the feature', async () => { const passwordModule = await import('@electron/services/remembered-password'); const persistence = { load: vi.fn().mockRejectedValue(new Error('credential cannot be decrypted')), save: vi.fn().mockResolvedValue(undefined), }; await passwordModule.initializeRememberedPassword({ persistence }); expect(persistence.save).toHaveBeenCalledWith(null); await expect(passwordModule.getRememberedPasswordState()).resolves.toEqual({ available: true, credentials: null, }); }); });