126 lines
4.9 KiB
TypeScript
126 lines
4.9 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
|
vi.mock('electron', () => ({ default: { ipcMain: { handle: vi.fn() }, dialog: {}, shell: {} } }));
|
|
vi.mock('../../src/main/services/VisionDetect.js', () => ({
|
|
refreshVisionCache: vi.fn(async () => ({ ok: true as const, models: ['gemma3:12b-vision'] }))
|
|
}));
|
|
vi.mock('../../src/main/services/GitClient.js');
|
|
|
|
import electron from 'electron';
|
|
import { refreshVisionCache } from '../../src/main/services/VisionDetect.js';
|
|
import { registerSettingsApi } from '../../src/main/ipc/settingsApi.js';
|
|
import type { SettingsIpcDeps } from '../../src/main/ipc/settingsApi.js';
|
|
|
|
function getHandler(channel: string): (...args: unknown[]) => unknown {
|
|
const handle = (electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle;
|
|
const call = handle.mock.calls.find((c) => c[0] === channel);
|
|
if (!call) throw new Error(`channel ${channel} not registered`);
|
|
return call[1] as (...args: unknown[]) => unknown;
|
|
}
|
|
|
|
function makeDeps() {
|
|
const settings = {
|
|
getVisionModel: vi.fn(async () => 'gemma3:12b-vision'),
|
|
setVisionModel: vi.fn(async () => {}),
|
|
getVisionCapableCache: vi.fn(async () => ({
|
|
models: ['gemma3:12b-vision', 'llava:13b'],
|
|
at: '2026-05-10T05:00:00Z'
|
|
})),
|
|
setVisionCapableCache: vi.fn(async () => {}),
|
|
// existing methods used by other handlers
|
|
getAll: vi.fn(async () => ({
|
|
ollama: { endpoint: 'http://localhost:11434', model: 'gemma2:2b' }
|
|
})),
|
|
setAiEnabled: vi.fn(async () => {}),
|
|
setOnboardingCompleted: vi.fn(async () => {}),
|
|
isAiEnabled: vi.fn(async () => true),
|
|
getSyncRepoUrl: vi.fn(async () => null),
|
|
setSyncRepoUrl: vi.fn(async () => {}),
|
|
isAutoSyncEnabled: vi.fn(async () => false),
|
|
getSyncIntervalMin: vi.fn(async () => 30),
|
|
setSyncIntervalMin: vi.fn(async () => {}),
|
|
setAutoSyncEnabled: vi.fn(async () => {})
|
|
};
|
|
|
|
const syncSvc = {
|
|
getSyncDir: vi.fn(() => '/tmp/sync'),
|
|
listConflicts: vi.fn(() => []),
|
|
resolveConflict: vi.fn(async () => ({ ok: true as const })),
|
|
getLastStatus: vi.fn(() => ({ lastAt: null as string | null, lastResult: null as { ok: boolean } | null }))
|
|
};
|
|
|
|
const deps: Partial<SettingsIpcDeps> = {
|
|
backup: { runDaily: vi.fn(async () => ({ snapshotted: false })) } as never,
|
|
exportSvc: {} as never,
|
|
importSvc: {} as never,
|
|
syncSvc: syncSvc as never,
|
|
telemetry: { exportTo: vi.fn(async () => ({ eventCount: 0 })) } as never,
|
|
settings: settings as never,
|
|
getInboxWindow: () => null
|
|
};
|
|
|
|
return { settings, syncSvc, deps };
|
|
}
|
|
|
|
describe('vision IPC channels', () => {
|
|
beforeEach(() => {
|
|
(electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle.mockClear();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('3 vision channels registered', () => {
|
|
const { deps } = makeDeps();
|
|
registerSettingsApi(deps as SettingsIpcDeps);
|
|
|
|
const handle = (electron.ipcMain as unknown as { handle: ReturnType<typeof vi.fn> }).handle;
|
|
const channels = handle.mock.calls.map((c) => c[0]);
|
|
expect(channels).toContain('settings:get-vision-models');
|
|
expect(channels).toContain('settings:set-vision-model');
|
|
expect(channels).toContain('settings:refresh-vision-cache');
|
|
});
|
|
|
|
it('settings:get-vision-models returns { models, at, selected } from settings', async () => {
|
|
const { deps, settings } = makeDeps();
|
|
registerSettingsApi(deps as SettingsIpcDeps);
|
|
const h = getHandler('settings:get-vision-models');
|
|
const r = await h({});
|
|
expect(settings.getVisionCapableCache).toHaveBeenCalled();
|
|
expect(settings.getVisionModel).toHaveBeenCalled();
|
|
expect(r).toEqual({
|
|
models: ['gemma3:12b-vision', 'llava:13b'],
|
|
at: '2026-05-10T05:00:00Z',
|
|
selected: 'gemma3:12b-vision'
|
|
});
|
|
});
|
|
|
|
it('settings:set-vision-model calls settings.setVisionModel(value) + returns { ok: true }', async () => {
|
|
const { deps, settings } = makeDeps();
|
|
registerSettingsApi(deps as SettingsIpcDeps);
|
|
const h = getHandler('settings:set-vision-model');
|
|
const r = await h({}, 'llava:13b');
|
|
expect(settings.setVisionModel).toHaveBeenCalledWith('llava:13b');
|
|
expect(r).toEqual({ ok: true });
|
|
});
|
|
|
|
it('settings:refresh-vision-cache calls refreshVisionCache and returns result', async () => {
|
|
const { deps } = makeDeps();
|
|
registerSettingsApi(deps as SettingsIpcDeps);
|
|
const h = getHandler('settings:refresh-vision-cache');
|
|
const r = await h({});
|
|
expect(refreshVisionCache).toHaveBeenCalledWith({
|
|
settings: deps.settings,
|
|
endpoint: 'http://localhost:11434'
|
|
});
|
|
expect(r).toEqual({ ok: true, models: ['gemma3:12b-vision'] });
|
|
});
|
|
|
|
it('settings:set-vision-model with null clears the value', async () => {
|
|
const { deps, settings } = makeDeps();
|
|
registerSettingsApi(deps as SettingsIpcDeps);
|
|
const h = getHandler('settings:set-vision-model');
|
|
const r = await h({}, null);
|
|
expect(settings.setVisionModel).toHaveBeenCalledWith(null);
|
|
expect(r).toEqual({ ok: true });
|
|
});
|
|
});
|