120 lines
5.0 KiB
JavaScript
120 lines
5.0 KiB
JavaScript
'use strict';
|
||
/**
|
||
* 老数据目录(%USERPROFILE%\.clipboard-app)→ 新默认目录(项目/exe 同级 data/)
|
||
* 的一次性迁移。
|
||
*
|
||
* 三条底线:
|
||
* 1. 迁完 config.json 里的 dataDir 必须指向新目录 —— 否则下次启动又跑回老地方;
|
||
* 2. 新目录已经在用时不得覆盖(幂等,重复启动不会拿老库盖新库);
|
||
* 3. 老文件保留 —— 复制不是移动,出问题能回退。
|
||
*/
|
||
const assert = require('node:assert');
|
||
const fs = require('node:fs');
|
||
const os = require('node:os');
|
||
const path = require('node:path');
|
||
|
||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'clip-mig-'));
|
||
const home = path.join(root, 'home');
|
||
const target = path.join(root, 'Clipboard Data'); // 模拟 D:\Clipboard Data
|
||
const legacy = path.join(home, '.clipboard-app');
|
||
|
||
process.env.USERPROFILE = home;
|
||
process.env.HOME = home;
|
||
process.env.CLIPBOARD_APP_DATA_DIR = target;
|
||
|
||
const cfg = require('../config.js');
|
||
|
||
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; }
|
||
}
|
||
|
||
function resetTarget() {
|
||
fs.rmSync(target, { recursive: true, force: true });
|
||
}
|
||
|
||
function seedLegacy(extraCfg) {
|
||
fs.rmSync(legacy, { recursive: true, force: true });
|
||
fs.mkdirSync(legacy, { recursive: true });
|
||
fs.writeFileSync(path.join(legacy, 'history.db'), 'OLD-HISTORY');
|
||
fs.writeFileSync(path.join(legacy, 'history.db-wal'), 'OLD-WAL');
|
||
fs.writeFileSync(path.join(legacy, 'favorites.db'), 'OLD-FAV');
|
||
fs.writeFileSync(path.join(legacy, 'history.db-shm'), 'OLD-SHM');
|
||
fs.writeFileSync(
|
||
path.join(legacy, 'config.json'),
|
||
JSON.stringify({ maxItems: 500, pollMs: 500, theme: 'carbon', dataDir: legacy, ...extraCfg }),
|
||
);
|
||
}
|
||
|
||
check('老数据被复制到新目录,且 dataDir 改写为新目录', () => {
|
||
resetTarget();
|
||
seedLegacy();
|
||
const r = cfg.migrateLegacyData();
|
||
assert.strictEqual(r.migrated, true);
|
||
assert.strictEqual(fs.readFileSync(path.join(target, 'history.db'), 'utf8'), 'OLD-HISTORY');
|
||
assert.strictEqual(fs.readFileSync(path.join(target, 'history.db-wal'), 'utf8'), 'OLD-WAL');
|
||
assert.strictEqual(fs.readFileSync(path.join(target, 'favorites.db'), 'utf8'), 'OLD-FAV');
|
||
|
||
const c = cfg.loadConfig();
|
||
assert.strictEqual(path.resolve(c.dataDir), path.resolve(target), 'dataDir 必须指向新目录');
|
||
assert.strictEqual(c.maxItems, 500, '其他设置要保留');
|
||
assert.strictEqual(c.theme, 'carbon');
|
||
});
|
||
|
||
check('-shm 不搬(SQLite 自己重建)', () => {
|
||
assert.ok(!fs.existsSync(path.join(target, 'history.db-shm')));
|
||
});
|
||
|
||
check('老文件保留,不是移动', () => {
|
||
assert.ok(fs.existsSync(path.join(legacy, 'history.db')));
|
||
assert.ok(fs.existsSync(path.join(legacy, 'config.json')));
|
||
});
|
||
|
||
check('幂等:新目录已在用时不覆盖', () => {
|
||
fs.writeFileSync(path.join(target, 'history.db'), 'NEW-HISTORY');
|
||
const r = cfg.migrateLegacyData();
|
||
assert.strictEqual(r.migrated, false);
|
||
assert.strictEqual(r.reason, 'target-in-use');
|
||
assert.strictEqual(fs.readFileSync(path.join(target, 'history.db'), 'utf8'), 'NEW-HISTORY');
|
||
});
|
||
|
||
check('没有老数据时什么都不做', () => {
|
||
resetTarget();
|
||
fs.rmSync(legacy, { recursive: true, force: true });
|
||
const r = cfg.migrateLegacyData();
|
||
assert.strictEqual(r.migrated, false);
|
||
assert.strictEqual(r.reason, 'nothing-to-migrate');
|
||
assert.ok(!fs.existsSync(path.join(target, 'config.json')));
|
||
});
|
||
|
||
check('老 config 指向自定义目录时,从那个目录搬', () => {
|
||
resetTarget();
|
||
const custom = path.join(root, 'custom');
|
||
fs.mkdirSync(custom, { recursive: true });
|
||
fs.writeFileSync(path.join(custom, 'history.db'), 'CUSTOM-HISTORY');
|
||
seedLegacy({ dataDir: custom });
|
||
const r = cfg.migrateLegacyData();
|
||
assert.strictEqual(r.migrated, true);
|
||
assert.strictEqual(path.resolve(r.from), path.resolve(custom));
|
||
assert.strictEqual(fs.readFileSync(path.join(target, 'history.db'), 'utf8'), 'CUSTOM-HISTORY');
|
||
assert.strictEqual(path.resolve(cfg.loadConfig().dataDir), path.resolve(target));
|
||
});
|
||
|
||
check('老 config 缺 dataDir 字段时,从老目录搬(不能用 sanitize 回填的新默认目录当源)', () => {
|
||
// sanitize 会把缺省的 dataDir 补成新默认目录(= target),拿它当 src
|
||
// 就是"从 target 搬到 target":什么也没搬却报 migrated,老数据永久遗留。
|
||
resetTarget();
|
||
fs.rmSync(legacy, { recursive: true, force: true });
|
||
fs.mkdirSync(legacy, { recursive: true });
|
||
fs.writeFileSync(path.join(legacy, 'history.db'), 'OLD-HISTORY');
|
||
fs.writeFileSync(path.join(legacy, 'config.json'), JSON.stringify({ maxItems: 100, pollMs: 500 }));
|
||
const r = cfg.migrateLegacyData();
|
||
assert.strictEqual(r.migrated, true);
|
||
assert.strictEqual(path.resolve(r.from), path.resolve(legacy), 'src 必须是老目录');
|
||
assert.strictEqual(fs.readFileSync(path.join(target, 'history.db'), 'utf8'), 'OLD-HISTORY');
|
||
});
|
||
|
||
fs.rmSync(root, { recursive: true, force: true });
|
||
console.log(`\n${passed} checks passed`);
|