import { describe, expect, it, vi } from 'vitest'; import { warmupOpencodeRuntime, type OpencodeStartupWarmupDependencies, } from '@electron/opencode/startup-warmup'; function createDependencies( overrides: Partial = {}, ): OpencodeStartupWarmupDependencies { return { hasAuthenticatedSession: () => true, getStatus: () => ({ state: 'stopped', port: 4096 }), getActiveProject: vi.fn(async () => ({ path: '/projects/demo' })), readProjectConfig: vi.fn(async () => ({ status: 'valid' as const, config: { initialized: true }, })), getConfiguredProviderCount: vi.fn(async () => 1), start: vi.fn(async () => ({ state: 'running' as const, port: 4096 })), ...overrides, }; } describe('warmupOpencodeRuntime', () => { it('starts a ready runtime in the background', async () => { const dependencies = createDependencies(); const result = await warmupOpencodeRuntime(dependencies); expect(result).toEqual({ started: true, status: { state: 'running', port: 4096 }, }); expect(dependencies.readProjectConfig).toHaveBeenCalledWith('/projects/demo'); expect(dependencies.getConfiguredProviderCount).toHaveBeenCalledOnce(); expect(dependencies.start).toHaveBeenCalledOnce(); }); it('does not start without an authenticated session', async () => { const dependencies = createDependencies({ hasAuthenticatedSession: () => false, }); await expect(warmupOpencodeRuntime(dependencies)).resolves.toEqual({ started: false, reason: 'no-session', }); expect(dependencies.getActiveProject).not.toHaveBeenCalled(); expect(dependencies.start).not.toHaveBeenCalled(); }); it.each([ ['no active project', { getActiveProject: vi.fn(async () => null) }, 'no-active-project'], [ 'uninitialized project', { readProjectConfig: vi.fn(async () => ({ status: 'valid' as const, config: { initialized: false }, })), }, 'project-not-ready', ], ['no configured provider', { getConfiguredProviderCount: vi.fn(async () => 0) }, 'no-provider'], ] as const)('skips when there is %s', async (_label, overrides, reason) => { const dependencies = createDependencies(overrides); await expect(warmupOpencodeRuntime(dependencies)).resolves.toMatchObject({ started: false, reason, }); expect(dependencies.start).not.toHaveBeenCalled(); }); it('does not compete with an already active runtime', async () => { const dependencies = createDependencies({ getStatus: () => ({ state: 'starting', port: 4096 }), }); await expect(warmupOpencodeRuntime(dependencies)).resolves.toEqual({ started: false, reason: 'runtime-active', }); expect(dependencies.getActiveProject).not.toHaveBeenCalled(); expect(dependencies.start).not.toHaveBeenCalled(); }); it('swallows startup errors and leaves lazy Chat startup available', async () => { const error = new Error('runtime failed'); const onError = vi.fn(); const dependencies = createDependencies({ start: vi.fn(async () => { throw error; }), onError, }); await expect(warmupOpencodeRuntime(dependencies)).resolves.toMatchObject({ started: false, reason: 'start-failed', error, }); expect(onError).toHaveBeenCalledWith(error, 'start'); }); });