Files
peoplelib/src/_test/mirror.test.js
T
lofyerandfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> 3ccd044527 feat: 内置阅读器、批注笔记与 AI 助手,发布 1.3.0
新增 PDF/EPUB/MOBI/AZW 内置阅读器,PDF 分段读取支持超大文件,
批注、读书与画布笔记、封面生成与本地导入。AI 助手支持三种协议、
图像上下文与安全 Markdown 渲染,上下文范围改为 选中/当前页/全文,
页面与全文无需选中文本即可发送,全文会提示可能超出模型限制。

便携版输出目录固定为 PeopleLib-windows-x64,不再随版本号变化,
避免升级后 data/ 被遗留在旧目录。

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-08-03 12:13:02 +08:00

119 lines
4.0 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert');
const path = require('path');
function freshMirror() {
const p = require.resolve(path.join(__dirname, '..', 'sources', 'mirror.js'));
delete require.cache[p];
return require(p);
}
test('tryMirrors: 内容级错误立即返回,不再试其它镜像', async () => {
const m = freshMirror();
const tried = [];
await assert.rejects(
m.tryMirrors('p1', ['a', 'b', 'c'], async (x) => {
tried.push(x);
throw m.contentError('该 DOI 不存在');
}),
/不存在/
);
assert.deepStrictEqual(tried, ['a'], '内容级错误不该继续轮询');
});
test('tryMirrors: 内容级错误不拉黑镜像,下次仍优先使用', async () => {
const m = freshMirror();
await assert.rejects(m.tryMirrors('p2', ['a', 'b'], async () => {
throw m.contentError('没有这篇');
}));
const tried = [];
await m.tryMirrors('p2', ['a', 'b'], async (x) => { tried.push(x); return 'ok'; });
assert.strictEqual(tried[0], 'a', '健康镜像被误拉黑了');
});
test('tryMirrors: 真实网络故障会依次换镜像', async () => {
const m = freshMirror();
const tried = [];
const r = await m.tryMirrors('p3', ['a', 'b', 'c'], async (x) => {
tried.push(x);
if (x !== 'c') throw new Error('网络连接失败,请检查网络或代理设置');
return 'ok';
});
assert.strictEqual(r, 'ok');
assert.deepStrictEqual(tried, ['a', 'b', 'c']);
});
test('tryMirrors: 成功镜像会被记住并优先', async () => {
const m = freshMirror();
await m.tryMirrors('p4', ['a', 'b', 'c'], async (x) => {
if (x !== 'c') throw new Error('请求超时,站点无响应');
return 'ok';
});
const tried = [];
await m.tryMirrors('p4', ['a', 'b', 'c'], async (x) => { tried.push(x); return 'ok'; });
assert.strictEqual(tried[0], 'c', '上次成功的镜像没有被优先');
});
test('tryMirrors: 全部失败时抛出最后一个错误', async () => {
const m = freshMirror();
await assert.rejects(
m.tryMirrors('p5', ['a', 'b'], async () => { throw new Error('请求超时,站点无响应'); }),
/超时/
);
});
test('raceMirrors: 返回最快成功的结果', async () => {
const m = freshMirror();
const r = await m.raceMirrors('r1', ['slow', 'fast'], async (x) => {
if (x === 'slow') { await new Promise((s) => setTimeout(s, 200)); return 'slow'; }
return 'fast';
});
assert.strictEqual(r, 'fast');
});
test('raceMirrors: 胜出后中止其余在途请求', async () => {
const m = freshMirror();
let aborted = false;
const r = await m.raceMirrors('r2', ['loser', 'winner'], async (x, signal) => {
if (x === 'winner') return 'w';
return new Promise((_res, rej) => {
signal.addEventListener('abort', () => { aborted = true; rej(new Error('请求已取消')); });
});
});
assert.strictEqual(r, 'w');
await new Promise((s) => setTimeout(s, 20));
assert.ok(aborted, '败者没有被中止');
});
test('raceMirrors: 跳过已拉黑镜像', async () => {
const m = freshMirror();
// 先让 bad 因真实故障进黑名单
await m.raceMirrors('r3', ['bad', 'good'], async (x) => {
if (x === 'bad') throw new Error('网络连接失败,请检查网络或代理设置');
return 'ok';
});
const tried = [];
await m.raceMirrors('r3', ['bad', 'good'], async (x) => { tried.push(x); return 'ok'; });
assert.ok(!tried.includes('bad'), '黑名单在竞速模式下失效了');
});
test('raceMirrors: 全部失败时 reject 而不是挂起', async () => {
const m = freshMirror();
await assert.rejects(
m.raceMirrors('r4', ['a', 'b'], async () => { throw new Error('请求超时,站点无响应'); }),
/超时/
);
});
test('raceMirrors: 内容级错误直接结束竞速', async () => {
const m = freshMirror();
await assert.rejects(
m.raceMirrors('r5', ['a', 'b'], async (x) => {
if (x === 'a') throw m.contentError('页面结构无法识别');
await new Promise((s) => setTimeout(s, 500));
return 'late';
}),
/页面结构无法识别/
);
});