feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
122
apps/mobile-rn/__tests__/data-portability-schema.test.ts
Normal file
122
apps/mobile-rn/__tests__/data-portability-schema.test.ts
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import {
|
||||
createPortableArchive,
|
||||
parsePortableArchive,
|
||||
parsePortablePayload,
|
||||
serializePortableArchive,
|
||||
stableJsonStringify,
|
||||
type PortablePayload,
|
||||
} from '../src/features/data-portability';
|
||||
|
||||
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: 'mobile',
|
||||
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',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
describe('data portability archive schema', () => {
|
||||
it('round-trips the strict versioned archive and checksum', () => {
|
||||
const canonical = stableJsonStringify(payload());
|
||||
const archive = createPortableArchive(canonical);
|
||||
const parsed = parsePortableArchive(
|
||||
serializePortableArchive(archive),
|
||||
OWNER,
|
||||
);
|
||||
|
||||
expect(parsed.archive.checksum).toMatch(/^[0-9a-f]{64}$/);
|
||||
expect(parsed.payload).toEqual(payload());
|
||||
});
|
||||
|
||||
it('rejects a checksum-tampered payload', () => {
|
||||
const archive = createPortableArchive(stableJsonStringify(payload()));
|
||||
const tampered = JSON.stringify({
|
||||
...archive,
|
||||
canonical_payload: archive.canonical_payload.replace(
|
||||
'"source":"mobile"',
|
||||
'"source":"cloud"',
|
||||
),
|
||||
});
|
||||
|
||||
expect(() => parsePortableArchive(tampered, OWNER)).toThrow('checksum');
|
||||
});
|
||||
|
||||
it('rejects unsupported fields instead of ignoring them', () => {
|
||||
const invalid = {
|
||||
...payload(),
|
||||
storage_objects: [{ path: 'private/audio.wav' }],
|
||||
};
|
||||
expect(() => parsePortablePayload(stableJsonStringify(invalid))).toThrow(
|
||||
'unsupported fields',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a foreign owner before an RPC can run', () => {
|
||||
const foreign = {
|
||||
...payload(),
|
||||
owner_id: '1d30574e-5da7-4b22-bf4a-028fd0084962',
|
||||
};
|
||||
const archive = createPortableArchive(stableJsonStringify(foreign));
|
||||
expect(() =>
|
||||
parsePortableArchive(serializePortableArchive(archive), OWNER),
|
||||
).toThrow('different account');
|
||||
});
|
||||
|
||||
it('rejects orphan meeting children', () => {
|
||||
const invalid = payload();
|
||||
invalid.datasets.transcripts.push({
|
||||
id: '2d47b3b6-a0dc-4ce4-8cd4-37b32ef3a29c',
|
||||
meeting_id: '292a4f9d-288e-4277-aab6-b07132a80c69',
|
||||
segment_index: 0,
|
||||
timestamp_ms: 0,
|
||||
duration_ms: null,
|
||||
text: 'orphan',
|
||||
speaker: null,
|
||||
edited: false,
|
||||
created_at: '2026-08-21T00:00:00.000Z',
|
||||
updated_at: '2026-08-21T00:00:00.000Z',
|
||||
});
|
||||
expect(() => parsePortablePayload(stableJsonStringify(invalid))).toThrow(
|
||||
'without its parent',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects the same row id twice', () => {
|
||||
const dictionary = {
|
||||
id: '7e050e11-c90c-4e95-8581-d37ad687d836',
|
||||
user_id: OWNER,
|
||||
word: 'D3RO',
|
||||
pronunciation: null,
|
||||
category: 'technical' as const,
|
||||
usage_count: 0,
|
||||
last_used_at: null,
|
||||
created_at: '2026-08-21T00:00:00.000Z',
|
||||
updated_at: '2026-08-21T00:00:00.000Z',
|
||||
};
|
||||
const invalid = payload();
|
||||
invalid.datasets.dictionary = [dictionary, dictionary];
|
||||
expect(() => parsePortablePayload(stableJsonStringify(invalid))).toThrow(
|
||||
'repeats a row id',
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue