Files
doodle-store/apps/server/test/category-input.test.ts

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