import { exportAccountArchive, importAccountArchive, stableJsonStringify, type PortablePayload, } from '../src/features/data-portability'; import { sha256Hex } from '../src/features/data-portability/canonical-json'; const OWNER = '4f642f87-2d12-4dcf-9aa1-f4427d4078a8'; function payload(): PortablePayload { return { format: 'd3ro-account-portability', schema_version: 1, exported_at: '2026-08-21T00:00:00.000Z', owner_id: OWNER, source: 'cloud', account: { profile: null, settings: null, subscription: null }, datasets: { dictionary: [], history: [], meetings: [], transcripts: [], meeting_memos: [], meeting_documents: [], custom_instructions: [], }, exclusions: [ 'raw_audio', 'storage_objects', 'payment_credentials', 'push_tokens', ], }; } function client( rpcImpl: ( name: string, args?: Record, ) => Promise<{ data: unknown; error: unknown; }>, ): any { return { auth: { getUser: jest.fn(async () => ({ data: { user: { id: OWNER } }, error: null, })), }, rpc: jest.fn(rpcImpl), }; } describe('account portability service contracts', () => { it('verifies the server export checksum and row count', async () => { const canonical = stableJsonStringify(payload()); const fake = client(async name => ({ data: name === 'export_account_portability' ? [ { canonical_payload: canonical, checksum: sha256Hex(canonical), exported_at: payload().exported_at, row_count: 0, }, ] : null, error: null, })); const result = await exportAccountArchive(OWNER, fake); expect(result.rowCount).toBe(0); expect(result.archive.checksum).toBe(sha256Hex(canonical)); expect(fake.rpc).toHaveBeenCalledWith('export_account_portability'); }); it('does not call restore RPC when a portable file is corrupted', async () => { const fake = client(async () => ({ data: null, error: null })); const invalid = JSON.stringify({ format: 'd3ro-account-portability', schema_version: 1, checksum_algorithm: 'sha256', checksum: '0'.repeat(64), canonical_payload: stableJsonStringify(payload()), }); await expect( importAccountArchive( OWNER, invalid, 'backup.json', 'application/json', fake, ), ).rejects.toMatchObject({ code: 'checksum' }); expect(fake.rpc).not.toHaveBeenCalled(); }); it('sends only canonical payload and checksum to the atomic restore RPC', async () => { const canonical = stableJsonStringify(payload()); const archive = JSON.stringify({ format: 'd3ro-account-portability', schema_version: 1, checksum_algorithm: 'sha256', checksum: sha256Hex(canonical), canonical_payload: canonical, }); const fake = client(async (name, args) => ({ data: name === 'restore_account_portability' ? { status: 'imported', checksum: args?.supplied_checksum, imported_rows: 0, skipped_rows: 0, imported_at: '2026-08-21T00:01:00.000Z', } : null, error: null, })); const result = await importAccountArchive( OWNER, archive, 'backup.json', 'application/json', fake, ); expect(result.result.status).toBe('imported'); expect(fake.rpc).toHaveBeenCalledWith('restore_account_portability', { canonical_payload: canonical, supplied_checksum: sha256Hex(canonical), }); }); it('refuses a mismatched active session before export', async () => { const fake = client(async () => ({ data: null, error: null })); fake.auth.getUser.mockResolvedValue({ data: { user: { id: '1d30574e-5da7-4b22-bf4a-028fd0084962' } }, error: null, }); await expect(exportAccountArchive(OWNER, fake)).rejects.toMatchObject({ code: 'auth', }); expect(fake.rpc).not.toHaveBeenCalled(); }); });