Files
makelore/tests/unit/user-sync-contract.test.ts
2026-07-29 17:22:35 +08:00

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: 'nitu-pm-test',
displayName: 'PM',
templateRoleId: 'pm',
description: 'Reusable partner',
prompt: 'Reusable prompt',
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',
} as never);
expect(result).toEqual({
id: 'nitu-pm-test',
displayName: 'PM',
templateRoleId: 'pm',
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: 'nitu-pm-test',
display_name: 'PM',
template_role_id: 'pm',
skill_ids: ['pm-project-plan'],
file_path: 'D:/repo/.opencode/agent/nitu-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: 'nitu-pm-test',
displayName: 'PM',
templateRoleId: 'pm',
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: 'nitu-pm-test',
displayName: 'PM',
templateRoleId: 'pm',
skillIds: ['pm-project-plan'],
createdAt: '2026-07-04T00:00:00.000Z',
updatedAt: '2026-07-04T00:00:00.000Z',
})).toEqual({
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',
});
});
});