update
This commit is contained in:
87
tests/unit/main/literature-import-move.test.ts
Normal file
87
tests/unit/main/literature-import-move.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
// tests/unit/main/literature-import-move.test.ts
|
||||
//
|
||||
// 守 literature 域「导入 → 立刻搬进子目录」链路(r170)。
|
||||
//
|
||||
// 用户报「粘贴功能有问题」的文献侧根因:旧实现在 IPC handler 里直调
|
||||
// importLiteratureFile + upsertResource,**没更新 LiteratureService.byId**
|
||||
// —— 导入后紧跟的 svc.moveFile(id, target) 走 byId.get(id) 必 miss →
|
||||
// NOT_FOUND。两条用户可见路径同时失效:
|
||||
// - 「复制 → 粘贴」进文件夹(store/literature.ts copy 分支)
|
||||
// - 「OS 拖文件进文件夹」(LiteratureList 拖放 handler)
|
||||
//
|
||||
// 本用例在 service 层锁契约:importFile 之后 byId 立即可见,moveFile 不再
|
||||
// NOT_FOUND,且搬移后 id 变化(absPath 变 → 'lit:'+absPath 变)。
|
||||
//
|
||||
// 真库(electron app.getPath)在测试环境不可用 → resources-repo 整体 mock;
|
||||
// 被测行为(文件拷贝 + byId 维护 + moveFile 的 fs.rename)全在 service 内。
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
const repo = vi.hoisted(() => ({
|
||||
upsertResource: vi.fn(),
|
||||
deleteResource: vi.fn(),
|
||||
// r196:文献 id 换行走**单事务** renameResource(upsert 新行 + 删旧行),
|
||||
// 不再是 delete + upsert 两个独立事务 —— 见 literature-rename.test.ts。
|
||||
renameResource: vi.fn(),
|
||||
listResources: vi.fn(() => [] as unknown[]),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/main/db/resources-repo', () => ({
|
||||
upsertResource: repo.upsertResource,
|
||||
deleteResource: repo.deleteResource,
|
||||
renameResource: repo.renameResource,
|
||||
listResources: repo.listResources,
|
||||
}));
|
||||
|
||||
import { setRootPath } from '../../../src/main/utils/safe-write';
|
||||
import { LiteratureService } from '../../../src/main/literature/literature-service';
|
||||
|
||||
describe('LiteratureService 导入 → 搬进子目录(r170)', () => {
|
||||
let root: string;
|
||||
let svc: LiteratureService;
|
||||
|
||||
beforeEach(async () => {
|
||||
root = await fs.mkdtemp(join(tmpdir(), 'rkms-lit-'));
|
||||
setRootPath(root);
|
||||
await fs.mkdir(join(root, 'literature'), { recursive: true });
|
||||
svc = new LiteratureService();
|
||||
// skipInitialLoad: 只关心 import → move 这条链,不引入 reload 的磁盘扫描
|
||||
await svc.start({ skipInitialLoad: true });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await svc.stop();
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('importFile 之后 byId 立即可见(旧实现只写 DB 行,byId 缺 → moveFile NOT_FOUND)', async () => {
|
||||
const src = join(root, 'paper.pdf');
|
||||
await fs.writeFile(src, 'PDF-BYTES', 'utf-8');
|
||||
const imported = await svc.importFile(src);
|
||||
// 内存索引已含新行 —— 搬移立刻能找到它
|
||||
expect(svc.findById(imported.id)).not.toBeNull();
|
||||
// DB 行也写了
|
||||
expect(repo.upsertResource).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('导入后立刻搬进子目录成功,id 随 absPath 变化', async () => {
|
||||
const src = join(root, 'paper.pdf');
|
||||
await fs.writeFile(src, 'PDF-BYTES', 'utf-8');
|
||||
const imported = await svc.importFile(src);
|
||||
const moved = await svc.moveFile(imported.id, 'sub');
|
||||
expect(moved.id).not.toBe(imported.id);
|
||||
expect(moved.absPath).toContain('sub');
|
||||
// 旧 id 已被换掉(内存索引与 DB 同步)
|
||||
expect(svc.findById(imported.id)).toBeNull();
|
||||
expect(svc.findById(moved.id)).not.toBeNull();
|
||||
});
|
||||
|
||||
it('搬不存在的 id 仍抛 NOT_FOUND(契约不因本轮改动而松动)', async () => {
|
||||
const ghost = 'lit:no-such' as unknown as Parameters<LiteratureService['moveFile']>[0];
|
||||
await expect(svc.moveFile(ghost, 'sub')).rejects.toMatchObject({
|
||||
code: 'NOT_FOUND',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user