Makelore 2.0 initial clean snapshot
This commit is contained in:
239
tests/unit/user-sync-routes.test.ts
Normal file
239
tests/unit/user-sync-routes.test.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { IncomingMessage, ServerResponse } from 'http';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { handleUserSyncRoutes } from '@electron/api/routes/user-sync';
|
||||
|
||||
const fetchMock = vi.hoisted(() => vi.fn());
|
||||
const providerServiceMock = vi.hoisted(() => ({
|
||||
upsertSyncedAccountMetadata: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/proxy-fetch', () => ({
|
||||
proxyAwareFetch: (...args: unknown[]) => fetchMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock('@electron/services/providers/provider-service', () => ({
|
||||
getProviderService: () => providerServiceMock,
|
||||
}));
|
||||
|
||||
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,
|
||||
headers: Record<string, string> = {},
|
||||
): IncomingMessage {
|
||||
const req = new EventEmitter();
|
||||
Object.assign(req, {
|
||||
method,
|
||||
headers: body === undefined
|
||||
? headers
|
||||
: { ...headers, 'content-type': 'application/json' },
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
if (body !== undefined) {
|
||||
yield Buffer.from(JSON.stringify(body));
|
||||
}
|
||||
},
|
||||
});
|
||||
return req as IncomingMessage;
|
||||
}
|
||||
|
||||
describe('user sync host api routes', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.restoreAllMocks();
|
||||
providerServiceMock.upsertSyncedAccountMetadata.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it('requires a current user access token', async () => {
|
||||
const response = createResponse();
|
||||
|
||||
const handled = await handleUserSyncRoutes(
|
||||
createRequest('GET'),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/user-sync/bootstrap'),
|
||||
{} as never,
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(response.statusCode).toBe(400);
|
||||
expect(response.json()).toEqual({
|
||||
success: false,
|
||||
error: 'Missing x-niancode-access-token',
|
||||
});
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fetches bootstrap, applies provider metadata, and strips secrets from the response', async () => {
|
||||
fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
preferences: {
|
||||
theme: 'dark',
|
||||
sidebar_collapsed: true,
|
||||
},
|
||||
provider_accounts: [{
|
||||
id: 'openai-account-1',
|
||||
vendor_id: 'openai',
|
||||
label: 'OpenAI',
|
||||
auth_mode: 'api_key',
|
||||
model: 'gpt-4.1-mini',
|
||||
fallback_models: ['gpt-4o-mini'],
|
||||
enabled: true,
|
||||
is_default: true,
|
||||
api_key: 'sk-should-not-return',
|
||||
refresh_token: 'refresh-should-not-return',
|
||||
headers: { Authorization: 'Bearer secret' },
|
||||
updated_at: '2026-07-04T00:00:00.000Z',
|
||||
}],
|
||||
partners: [],
|
||||
usage_aggregates: [],
|
||||
}), { status: 200 }));
|
||||
const response = createResponse();
|
||||
|
||||
const handled = await handleUserSyncRoutes(
|
||||
createRequest('GET', undefined, { 'x-niancode-access-token': 'access-token' }),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/user-sync/bootstrap'),
|
||||
{} as never,
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json()).toEqual({
|
||||
success: true,
|
||||
bootstrap: {
|
||||
preferences: {
|
||||
theme: 'dark',
|
||||
sidebarCollapsed: true,
|
||||
},
|
||||
providerAccounts: [{
|
||||
id: 'openai-account-1',
|
||||
vendorId: 'openai',
|
||||
label: 'OpenAI',
|
||||
authMode: 'api_key',
|
||||
model: 'gpt-4.1-mini',
|
||||
fallbackModels: ['gpt-4o-mini'],
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
metadata: {},
|
||||
updatedAt: '2026-07-04T00:00:00.000Z',
|
||||
}],
|
||||
partners: [],
|
||||
usageAggregates: [],
|
||||
},
|
||||
applied: { providerAccounts: 1 },
|
||||
});
|
||||
expect(JSON.stringify(response.json())).not.toContain('sk-should-not-return');
|
||||
expect(JSON.stringify(response.json())).not.toContain('refresh-should-not-return');
|
||||
expect(JSON.stringify(response.json())).not.toContain('Authorization');
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://square.nianxx.cn/api/me/bootstrap', {
|
||||
method: 'GET',
|
||||
headers: { Authorization: 'Bearer access-token' },
|
||||
});
|
||||
expect(providerServiceMock.upsertSyncedAccountMetadata).toHaveBeenCalledWith({
|
||||
id: 'openai-account-1',
|
||||
vendorId: 'openai',
|
||||
label: 'OpenAI',
|
||||
authMode: 'api_key',
|
||||
model: 'gpt-4.1-mini',
|
||||
fallbackModels: ['gpt-4o-mini'],
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
metadata: {},
|
||||
updatedAt: '2026-07-04T00:00:00.000Z',
|
||||
});
|
||||
});
|
||||
|
||||
it('proxies preferences as sanitized server payloads', async () => {
|
||||
fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
preferences: { theme: 'dark' },
|
||||
provider_accounts: [],
|
||||
partners: [],
|
||||
usage_aggregates: [],
|
||||
}), { status: 200 }));
|
||||
const response = createResponse();
|
||||
|
||||
const handled = await handleUserSyncRoutes(
|
||||
createRequest('PUT', {
|
||||
theme: 'dark',
|
||||
language: 'zh-CN',
|
||||
launchAtStartup: true,
|
||||
activeProjectId: 'prj_1',
|
||||
}, { 'x-niancode-access-token': 'access-token' }),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/user-sync/preferences'),
|
||||
{} as never,
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://square.nianxx.cn/api/me/preferences', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Authorization: 'Bearer access-token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ theme: 'dark', language: 'zh-CN' }),
|
||||
});
|
||||
});
|
||||
|
||||
it('proxies partners as sanitized server payloads', async () => {
|
||||
fetchMock.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||
preferences: {},
|
||||
provider_accounts: [],
|
||||
partners: [],
|
||||
usage_aggregates: [],
|
||||
}), { status: 200 }));
|
||||
const response = createResponse();
|
||||
|
||||
const handled = await handleUserSyncRoutes(
|
||||
createRequest('POST', {
|
||||
id: 'nitu-pm-test',
|
||||
displayName: 'PM',
|
||||
templateRoleId: 'pm',
|
||||
skillIds: ['pm-project-plan'],
|
||||
filePath: 'D:/repo/.opencode/agent/nitu-pm-test.md',
|
||||
projectRuntimeConfigsByProjectId: { prj_1: { filePath: 'D:/repo/file.md' } },
|
||||
createdAt: '2026-07-04T00:00:00.000Z',
|
||||
updatedAt: '2026-07-04T00:00:00.000Z',
|
||||
}, { 'x-niancode-access-token': 'access-token' }),
|
||||
response.res,
|
||||
new URL('http://127.0.0.1/api/user-sync/partners'),
|
||||
{} as never,
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(fetchMock).toHaveBeenCalledWith('https://square.nianxx.cn/api/me/partners', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer access-token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: 'nitu-pm-test',
|
||||
display_name: 'PM',
|
||||
template_role_id: 'pm',
|
||||
skill_ids: ['pm-project-plan'],
|
||||
created_at: '2026-07-04T00:00:00.000Z',
|
||||
updated_at: '2026-07-04T00:00:00.000Z',
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user