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
109
apps/mobile-rn/__tests__/data-portability-legacy.test.ts
Normal file
109
apps/mobile-rn/__tests__/data-portability-legacy.test.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
parseImportedPortableFile,
|
||||
PortabilityError,
|
||||
} from '../src/features/data-portability';
|
||||
|
||||
const OWNER = '4f642f87-2d12-4dcf-9aa1-f4427d4078a8';
|
||||
const FIXTURE_ROOT = path.join(__dirname, 'fixtures', 'data-portability');
|
||||
|
||||
function fixture(name: string): string {
|
||||
return fs.readFileSync(path.join(FIXTURE_ROOT, name), 'utf8');
|
||||
}
|
||||
|
||||
describe('desktop and web legacy portability', () => {
|
||||
it('imports the declared desktop dictionary JSON shape deterministically', () => {
|
||||
const raw = fixture('desktop-dictionary.json');
|
||||
const first = parseImportedPortableFile(
|
||||
raw,
|
||||
'dictionary.json',
|
||||
'application/json',
|
||||
OWNER,
|
||||
'2026-08-21T00:00:00.000Z',
|
||||
);
|
||||
const second = parseImportedPortableFile(
|
||||
raw,
|
||||
'dictionary.json',
|
||||
'application/json',
|
||||
OWNER,
|
||||
'2026-08-21T00:00:00.000Z',
|
||||
);
|
||||
|
||||
expect(first.detectedFormat).toBe('legacy-json');
|
||||
expect(first.payload.datasets.dictionary).toHaveLength(2);
|
||||
expect(first.payload.datasets.dictionary[0].usage_count).toBe(7);
|
||||
expect(first.archive.checksum).toBe(second.archive.checksum);
|
||||
expect(first.payload.datasets.dictionary[1].id).toBe(
|
||||
second.payload.datasets.dictionary[1].id,
|
||||
);
|
||||
});
|
||||
|
||||
it('parses RFC4180 commas and escaped quotes in dictionary CSV', () => {
|
||||
const result = parseImportedPortableFile(
|
||||
fixture('desktop-dictionary.csv'),
|
||||
'dictionary.csv',
|
||||
'text/csv',
|
||||
OWNER,
|
||||
'2026-08-21T00:00:00.000Z',
|
||||
);
|
||||
expect(result.detectedFormat).toBe('dictionary-csv');
|
||||
expect(result.payload.datasets.dictionary.map(row => row.word)).toEqual([
|
||||
'쉼표, 포함',
|
||||
'따옴표 "포함"',
|
||||
]);
|
||||
});
|
||||
|
||||
it('converts the real desktop meeting Markdown markers', () => {
|
||||
const result = parseImportedPortableFile(
|
||||
fixture('desktop-meeting.md'),
|
||||
'meeting.md',
|
||||
'text/markdown',
|
||||
OWNER,
|
||||
'2026-08-21T00:00:00.000Z',
|
||||
);
|
||||
const meeting = result.payload.datasets.meetings[0];
|
||||
expect(result.detectedFormat).toBe('desktop-meeting-markdown');
|
||||
expect(meeting.title).toBe('모바일 출시 점검');
|
||||
expect(meeting.minutes_markdown).toContain('다른 사용자 데이터는 거부한다');
|
||||
expect(meeting.raw_transcript).toContain('전체를 롤백합니다');
|
||||
});
|
||||
|
||||
it('rejects a legacy file that declares a different owner', () => {
|
||||
expect(() =>
|
||||
parseImportedPortableFile(
|
||||
fixture('foreign-owner.json'),
|
||||
'foreign.json',
|
||||
'application/json',
|
||||
OWNER,
|
||||
),
|
||||
).toThrow(PortabilityError);
|
||||
try {
|
||||
parseImportedPortableFile(
|
||||
fixture('foreign-owner.json'),
|
||||
'foreign.json',
|
||||
'application/json',
|
||||
OWNER,
|
||||
);
|
||||
} catch (error) {
|
||||
expect((error as PortabilityError).code).toBe('owner');
|
||||
}
|
||||
});
|
||||
|
||||
it('fails closed when one row in a multi-row file is invalid', () => {
|
||||
const malformed = JSON.stringify({
|
||||
entries: [
|
||||
{ word: 'valid', category: 'user' },
|
||||
{ word: '', category: 'user' },
|
||||
],
|
||||
});
|
||||
expect(() =>
|
||||
parseImportedPortableFile(
|
||||
malformed,
|
||||
'dictionary.json',
|
||||
'application/json',
|
||||
OWNER,
|
||||
),
|
||||
).toThrow('invalid length');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue