Makelore 2.0 initial clean snapshot
This commit is contained in:
584
tests/unit/provider-routes.test.ts
Normal file
584
tests/unit/provider-routes.test.ts
Normal file
@@ -0,0 +1,584 @@
|
||||
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> = {}): 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user