Files
makelore/tests/unit/works-square-session-persistence.test.ts

121 lines
4.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { safeStorage } from 'electron';
const fakeStore = vi.hoisted(() => ({
options: null as Record<string, unknown> | null,
values: new Map<string, unknown>(),
constructorError: null as Error | null,
}));
vi.mock('electron-store', () => ({
default: class FakeElectronStore {
constructor(options: Record<string, unknown>) {
fakeStore.options = options;
if (fakeStore.constructorError && options.clearInvalidConfig !== true) {
throw fakeStore.constructorError;
}
}
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('Works Square encrypted session persistence', () => {
beforeEach(() => {
vi.resetModules();
fakeStore.options = null;
fakeStore.values.clear();
fakeStore.constructorError = null;
});
it('stores one encrypted Main-only record and restores it', async () => {
const firstModule = await import('@electron/services/works-square-session');
await firstModule.initializeWorksSquareSession({ secureStorage: safeStorage });
firstModule.storeWorksSquareSession({
accessToken: 'plain-access-token',
refreshToken: 'plain-refresh-token',
expiresAt: Date.now() + 3600_000,
lastActiveAt: Date.now(),
});
await firstModule.flushWorksSquareSessionPersistence();
expect(fakeStore.options).toMatchObject({
name: 'works-square-session',
configFileMode: 0o600,
});
const serializedStore = JSON.stringify(Object.fromEntries(fakeStore.values));
expect(serializedStore).not.toContain('plain-access-token');
expect(serializedStore).not.toContain('plain-refresh-token');
vi.resetModules();
const restoredModule = await import('@electron/services/works-square-session');
await expect(restoredModule.initializeWorksSquareSession({ secureStorage: safeStorage })).resolves.toMatchObject({
accessToken: 'plain-access-token',
canRefresh: true,
});
});
it('preserves a temporarily unreadable record until explicit reauthorization discards it', async () => {
const record = {
version: 1,
authBase: 'https://biz.nianxx.cn/auth/',
ciphertext: Buffer.from('encrypted:not-json', 'utf8').toString('base64'),
};
fakeStore.values.set('record', record);
const sessionModule = await import('@electron/services/works-square-session');
await expect(sessionModule.initializeWorksSquareSession({ secureStorage: safeStorage })).resolves.toBeNull();
expect(fakeStore.values.get('record')).toEqual(record);
expect(sessionModule.getWorksSquareSessionRestoreStatus()).toBe('unavailable');
await expect(sessionModule.discardUnrestorableWorksSquareSession()).resolves.toBe(true);
expect(fakeStore.values.has('record')).toBe(false);
expect(sessionModule.getWorksSquareSessionRestoreStatus()).toBe('ready');
});
it('discards malformed store config without aborting Main startup', async () => {
fakeStore.constructorError = new SyntaxError('Unexpected token in JSON');
const sessionModule = await import('@electron/services/works-square-session');
await expect(sessionModule.initializeWorksSquareSession({ secureStorage: safeStorage })).resolves.toBeNull();
expect(fakeStore.options).toMatchObject({
name: 'works-square-session',
configFileMode: 0o600,
clearInvalidConfig: true,
});
expect(sessionModule.getWorksSquareSessionRestoreStatus()).toBe('ready');
expect(sessionModule.consumeWorksSquareStartupRuntimeCleanupRequired()).toBe(true);
});
it('keeps unpackaged development sessions in memory without opening the disk store', async () => {
const sessionModule = await import('@electron/services/works-square-session');
await expect(sessionModule.initializeWorksSquareSession()).resolves.toBeNull();
await expect(sessionModule.commitWorksSquareSession({
accessToken: 'development-access-token',
refreshToken: 'development-refresh-token',
expiresAt: Date.now() + 3600_000,
lastActiveAt: Date.now(),
})).resolves.toMatchObject({
accessToken: 'development-access-token',
canRefresh: true,
});
await sessionModule.flushWorksSquareSessionPersistence();
expect(fakeStore.options).toBeNull();
expect(fakeStore.values.size).toBe(0);
});
});