import { EventEmitter } from 'node:events'; import type { IncomingMessage, ServerResponse } from 'http'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { handleProviderRoutes } from '@electron/api/routes/providers'; import type { ProviderAccount } from '@electron/shared/providers/types'; import { clearWorksSquareAIGatewayCredential, getWorksSquareAIGatewaySnapshot, } from '@electron/services/works-square-ai-gateway'; const providerServiceMock = vi.hoisted(() => ({ getAccount: vi.fn(), getAccountApiKey: vi.fn(), createAccount: vi.fn(), updateAccount: vi.fn(), setDefaultAccount: vi.fn(), })); const getHostApiTokenMock = vi.hoisted(() => vi.fn()); vi.mock('@electron/services/providers/provider-service', () => ({ getProviderService: () => providerServiceMock, })); vi.mock('@electron/api/server', () => ({ getHostApiToken: () => getHostApiTokenMock(), })); function createResponse() { const chunks: string[] = []; const res = { statusCode: 0, setHeader: vi.fn(), end: vi.fn((chunk?: string) => { if (chunk) chunks.push(chunk); }), } as unknown as ServerResponse; return { res, get statusCode() { return res.statusCode; }, json: () => JSON.parse(chunks.join('')) as unknown, }; } function createRequest(method: string, body?: unknown): IncomingMessage { const req = new EventEmitter(); Object.assign(req, { method, headers: body === undefined ? {} : { 'content-type': 'application/json' }, [Symbol.asyncIterator]: async function* () { if (body !== undefined) { yield Buffer.from(JSON.stringify(body)); } }, }); return req as IncomingMessage; } function createProviderAccount(overrides: Partial = {}): ProviderAccount { const now = '2026-06-19T00:00:00.000Z'; return { id: 'openai-account-1', vendorId: 'openai', label: 'OpenAI', authMode: 'api_key', enabled: true, isDefault: true, createdAt: now, updatedAt: now, ...overrides, }; } describe('provider host api routes', () => { beforeEach(() => { vi.clearAllMocks(); vi.restoreAllMocks(); providerServiceMock.getAccount.mockResolvedValue(createProviderAccount()); providerServiceMock.createAccount.mockImplementation(async (account: ProviderAccount) => ( createProviderAccount(account) )); providerServiceMock.updateAccount.mockResolvedValue(createProviderAccount({ updatedAt: '2026-06-19T01:00:00.000Z', })); providerServiceMock.setDefaultAccount.mockResolvedValue(undefined); providerServiceMock.getAccountApiKey.mockResolvedValue(null); getHostApiTokenMock.mockReturnValue('host-api-token'); clearWorksSquareAIGatewayCredential(); }); it('restarts a running opencode runtime after updating an account api key', async () => { const response = createResponse(); const restart = vi.fn(async () => ({ state: 'running', port: 4096, pid: 4242 })); const handled = await handleProviderRoutes( createRequest('PUT', { updates: {}, apiKey: 'sk-new' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/openai-account-1'), { opencodeManager: { getStatus: () => ({ state: 'running', port: 4096 }), restart, }, } as never, ); expect(handled).toBe(true); expect(response.statusCode).toBe(200); expect(response.json()).toEqual({ success: true, account: createProviderAccount({ updatedAt: '2026-06-19T01:00:00.000Z', }), }); expect(providerServiceMock.updateAccount).toHaveBeenCalledWith( 'openai-account-1', {}, 'sk-new', ); expect(restart).toHaveBeenCalledOnce(); }); it('imports current user Works Square model config into the provider store without returning the api key', async () => { const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 900, models: ['gpt-4.1-mini', 'gpt-4o-mini'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); providerServiceMock.getAccount.mockResolvedValueOnce(null); const response = createResponse(); const restart = vi.fn(async () => ({ state: 'running', port: 4096, pid: 4242 })); const handled = await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'running', port: 4096 }), restart, }, } as never, ); expect(handled).toBe(true); expect(response.statusCode).toBe(200); expect(response.json()).toEqual({ success: true, account: expect.objectContaining({ id: 'niancode-user-models', vendorId: 'custom', label: 'Makelore Models', baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', model: 'gpt-4.1-mini', fallbackModels: ['gpt-4o-mini'], }), importedModels: ['gpt-4.1-mini', 'gpt-4o-mini'], }); expect(JSON.stringify(response.json())).not.toContain('ws-ai-token'); expect(fetchMock).toHaveBeenCalledWith( 'https://square.nianxx.cn/api/auth/me/model-config', { method: 'GET', headers: { Authorization: 'Bearer access-token', }, }, ); expect(providerServiceMock.createAccount).toHaveBeenCalledWith( expect.objectContaining({ id: 'niancode-user-models', vendorId: 'custom', label: 'Makelore Models', authMode: 'api_key', baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', apiProtocol: 'openai-completions', model: 'gpt-4.1-mini', fallbackModels: ['gpt-4o-mini'], headers: undefined, metadata: expect.objectContaining({ customModels: ['gpt-4.1-mini', 'gpt-4o-mini'], worksSquareCredentialMode: 'works_square_ai_gateway_proxy', worksSquareOneApiBaseUrl: 'https://one-api.example.com/v1', }), enabled: true, isDefault: true, }), 'host-api-token', ); expect(getWorksSquareAIGatewaySnapshot()).toMatchObject({ accessToken: 'ws-ai-token', oneApiBaseUrl: 'https://one-api.example.com/v1', }); expect(providerServiceMock.setDefaultAccount).toHaveBeenCalledWith('niancode-user-models'); expect(restart).toHaveBeenCalledOnce(); }); it('normalizes a root Works Square AI gateway URL to the One API v1 endpoint', async () => { const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://token.nianxx.cn/', api_key: 'ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 900, models: ['deepseek-chat'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); providerServiceMock.getAccount.mockResolvedValueOnce(null); const response = createResponse(); const handled = await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'stopped', port: 4096 }), restart: vi.fn(), }, } as never, ); expect(handled).toBe(true); expect(response.statusCode).toBe(200); expect(providerServiceMock.createAccount).toHaveBeenCalledWith( expect.objectContaining({ baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', headers: undefined, metadata: expect.objectContaining({ worksSquareCredentialMode: 'works_square_ai_gateway_proxy', worksSquareOneApiBaseUrl: 'https://token.nianxx.cn/v1', }), }), 'host-api-token', ); }); it('strips DeepSeek routing prefixes when importing Works Square models', async () => { const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 900, models: ['deepseek/deepseek-v4-pro', 'deepseek/deepseek-chat'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); providerServiceMock.getAccount.mockResolvedValueOnce(null); const response = createResponse(); const handled = await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'stopped', port: 4096 }), restart: vi.fn(), }, } as never, ); expect(handled).toBe(true); expect(response.statusCode).toBe(200); expect(response.json()).toMatchObject({ success: true, importedModels: ['deepseek-v4-pro', 'deepseek-chat'], }); expect(providerServiceMock.createAccount).toHaveBeenCalledWith( expect.objectContaining({ model: 'deepseek-v4-pro', fallbackModels: ['deepseek-chat'], metadata: expect.objectContaining({ customModels: ['deepseek-v4-pro', 'deepseek-chat'], worksSquareCredentialMode: 'works_square_ai_gateway_proxy', }), }), 'host-api-token', ); }); it('does not restart a running runtime when importing the same local proxy provider shape', async () => { const existingAccount = createProviderAccount({ id: 'niancode-user-models', vendorId: 'custom', label: 'Makelore Models', authMode: 'api_key', baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', apiProtocol: 'openai-completions', model: 'gpt-4.1-mini', fallbackModels: ['gpt-4o-mini'], enabled: true, isDefault: true, metadata: { customModels: ['gpt-4.1-mini', 'gpt-4o-mini'], worksSquareCredentialMode: 'works_square_ai_gateway_proxy', worksSquareOneApiBaseUrl: 'https://one-api.example.com/v1', }, }); providerServiceMock.getAccount.mockResolvedValueOnce(existingAccount); providerServiceMock.getAccountApiKey.mockResolvedValueOnce('host-api-token'); providerServiceMock.updateAccount.mockResolvedValueOnce(existingAccount); const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'fresh-ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 3600, models: ['gpt-4.1-mini', 'gpt-4o-mini'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); const response = createResponse(); const restart = vi.fn(async () => ({ state: 'running', port: 4096, pid: 4242 })); await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'running', port: 4096 }), restart, }, } as never, ); expect(response.statusCode).toBe(200); expect(restart).not.toHaveBeenCalled(); }); it('keeps the previously selected imported model when refreshing the Works Square model config', async () => { const existingAccount = createProviderAccount({ id: 'niancode-user-models', vendorId: 'custom', label: 'Makelore Models', authMode: 'api_key', baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', apiProtocol: 'openai-completions', model: 'qwen3-coder', fallbackModels: ['deepseek-chat', 'gpt-4.1-mini'], enabled: true, isDefault: true, metadata: { customModels: ['deepseek-chat', 'gpt-4.1-mini', 'qwen3-coder'], worksSquareCredentialMode: 'works_square_ai_gateway_proxy', worksSquareOneApiBaseUrl: 'https://one-api.example.com/v1', }, }); providerServiceMock.getAccount.mockResolvedValueOnce(existingAccount); providerServiceMock.getAccountApiKey.mockResolvedValueOnce('host-api-token'); providerServiceMock.updateAccount.mockResolvedValueOnce(existingAccount); const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'fresh-ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 3600, models: ['deepseek-chat', 'gpt-4.1-mini', 'qwen3-coder'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); const response = createResponse(); await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'stopped', port: 4096 }), restart: vi.fn(), }, } as never, ); expect(response.statusCode).toBe(200); expect(providerServiceMock.updateAccount).toHaveBeenCalledWith( 'niancode-user-models', expect.objectContaining({ model: 'qwen3-coder', fallbackModels: ['deepseek-chat', 'gpt-4.1-mini'], }), 'host-api-token', ); }); it('restarts a running runtime when the local proxy Host API token changed', async () => { const existingAccount = createProviderAccount({ id: 'niancode-user-models', vendorId: 'custom', label: 'Makelore Models', authMode: 'api_key', baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', apiProtocol: 'openai-completions', model: 'gpt-4.1-mini', fallbackModels: ['gpt-4o-mini'], enabled: true, isDefault: true, metadata: { customModels: ['gpt-4.1-mini', 'gpt-4o-mini'], worksSquareCredentialMode: 'works_square_ai_gateway_proxy', worksSquareOneApiBaseUrl: 'https://one-api.example.com/v1', }, }); providerServiceMock.getAccount.mockResolvedValueOnce(existingAccount); providerServiceMock.getAccountApiKey.mockResolvedValueOnce('old-host-api-token'); getHostApiTokenMock.mockReturnValueOnce('new-host-api-token'); providerServiceMock.updateAccount.mockResolvedValueOnce(existingAccount); const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'fresh-ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 3600, models: ['gpt-4.1-mini', 'gpt-4o-mini'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); const response = createResponse(); const restart = vi.fn(async () => ({ state: 'running', port: 4096, pid: 4242 })); await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'running', port: 4096 }), restart, }, } as never, ); expect(response.statusCode).toBe(200); expect(providerServiceMock.updateAccount).toHaveBeenCalledWith( 'niancode-user-models', expect.objectContaining({ baseUrl: 'http://127.0.0.1:13210/api/ai-proxy/v1', metadata: expect.objectContaining({ worksSquareCredentialMode: 'works_square_ai_gateway_proxy', }), }), 'new-host-api-token', ); expect(restart).toHaveBeenCalledOnce(); }); it('returns a failure when the runtime restart fails after importing a new token', async () => { const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'fresh-ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 3600, models: ['gpt-4.1-mini'], }), { status: 200 }), ); vi.stubGlobal('fetch', fetchMock); providerServiceMock.getAccount.mockResolvedValueOnce(createProviderAccount({ id: 'niancode-user-models', model: 'old-model', })); providerServiceMock.getAccountApiKey.mockResolvedValueOnce('old-ws-ai-token'); const restart = vi.fn().mockRejectedValue(new Error('runtime restart failed')); const response = createResponse(); await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'running', port: 4096 }), restart, }, } as never, ); expect(response.statusCode).toBe(500); expect(response.json()).toEqual({ success: false, error: 'runtime restart failed', }); expect(providerServiceMock.updateAccount).toHaveBeenCalledWith( 'niancode-user-models', expect.objectContaining({ model: 'gpt-4.1-mini' }), 'host-api-token', ); expect(restart).toHaveBeenCalledOnce(); }); it('does not accept an attached server as the new provider runtime', async () => { vi.stubGlobal('fetch', vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ provider_type: 'openai-compatible', label: 'Makelore Models', base_url: 'https://one-api.example.com/v1', api_key: 'fresh-ws-ai-token', credential_mode: 'works_square_ai_gateway', api_key_expires_in: 3600, models: ['gpt-4.1-mini'], }), { status: 200 }), )); providerServiceMock.getAccount.mockResolvedValueOnce(createProviderAccount({ id: 'niancode-user-models', model: 'old-model', })); providerServiceMock.getAccountApiKey.mockResolvedValueOnce('old-ws-ai-token'); const restart = vi.fn(async () => ({ state: 'running', port: 4096 })); const response = createResponse(); await handleProviderRoutes( createRequest('POST', { accessToken: 'access-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'running', port: 4096 }), restart, }, } as never, ); expect(response.statusCode).toBe(500); expect(response.json()).toEqual({ success: false, error: 'opencode runtime did not restart with the new provider configuration', }); expect(restart).toHaveBeenCalledOnce(); }); it('forwards Works Square auth errors when importing current user model config fails', async () => { const fetchMock = vi.fn().mockResolvedValueOnce( new Response(JSON.stringify({ detail: 'Invalid token' }), { status: 401 }), ); vi.stubGlobal('fetch', fetchMock); const response = createResponse(); const handled = await handleProviderRoutes( createRequest('POST', { accessToken: 'bad-token' }), response.res, new URL('http://127.0.0.1/api/provider-accounts/import-user-model-config'), { opencodeManager: { getStatus: () => ({ state: 'stopped' }), restart: vi.fn(), }, } as never, ); expect(handled).toBe(true); expect(response.statusCode).toBe(401); expect(response.json()).toEqual({ success: false, error: 'Invalid token', }); expect(providerServiceMock.createAccount).not.toHaveBeenCalled(); expect(providerServiceMock.updateAccount).not.toHaveBeenCalled(); }); });