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', }); }); });