Files
makelore/tests/unit/provider-routes.test.ts

292 lines
10 KiB
TypeScript

import { EventEmitter } from 'node:events';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
handleProviderRoutes,
importCurrentUserModelConfig,
normalizeImportedUserModelConfig,
} from '@electron/api/routes/providers';
import type { ProviderAccount } from '@electron/shared/providers/types';
const providerServiceMock = vi.hoisted(() => ({
getAccount: vi.fn(),
getAccountApiKey: vi.fn(),
getDefaultAccountId: vi.fn(),
updateAccount: vi.fn(),
setDefaultAccount: vi.fn(),
}));
const proxyAwareFetchMock = vi.hoisted(() => vi.fn());
vi.mock('@electron/services/providers/provider-service', () => ({
getProviderService: () => providerServiceMock,
}));
vi.mock('@electron/utils/proxy-fetch', () => ({
proxyAwareFetch: (...args: unknown[]) => proxyAwareFetchMock(...args),
}));
function createRequest(method: string, body?: unknown): IncomingMessage {
const request = new EventEmitter();
Object.assign(request, {
method,
headers: body === undefined ? {} : { 'content-type': 'application/json' },
[Symbol.asyncIterator]: async function* () {
if (body !== undefined) yield Buffer.from(JSON.stringify(body));
},
});
return request as IncomingMessage;
}
function createResponse() {
const chunks: string[] = [];
const response = {
statusCode: 0,
setHeader: vi.fn(),
end: vi.fn((chunk?: string) => {
if (chunk) chunks.push(chunk);
}),
} as unknown as ServerResponse;
return {
response,
get statusCode() { return response.statusCode; },
json: () => JSON.parse(chunks.join('')) as unknown,
};
}
function account(overrides: Partial<ProviderAccount> = {}): ProviderAccount {
return {
id: 'account-1',
vendorId: 'openai',
label: 'OpenAI',
authMode: 'api_key',
enabled: true,
isDefault: true,
createdAt: '2026-08-24T00:00:00.000Z',
updatedAt: '2026-08-24T00:00:00.000Z',
...overrides,
};
}
describe('provider host api routes', () => {
const markProviderStale = vi.fn();
const context = {
codingProducts: { conversations: { markProviderStale } },
} as never;
beforeEach(() => {
vi.clearAllMocks();
providerServiceMock.getAccount.mockResolvedValue(account());
providerServiceMock.getAccountApiKey.mockResolvedValue('stored-key');
providerServiceMock.updateAccount.mockResolvedValue(account({
updatedAt: '2026-08-24T01:00:00.000Z',
}));
providerServiceMock.getDefaultAccountId.mockResolvedValue('account-1');
providerServiceMock.setDefaultAccount.mockResolvedValue(undefined);
});
it('normalizes optional Works model capabilities before storing the account', () => {
expect(normalizeImportedUserModelConfig({
label: 'Makelore Models',
base_url: 'https://gateway.test/v1',
api_key: 'gateway-key',
credential_mode: 'works_square_ai_gateway',
models: ['deepseek/deepseek-v4-pro', 'qwen3.8-max'],
model_capabilities: {
'deepseek/deepseek-v4-pro': {
reasoning_efforts: ['max', 'low', 'medium', 'low'],
reasoning_can_disable: true,
},
'qwen3.8-max': {
reasoning_efforts: ['high'],
reasoning_can_disable: false,
},
'not-provisioned': {
reasoning_efforts: ['low'],
reasoning_can_disable: true,
},
},
})).toMatchObject({
models: ['deepseek-v4-pro', 'qwen3.8-max'],
modelCapabilities: {
'deepseek-v4-pro': {
reasoningEfforts: ['low', 'max'],
reasoningCanDisable: true,
},
'qwen3.8-max': {
reasoningEfforts: ['high'],
reasoningCanDisable: false,
},
},
});
});
it('preserves exact v2 public aliases and native effort values on import', () => {
const config = normalizeImportedUserModelConfig({
base_url: 'https://gateway.test/v1', api_key: 'test', models: ['deepseek/public-model'],
model_capabilities_v2: { schema_version: 2, models: {
'deepseek/public-model': { input_modalities: ['text', 'image'],
reasoning: { supported: true, can_disable: true, effort_values: ['medium', 'xhigh', 'new-native'], control_format: 'qwen' } },
'not-authorized': { input_modalities: ['image'] },
} },
});
expect(config.models).toEqual(['deepseek/public-model']);
expect(Object.keys(config.modelCapabilitiesV2!.models)).toEqual(['deepseek/public-model']);
expect(config.modelCapabilitiesV2!.models['deepseek/public-model']!.reasoning.effortValues)
.toEqual(['medium', 'xhigh', 'new-native']);
});
it('persists server capabilities and invalidates the runtime when they change', async () => {
const existing = account({
id: 'niancode-user-models',
vendorId: 'custom',
model: 'deepseek-v4-pro',
metadata: {
customModels: ['deepseek-v4-pro'],
worksSquareModelCapabilities: {
'deepseek-v4-pro': {
reasoningEfforts: ['high'],
reasoningCanDisable: false,
},
},
},
});
providerServiceMock.getAccount.mockResolvedValue(existing);
providerServiceMock.updateAccount.mockImplementation(async (_accountId: string, patch: Partial<ProviderAccount>) => ({
...existing,
...patch,
}));
proxyAwareFetchMock.mockResolvedValue(new Response(JSON.stringify({
label: 'Makelore Models',
base_url: 'https://gateway.test/v1',
api_key: 'gateway-key',
credential_mode: 'api_key',
models: ['deepseek/deepseek-v4-pro'],
model_capabilities: {
'deepseek/deepseek-v4-pro': {
reasoning_efforts: ['low', 'high', 'max'],
reasoning_can_disable: true,
},
},
model_capabilities_v2: { schema_version: 2, models: { 'deepseek/deepseek-v4-pro': {
input_modalities: ['text'], reasoning: { supported: true, can_disable: true, effort_values: ['xhigh'] },
} } },
}), { status: 200, headers: { 'content-type': 'application/json' } }));
const imported = await importCurrentUserModelConfig(context, 'access-token');
expect(imported.account.metadata?.worksSquareModelCapabilitiesV2?.models['deepseek/deepseek-v4-pro']?.reasoning.effortValues).toEqual(['xhigh']);
expect(imported.account.metadata?.worksSquareModelCapabilities).toEqual({
'deepseek-v4-pro': {
reasoningEfforts: ['low', 'high', 'max'],
reasoningCanDisable: true,
},
});
expect(providerServiceMock.updateAccount).toHaveBeenCalledWith(
'niancode-user-models',
expect.objectContaining({
metadata: expect.objectContaining({
worksSquareModelCapabilities: {
'deepseek-v4-pro': {
reasoningEfforts: ['low', 'high', 'max'],
reasoningCanDisable: true,
},
},
}),
}),
'gateway-key',
);
expect(markProviderStale).toHaveBeenCalledTimes(1);
});
it('clears a previous server capability override when the optional field is absent', async () => {
const existing = account({
id: 'niancode-user-models',
vendorId: 'custom',
model: 'deepseek-v4-pro',
metadata: {
customModels: ['deepseek-v4-pro'],
worksSquareModelCapabilities: {
'deepseek-v4-pro': {
reasoningEfforts: ['max'],
reasoningCanDisable: true,
},
},
},
});
providerServiceMock.getAccount.mockResolvedValue(existing);
providerServiceMock.updateAccount.mockImplementation(async (_accountId: string, patch: Partial<ProviderAccount>) => ({
...existing,
...patch,
}));
proxyAwareFetchMock.mockResolvedValue(new Response(JSON.stringify({
base_url: 'https://gateway.test/v1',
api_key: 'gateway-key',
models: ['deepseek/deepseek-v4-pro'],
}), { status: 200, headers: { 'content-type': 'application/json' } }));
const imported = await importCurrentUserModelConfig(context, 'access-token');
expect(imported.account.metadata).not.toHaveProperty('worksSquareModelCapabilities');
expect(providerServiceMock.updateAccount).toHaveBeenCalledWith(
'niancode-user-models',
expect.objectContaining({
metadata: expect.not.objectContaining({ worksSquareModelCapabilities: expect.anything() }),
}),
'gateway-key',
);
});
it('marks Pi provider input stale after an account credential update', async () => {
const result = createResponse();
const handled = await handleProviderRoutes(
createRequest('PUT', { updates: {}, apiKey: 'sk-new' }),
result.response,
new URL('http://127.0.0.1/api/provider-accounts/account-1'),
context,
);
expect(handled).toBe(true);
expect(result.statusCode).toBe(200);
expect(providerServiceMock.updateAccount).toHaveBeenCalledWith('account-1', {}, 'sk-new');
expect(markProviderStale).toHaveBeenCalledTimes(1);
});
it('does not invalidate provider input for an unchanged account', async () => {
const result = createResponse();
await handleProviderRoutes(
createRequest('PUT', { updates: {} }),
result.response,
new URL('http://127.0.0.1/api/provider-accounts/account-1'),
context,
);
expect(result.statusCode).toBe(200);
expect(result.json()).toMatchObject({ success: true, noChange: true });
expect(providerServiceMock.updateAccount).not.toHaveBeenCalled();
expect(markProviderStale).not.toHaveBeenCalled();
});
it('invalidates provider input only when the default account changes', async () => {
const unchanged = createResponse();
await handleProviderRoutes(
createRequest('PUT', { accountId: 'account-1' }),
unchanged.response,
new URL('http://127.0.0.1/api/provider-accounts/default'),
context,
);
expect(markProviderStale).not.toHaveBeenCalled();
providerServiceMock.getDefaultAccountId.mockResolvedValue('account-2');
const changed = createResponse();
await handleProviderRoutes(
createRequest('PUT', { accountId: 'account-1' }),
changed.response,
new URL('http://127.0.0.1/api/provider-accounts/default'),
context,
);
expect(providerServiceMock.setDefaultAccount).toHaveBeenCalledWith('account-1');
expect(markProviderStale).toHaveBeenCalledTimes(1);
});
});