202 lines
6.4 KiB
TypeScript
202 lines
6.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
normalizeUserSyncBootstrap,
|
|
toServerUserSyncPartner,
|
|
toServerUserSyncProviderAccount,
|
|
toUserSyncPartner,
|
|
toUserSyncPreferences,
|
|
toUserSyncProviderAccount,
|
|
} from '../../shared/user-sync';
|
|
|
|
describe('user sync contract sanitizers', () => {
|
|
it('keeps only portable preferences', () => {
|
|
const result = toUserSyncPreferences({
|
|
theme: 'dark',
|
|
language: 'zh-CN',
|
|
sidebarCollapsed: true,
|
|
devModeUnlocked: true,
|
|
launchAtStartup: true,
|
|
setupComplete: true,
|
|
activeProjectId: 'prj_1',
|
|
proxyServer: 'http://proxy.example.com',
|
|
} as never, 'default-account');
|
|
|
|
expect(result).toEqual({
|
|
theme: 'dark',
|
|
language: 'zh-CN',
|
|
sidebarCollapsed: true,
|
|
devModeUnlocked: true,
|
|
defaultProviderAccountId: 'default-account',
|
|
});
|
|
});
|
|
|
|
it('keeps provider metadata but excludes secret and header fields', () => {
|
|
const result = toUserSyncProviderAccount({
|
|
id: 'openai-account-1',
|
|
vendorId: 'openai',
|
|
label: 'OpenAI',
|
|
authMode: 'api_key',
|
|
baseUrl: 'https://api.openai.com/v1',
|
|
apiProtocol: 'openai-responses',
|
|
headers: { Authorization: 'Bearer secret' },
|
|
model: 'gpt-4.1-mini',
|
|
fallbackModels: ['gpt-4o-mini'],
|
|
fallbackAccountIds: ['fallback-account'],
|
|
enabled: true,
|
|
isDefault: true,
|
|
apiKey: 'sk-should-not-sync',
|
|
refreshToken: 'refresh-should-not-sync',
|
|
metadata: { email: 'user@example.com' },
|
|
createdAt: '2026-07-04T00:00:00.000Z',
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
} as never);
|
|
|
|
expect(result).toEqual({
|
|
id: 'openai-account-1',
|
|
vendorId: 'openai',
|
|
label: 'OpenAI',
|
|
authMode: 'api_key',
|
|
baseUrl: 'https://api.openai.com/v1',
|
|
apiProtocol: 'openai-responses',
|
|
model: 'gpt-4.1-mini',
|
|
fallbackModels: ['gpt-4o-mini'],
|
|
fallbackAccountIds: ['fallback-account'],
|
|
enabled: true,
|
|
isDefault: true,
|
|
metadata: { email: 'user@example.com' },
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
});
|
|
expect(JSON.stringify(result)).not.toContain('sk-should-not-sync');
|
|
expect(JSON.stringify(result)).not.toContain('refresh-should-not-sync');
|
|
expect(JSON.stringify(result)).not.toContain('Authorization');
|
|
});
|
|
|
|
it('keeps global partner metadata but excludes project runtime fields', () => {
|
|
const result = toUserSyncPartner({
|
|
id: 'makelore-pm-test',
|
|
displayName: 'PM',
|
|
templateRoleId: 'custom',
|
|
description: 'Reusable partner',
|
|
prompt: 'Reusable prompt',
|
|
skillIds: ['pm-project-plan'],
|
|
filePath: 'D:/repo/.opencode/agent/makelore-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',
|
|
} as never);
|
|
|
|
expect(result).toEqual({
|
|
id: 'makelore-pm-test',
|
|
displayName: 'PM',
|
|
templateRoleId: 'custom',
|
|
description: 'Reusable partner',
|
|
prompt: 'Reusable prompt',
|
|
skillIds: ['pm-project-plan'],
|
|
createdAt: '2026-07-04T00:00:00.000Z',
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
});
|
|
expect(JSON.stringify(result)).not.toContain('filePath');
|
|
expect(JSON.stringify(result)).not.toContain('projectRuntimeConfigsByProjectId');
|
|
});
|
|
|
|
it('normalizes backend snake_case bootstrap into desktop camelCase records', () => {
|
|
const result = normalizeUserSyncBootstrap({
|
|
preferences: {
|
|
theme: 'dark',
|
|
language: 'zh-CN',
|
|
sidebar_collapsed: true,
|
|
launch_at_startup: true,
|
|
},
|
|
provider_accounts: [{
|
|
id: 'openai-account-1',
|
|
vendor_id: 'openai',
|
|
label: 'OpenAI',
|
|
auth_mode: 'api_key',
|
|
fallback_models: ['gpt-4o-mini'],
|
|
fallback_account_ids: ['fallback-account'],
|
|
api_key: 'sk-should-not-sync',
|
|
updated_at: '2026-07-04T00:00:00.000Z',
|
|
}],
|
|
partners: [{
|
|
id: 'makelore-pm-test',
|
|
display_name: 'PM',
|
|
template_role_id: 'custom',
|
|
skill_ids: ['pm-project-plan'],
|
|
file_path: 'D:/repo/.opencode/agent/makelore-pm-test.md',
|
|
created_at: '2026-07-04T00:00:00.000Z',
|
|
updated_at: '2026-07-04T00:00:00.000Z',
|
|
}],
|
|
usage_aggregates: [],
|
|
projects: [{ id: 'prj_1' }],
|
|
});
|
|
|
|
expect(result.preferences).toEqual({ theme: 'dark', language: 'zh-CN', sidebarCollapsed: true });
|
|
expect(result.providerAccounts).toEqual([{
|
|
id: 'openai-account-1',
|
|
vendorId: 'openai',
|
|
label: 'OpenAI',
|
|
authMode: 'api_key',
|
|
fallbackModels: ['gpt-4o-mini'],
|
|
fallbackAccountIds: ['fallback-account'],
|
|
enabled: true,
|
|
isDefault: false,
|
|
metadata: {},
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
}]);
|
|
expect(result.partners).toEqual([{
|
|
id: 'makelore-pm-test',
|
|
displayName: 'PM',
|
|
templateRoleId: 'custom',
|
|
skillIds: ['pm-project-plan'],
|
|
createdAt: '2026-07-04T00:00:00.000Z',
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
}]);
|
|
expect(JSON.stringify(result)).not.toContain('prj_1');
|
|
expect(JSON.stringify(result)).not.toContain('sk-should-not-sync');
|
|
expect(JSON.stringify(result)).not.toContain('file_path');
|
|
});
|
|
|
|
it('converts sanitized desktop records to server snake_case payloads', () => {
|
|
expect(toServerUserSyncProviderAccount({
|
|
id: 'openai-account-1',
|
|
vendorId: 'openai',
|
|
label: 'OpenAI',
|
|
authMode: 'api_key',
|
|
fallbackModels: ['gpt-4o-mini'],
|
|
fallbackAccountIds: ['fallback-account'],
|
|
enabled: true,
|
|
isDefault: true,
|
|
metadata: {},
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
})).toEqual({
|
|
id: 'openai-account-1',
|
|
vendor_id: 'openai',
|
|
label: 'OpenAI',
|
|
auth_mode: 'api_key',
|
|
fallback_models: ['gpt-4o-mini'],
|
|
fallback_account_ids: ['fallback-account'],
|
|
enabled: true,
|
|
is_default: true,
|
|
metadata: {},
|
|
updated_at: '2026-07-04T00:00:00.000Z',
|
|
});
|
|
|
|
expect(toServerUserSyncPartner({
|
|
id: 'makelore-pm-test',
|
|
displayName: 'PM',
|
|
templateRoleId: 'custom',
|
|
skillIds: ['pm-project-plan'],
|
|
createdAt: '2026-07-04T00:00:00.000Z',
|
|
updatedAt: '2026-07-04T00:00:00.000Z',
|
|
})).toEqual({
|
|
id: 'makelore-pm-test',
|
|
display_name: 'PM',
|
|
template_role_id: 'custom',
|
|
skill_ids: ['pm-project-plan'],
|
|
created_at: '2026-07-04T00:00:00.000Z',
|
|
updated_at: '2026-07-04T00:00:00.000Z',
|
|
});
|
|
});
|
|
});
|