import { describe, it, expect } from 'vitest'; import nodePath from 'node:path'; import os from 'node:os'; import { promises as fs } from 'node:fs'; import { assertInsideVault, sanitizeName, dedupeName, IllegalPathError } from '../../apps/desktop/src/main/safe/safe-path'; describe('sanitizeName (FR-F13)', () => { it('替换非法字符为下划线', () => { expect(sanitizeName('a:bd?')).toBe('a_b_c_d_'); }); it('空名回退为未命名', () => { expect(sanitizeName(' ')).toBe('未命名'); expect(sanitizeName('')).toBe('未命名'); }); it('保留正常名', () => { expect(sanitizeName('我的笔记')).toBe('我的笔记'); }); }); describe('assertInsideVault (FR-F15)', () => { const root = nodePath.join(os.tmpdir(), 'dsh-vault-test'); it('接受库内相对路径', () => { expect(assertInsideVault(root, 'a/b.md')).toBe(nodePath.resolve(root, 'a/b.md')); }); it('拒绝绝对路径', () => { expect(() => assertInsideVault(root, nodePath.resolve(root, 'x'))).toThrow(IllegalPathError); }); it('拒绝盘符前缀', () => { expect(() => assertInsideVault(root, 'C:/x')).toThrow(IllegalPathError); }); it('拒绝 .. 穿越', () => { expect(() => assertInsideVault(root, '../escape.md')).toThrow(IllegalPathError); expect(() => assertInsideVault(root, 'a/../../escape.md')).toThrow(IllegalPathError); }); }); describe('dedupeName (FR-F14)', () => { const root = nodePath.join(os.tmpdir(), 'dsh-vault-dedupe'); it('重名追加序号', async () => { await fs.rm(root, { recursive: true, force: true }); await fs.mkdir(nodePath.join(root, 'd'), { recursive: true }); await fs.writeFile(nodePath.join(root, 'd', 'n.md'), ''); expect(await dedupeName(root, 'd', 'n.md')).toBe('n-1.md'); await fs.writeFile(nodePath.join(root, 'd', 'n-1.md'), ''); expect(await dedupeName(root, 'd', 'n.md')).toBe('n-2.md'); }); it('不冲突时原样返回', async () => { expect(await dedupeName(root, 'd', 'unique.md')).toBe('unique.md'); }); });