99 lines
3.7 KiB
TypeScript
99 lines
3.7 KiB
TypeScript
// tests/unit/main/location-relative-datapath.test.ts
|
|
//
|
|
// 守 r197「相对 dataPath 不得被 mkdir + 持久化」契约。
|
|
//
|
|
// location.ts 的 resolveDataPath() 有 9 条分支,其中**三条**会「拿一个
|
|
// 候选路径 → fs.mkdir → writeLocation 持久化」:
|
|
// - 第 1 步 location.json → 有 isAbsolute 守卫(:110)
|
|
// - 第 5 步 merged settings → 有 isAbsolute 守卫(:264,2026-10-01 补)
|
|
// - 第 7 步 userData settings → r197 补上同名守卫(defense-in-depth)
|
|
//
|
|
// 后果:userData 下残留的旧版 settings.json 写着 dataPath: "notes",
|
|
// 启动时工作区被建在 process.cwd()/notes(dev = 项目根、打包后 = asar
|
|
// 目录),并被写进 location.json 长期固化 —— 用户的工作区落在磁盘角落。
|
|
//
|
|
// 说明(可达性,别被误导):第 7 步在**当前**流程里其实走不到 ——
|
|
// userData settings 里带 dataPath 的情形已被第 5 步的 withPath 分支截住
|
|
// (且那里已有 isAbsolute 校验)。r197 在第 7 步补的是「将来流程调整时
|
|
// 不会静默改回去」的保险。本测是**行为级**守卫:不管最终由哪条分支兜住,
|
|
// 「相对 dataPath 必须落回默认、且不得在 cwd 下建目录」这条不变式都要成立。
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { promises as fs, existsSync } from 'fs';
|
|
import { join } from 'path';
|
|
import { tmpdir } from 'os';
|
|
|
|
const paths = vi.hoisted(() => ({ userData: '', documents: '' }));
|
|
|
|
vi.mock('electron', () => ({
|
|
app: {
|
|
getPath: (name: string): string => {
|
|
if (name === 'userData') return paths.userData;
|
|
if (name === 'documents') return paths.documents;
|
|
return tmpdir();
|
|
},
|
|
},
|
|
}));
|
|
|
|
import { resolveDataPath } from '../../../src/main/location';
|
|
import { WORKSPACE_DATA_LAYOUT } from '../../../src/shared/types';
|
|
|
|
describe('resolveDataPath 相对 dataPath(r197)', () => {
|
|
let root: string;
|
|
let userData: string;
|
|
let documents: string;
|
|
|
|
beforeEach(async () => {
|
|
root = await fs.mkdtemp(join(tmpdir(), 'rkms-loc-'));
|
|
userData = join(root, 'userData');
|
|
documents = join(root, 'Documents');
|
|
paths.userData = userData;
|
|
paths.documents = documents;
|
|
await fs.mkdir(userData, { recursive: true });
|
|
await fs.mkdir(documents, { recursive: true });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await fs.rm(root, { recursive: true, force: true });
|
|
});
|
|
|
|
it('userData settings.json 的相对 dataPath → 被忽略,落回默认路径', async () => {
|
|
await fs.writeFile(
|
|
join(userData, WORKSPACE_DATA_LAYOUT.SETTINGS_FILE),
|
|
JSON.stringify({ dataPath: 'relative-notes' }),
|
|
'utf-8',
|
|
);
|
|
|
|
const res = await resolveDataPath();
|
|
const def = join(documents, 'ResearchHub', 'data');
|
|
expect(res.path).toBe(def);
|
|
// 关键:不得在 cwd 下把「相对路径」当工作区建出来
|
|
expect(existsSync(join(process.cwd(), 'relative-notes'))).toBe(false);
|
|
});
|
|
|
|
it('相对 dataPath 不得被持久化进 location.json', async () => {
|
|
await fs.writeFile(
|
|
join(userData, WORKSPACE_DATA_LAYOUT.SETTINGS_FILE),
|
|
JSON.stringify({ dataPath: 'relative-notes' }),
|
|
'utf-8',
|
|
);
|
|
await resolveDataPath();
|
|
const locPath = join(userData, 'location.json');
|
|
if (existsSync(locPath)) {
|
|
const raw = await fs.readFile(locPath, 'utf-8');
|
|
expect(raw).not.toContain('relative-notes');
|
|
}
|
|
});
|
|
|
|
it('绝对 dataPath 仍然照常采纳(契约不因本轮改动而松动)', async () => {
|
|
const abs = join(root, 'workspace-abs');
|
|
await fs.writeFile(
|
|
join(userData, WORKSPACE_DATA_LAYOUT.SETTINGS_FILE),
|
|
JSON.stringify({ dataPath: abs }),
|
|
'utf-8',
|
|
);
|
|
const res = await resolveDataPath();
|
|
expect(res.path).toBe(abs);
|
|
expect(existsSync(abs)).toBe(true);
|
|
});
|
|
});
|