Files
Clipboard/test/data-dir-fallback.test.js
2026-09-12 13:59:12 +08:00

141 lines
5.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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 盘不存在」的 mockfs.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`);