update
This commit is contained in:
141
test/data-dir-fallback.test.js
Normal file
141
test/data-dir-fallback.test.js
Normal file
@@ -0,0 +1,141 @@
|
||||
'use strict';
|
||||
/**
|
||||
* 数据目录降级测试 —— 解决「默认 D:\Clipboard Data 不可用就启动失败」的兼容问题。
|
||||
*
|
||||
* 被测对象是 config.js 的 isDirUsable / defaultDataDir。三条契约:
|
||||
* 1. isDirUsable 能正确判断目录真的能写入;
|
||||
* 2. defaultDataDir 在默认目录不可用时降级到 home 下的 Clipboard Data;
|
||||
* 3. 探针文件不会残留(每次探测都自清理)。
|
||||
*
|
||||
* 模拟「D 盘不存在」的方法:临时替换 fs.mkdirSync,让它对 D:\\ 开头的路径
|
||||
* 抛 ENOENT —— 跟项目里「不引入 mock 框架、靠临时替换内置模块方法」的风格
|
||||
* 一致。restore 严格用 finally / afterEach 兜住,避免污染其它测试。
|
||||
*/
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
|
||||
let passed = 0;
|
||||
function check(name, fn) {
|
||||
try { fn(); console.log(' ok -', name); passed++; }
|
||||
catch (e) { console.error(' FAIL -', name, '\n', e.message); process.exitCode = 1; }
|
||||
}
|
||||
|
||||
// 用临时 HOME 隔离 —— defaultDataDir() 降级时是 %USERPROFILE%\Clipboard Data,
|
||||
// 我们希望探针 / 副作用都落在临时目录,不污染真实 home。
|
||||
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'clip-fb-'));
|
||||
process.env.USERPROFILE = tmpHome;
|
||||
process.env.HOME = tmpHome;
|
||||
// 不预设 CLIPBOARD_APP_DATA_DIR —— 本测试就是要验证它没设时的降级行为
|
||||
delete process.env.CLIPBOARD_APP_DATA_DIR;
|
||||
const cfg = require('../config.js');
|
||||
|
||||
// 「D 盘不存在」的 mock:fs.mkdirSync 对 D:\ 前缀抛 ENOENT。
|
||||
// 不影响其它调用 —— D: 在大多数机器上不一定存在,我们的判断「按前缀」
|
||||
// 已经足够准;同时不影响 home 下的真实写入。
|
||||
const originalMkdir = fs.mkdirSync;
|
||||
function mockNoDDrive() {
|
||||
fs.mkdirSync = function patched(p, opts) {
|
||||
if (typeof p === 'string' && /^[dD]:[\\/]/.test(p)) {
|
||||
const e = new Error(`ENOENT: no such file or directory, mkdir '${p}'`);
|
||||
e.code = 'ENOENT';
|
||||
throw e;
|
||||
}
|
||||
return originalMkdir.call(fs, p, opts);
|
||||
};
|
||||
}
|
||||
function restoreFs() {
|
||||
fs.mkdirSync = originalMkdir;
|
||||
}
|
||||
process.on('exit', restoreFs);
|
||||
|
||||
check('isDirUsable: 真实可写目录返回 true', () => {
|
||||
// tmpHome 必然可写
|
||||
assert.strictEqual(cfg.isDirUsable(tmpHome), true);
|
||||
});
|
||||
|
||||
check('isDirUsable: 不存在的盘符(D:\\__no_such__)在 mock 下返回 false', () => {
|
||||
mockNoDDrive();
|
||||
try {
|
||||
assert.strictEqual(cfg.isDirUsable('D:\\__no_such_drive_xyz__'), false);
|
||||
} finally { restoreFs(); }
|
||||
});
|
||||
|
||||
check('isDirUsable: 空字符串返回 false(不打 fs.mkdirSync 空路径)', () => {
|
||||
assert.strictEqual(cfg.isDirUsable(''), false);
|
||||
assert.strictEqual(cfg.isDirUsable(null), false);
|
||||
assert.strictEqual(cfg.isDirUsable(undefined), false);
|
||||
});
|
||||
|
||||
check('isDirUsable: 探针文件被清理,不在目录里残留', () => {
|
||||
// 探针名字固定 —— 跑两次探测,第二次还能成功说明第一次 unlink 了。
|
||||
const probePath = path.join(tmpHome, '.clipboard-write-test');
|
||||
assert.ok(!fs.existsSync(probePath), '开始前不应有残留');
|
||||
assert.strictEqual(cfg.isDirUsable(tmpHome), true);
|
||||
assert.ok(!fs.existsSync(probePath), '探测后不应残留探针文件');
|
||||
assert.strictEqual(cfg.isDirUsable(tmpHome), true, '第二次探测照样过 → 第一次 unlink 干净');
|
||||
});
|
||||
|
||||
check('defaultDataDir: D 盘不可用时降级到 home 下的 Clipboard Data', () => {
|
||||
mockNoDDrive();
|
||||
try {
|
||||
// 重新加载模块,让 defaultDataDir() 重新走一遍降级逻辑
|
||||
delete require.cache[require.resolve('../config.js')];
|
||||
const cfg2 = require('../config.js');
|
||||
const dir = cfg2.defaultDataDir();
|
||||
// 降级路径必须等于 home + Clipboard Data
|
||||
assert.strictEqual(
|
||||
path.resolve(dir),
|
||||
path.resolve(tmpHome, 'Clipboard Data'),
|
||||
'D 盘被 mock 掉时必须降级到 home\\Clipboard Data'
|
||||
);
|
||||
// 降级目录真的可用 —— 否则还是炸
|
||||
assert.strictEqual(cfg2.isDirUsable(dir), true);
|
||||
} finally {
|
||||
restoreFs();
|
||||
delete require.cache[require.resolve('../config.js')];
|
||||
require('../config.js'); // 重新挂回正常状态给后面的 case
|
||||
}
|
||||
});
|
||||
|
||||
check('defaultDataDir: env 覆盖在降级链路里仍最高优先级', () => {
|
||||
mockNoDDrive();
|
||||
const custom = path.join(tmpHome, 'env-override-dir');
|
||||
process.env.CLIPBOARD_APP_DATA_DIR = custom;
|
||||
try {
|
||||
delete require.cache[require.resolve('../config.js')];
|
||||
const cfg2 = require('../config.js');
|
||||
assert.strictEqual(path.resolve(cfg2.defaultDataDir()), path.resolve(custom),
|
||||
'env 覆盖必须胜过 D 盘 / home 的降级选择');
|
||||
} finally {
|
||||
restoreFs();
|
||||
delete process.env.CLIPBOARD_APP_DATA_DIR;
|
||||
delete require.cache[require.resolve('../config.js')];
|
||||
require('../config.js');
|
||||
}
|
||||
});
|
||||
|
||||
check('defaultDataDir: D 盘可用时首选 D 盘(契约保留)', () => {
|
||||
// 不 mock —— 用真实环境。仅当 D 盘真的可用时断言,否则跳过。
|
||||
if (!cfg.isDirUsable('D:\\Clipboard Data')) {
|
||||
console.log(' (D:\\Clipboard Data 在本机不可用,跳过)');
|
||||
return;
|
||||
}
|
||||
// 不设 env 时必须首选 D 盘
|
||||
const saved = process.env.CLIPBOARD_APP_DATA_DIR;
|
||||
delete process.env.CLIPBOARD_APP_DATA_DIR;
|
||||
delete require.cache[require.resolve('../config.js')];
|
||||
try {
|
||||
const cfg2 = require('../config.js');
|
||||
assert.strictEqual(path.resolve(cfg2.defaultDataDir()), path.resolve('D:\\Clipboard Data'));
|
||||
} finally {
|
||||
process.env.CLIPBOARD_APP_DATA_DIR = saved;
|
||||
delete require.cache[require.resolve('../config.js')];
|
||||
require('../config.js');
|
||||
}
|
||||
});
|
||||
|
||||
// 清理
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
console.log(`\n${passed} checks passed`);
|
||||
Reference in New Issue
Block a user