import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import nodePath from 'node:path'; import os from 'node:os'; import { promises as fs } from 'node:fs'; import { FileService } from '../../apps/desktop/src/main/services/file-service'; const tmp = nodePath.join(os.tmpdir(), 'dsh-m3-test'); beforeEach(async () => { await fs.rm(tmp, { recursive: true, force: true }); await fs.mkdir(tmp, { recursive: true }); }); afterEach(async () => { await fs.rm(tmp, { recursive: true, force: true }); }); describe('M3 文档属性契约(FR-M3/M4/M5/M6)', () => { it('标签增删后落盘往返一致', async () => { const s = new FileService(tmp); await s.create('', 'd', false); await s.writeDoc({ path: 'd.md', meta: { tags: ['x', 'y'] }, body: '正文' }); let r = await s.readDoc('d.md'); expect(r.meta.tags).toEqual(['x', 'y']); // 删一个 await s.writeDoc({ path: 'd.md', meta: { tags: ['x'] }, body: '正文' }); r = await s.readDoc('d.md'); expect(r.meta.tags).toEqual(['x']); }); it('星标切换落盘:true 写入、false 不写', async () => { const s = new FileService(tmp); await s.create('', 'd', false); await s.writeDoc({ path: 'd.md', meta: { star: true }, body: 'b' }); expect((await s.readDoc('d.md')).meta.star).toBe(true); // 取消星标 -> star 字段不落盘 await s.writeDoc({ path: 'd.md', meta: { star: false }, body: 'b' }); const r = await s.readDoc('d.md'); expect(r.meta.star).toBeFalsy(); // 文件内不应出现 star: 字样 const raw = await fs.readFile(nodePath.join(tmp, 'd.md'), 'utf8'); expect(raw).not.toContain('star'); }); it('关联链接落盘往返', async () => { const s = new FileService(tmp); await s.create('', 'd', false); await s.writeDoc({ path: 'd.md', meta: { links: ['a/b.md', 'c.md'] }, body: 'b' }); expect((await s.readDoc('d.md')).meta.links).toEqual(['a/b.md', 'c.md']); }); it('文档信息:标题缺省回退文件名', async () => { const s = new FileService(tmp); await s.create('', '我的笔记', false); await s.writeDoc({ path: '我的笔记.md', meta: {}, body: '内容' }); const r = await s.readDoc('我的笔记.md'); expect(r.meta.title).toBeUndefined(); // 缺省不落盘,UI 侧回退文件名 }); });