新增 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>
119 lines
4.0 KiB
JavaScript
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';
|
|
}),
|
|
/页面结构无法识别/
|
|
);
|
|
});
|