85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { categoryTreeOrder } from '../src/categories/category-input';
|
|
import { parseCategoryExport } from '../src/categories/category-import';
|
|
|
|
const root = {
|
|
_id: 'math',
|
|
name: '数感启蒙',
|
|
icon: '📐',
|
|
color: '#CDB4DB',
|
|
sortOrder: 0,
|
|
parentId: null,
|
|
};
|
|
|
|
test('分类导入:兼容 JSON Lines/数组,保留原 ID、Emoji 和带时区时间', () => {
|
|
const child = {
|
|
...root,
|
|
_id: 'Math',
|
|
parentId: 'math',
|
|
createdAt: '2025-01-01T08:00:00+08:00',
|
|
};
|
|
const lines = `\uFEFF${JSON.stringify(child)}\n\n${JSON.stringify(root)}\n`;
|
|
const rows = parseCategoryExport(lines);
|
|
assert.deepEqual(
|
|
rows.map((row) => row.id),
|
|
['Math', 'math'],
|
|
);
|
|
assert.equal(rows[0]?.createdAt?.toISOString(), '2025-01-01T00:00:00.000Z');
|
|
assert.equal(rows[1]?.createdAt, undefined);
|
|
assert.equal(rows[1]?.icon, '📐');
|
|
assert.deepEqual(categoryTreeOrder(rows), ['math', 'Math']);
|
|
assert.equal(
|
|
parseCategoryExport(JSON.stringify([{ ...root, parentId: '' }]))[0]
|
|
?.parentId,
|
|
null,
|
|
);
|
|
});
|
|
|
|
test('分类导入:拒绝重复、未知字段、类型转换、越界及未迁移文件图标', () => {
|
|
const bad = [
|
|
[{ ...root, id: 'other' }],
|
|
[root, root],
|
|
[{ ...root, role: 'admin' }],
|
|
[{ ...root, name: 42 }],
|
|
[{ ...root, icon: 'https://private.example/icon.png' }],
|
|
[{ ...root, icon: 'cloud://secret/icon' }],
|
|
[{ ...root, _id: ' math' }],
|
|
[{ ...root, sortOrder: '2' }],
|
|
[{ ...root, sortOrder: 0.5 }],
|
|
[{ ...root, sortOrder: 2147483648 }],
|
|
[{ ...root, parentId: false }],
|
|
[{ ...root, createdAt: '2025-01-01T00:00:00' }],
|
|
[{ ...root, createdAt: { $date: '2025-01-01' } }],
|
|
[
|
|
{
|
|
...root,
|
|
createdAt: '2025-01-02T00:00:00Z',
|
|
updatedAt: '2025-01-01T00:00:00Z',
|
|
},
|
|
],
|
|
];
|
|
for (const value of bad)
|
|
assert.throws(() => parseCategoryExport(JSON.stringify(value)));
|
|
for (const value of ['', '{}', '[]', '{broken'])
|
|
assert.throws(() => parseCategoryExport(value));
|
|
});
|
|
|
|
test('分类树:缺失父级、自引用和多层循环均拒绝,深层树不递归', () => {
|
|
for (const rows of [
|
|
[{ id: 'a', parentId: 'missing' }],
|
|
[{ id: 'a', parentId: 'a' }],
|
|
[
|
|
{ id: 'a', parentId: 'b' },
|
|
{ id: 'b', parentId: 'c' },
|
|
{ id: 'c', parentId: 'a' },
|
|
],
|
|
])
|
|
assert.throws(() => categoryTreeOrder(rows));
|
|
const deep = Array.from({ length: 10_000 }, (_, i) => ({
|
|
id: String(i),
|
|
parentId: i ? String(i - 1) : null,
|
|
})).reverse();
|
|
assert.equal(categoryTreeOrder(deep).length, 10_000);
|
|
});
|