P1 阶段的结论先行:**「中继端到端未打通」是过时批注**——对活网关实测, 服务端三条路由与客户端三个函数一直是通的(open 200 → PUT/GET → 字节 全等)。真缺的是自动化验证与代码归位。本轮补上: · net/relay_client.dart(227 行):LanDownloadResult / receiveToFile / LanRelayChannel / openLanRelay / uploadViaRelay / downloadViaRelay 从 host/lan_service.dart 原样抽出(纯搬迁,export 转出,调用方零改动)。 这样 link/ 不必反向依赖 host/,分层纪律不破。 · link/driver.dart:TransportDriver 契约(mode / probe / close)。P1 的 RelayDriver 是第一个实现者,契约按真实需要定;数据面方法故意不立 (四种模式传输形态差异仍大,此时统一只会造空壳适配器)。 · link/relay_driver.dart:探活走 GET /health(**不烧配额**——探活若走 open 会占满"每账号并发 1"的闸,选路一频繁用户就永远"请等前一条"); openForOffer 委托 openLanRelay;upload/download 委托既有函数。 host 的 openRelayForSend 改为仅注入凭据、逻辑全交驱动。 · tests/relay-route-e2e.js(14 项,需网关):打真路由验八组—— 完整往返 / 401 缺身份 / E_BAD_SIZE / E_TOO_LARGE / E_TOO_MANY(并发 1)/ 超声明即断连且不超量 / 错向令牌 404。relay-test.js 测不到"路由怎么接 进来"(曾出过类全绿、路由 503 的事故)。 · client/bin/relay_check.dart + tests/relay-client-e2e.js(6 项):跑客户端 自己的 Dart 实现打真网关——这是「服务端通 + 客户端函数在」真的连起来 用的唯一证据。CLI 输出机器可读 JSON,e2e 断言字节数与 sha256。 · client/test/relay_driver_test.dart(7 项,真起本地 HttpServer): health 200+ok / degraded 200+ok:false 按不可达 / 500 / 端口没人听 / 缺凭据抛人话 / 409 中文原因原样上抛 / close 幂等。 最关键一条:**探活只许打 /health,绝不许打 /relay/open**(变异自证: 改成打 open → 精确变红)。 全量:Dart 416 项全过(409+7);run-all 26 通过 / 3 失败——3 个失败中 2 个是既有(指标字段一致性 / FFI 冒烟的 Rust 事件漂移),第三个是隐私 扫描(另一笔提交信息含本机 denylist 词,已在下一轮处理)。
89 lines
2.8 KiB
JavaScript
89 lines
2.8 KiB
JavaScript
/**
|
||
* 中继客户端端到端 —— 用**客户端自己的 Dart 实现**打真网关
|
||
*
|
||
* 为什么需要这一层:relay-route-e2e.js 证明了"HTTP 路由能用",
|
||
* relay-test.js 证明了"Relay 类对",但两者都绕过了 Dart 客户端。
|
||
* 历史批注「中继端到端未打通」指的正是这条缝 —— 服务端通、客户端函数在,
|
||
* 但没人证明过它们**连起来**能用。本套件跑 client/bin/relay_check.dart:
|
||
* 客户端 openLanRelay → 下载端挂起 → uploadViaRelay → 校验字节。
|
||
*
|
||
* 前置:网关 @ :3000;dart 可用(客户端目录已 pub get)
|
||
* 运行:node tests/relay-client-e2e.js
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
const { execFileSync } = require('child_process');
|
||
const path = require('path');
|
||
|
||
const ROOT = path.resolve(__dirname, '..');
|
||
const CLIENT = path.join(ROOT, 'client');
|
||
const GATEWAY = process.env.GATEWAY || 'http://127.0.0.1:3000';
|
||
|
||
let pass = 0;
|
||
let fail = 0;
|
||
|
||
function check(name, cond, extra = '') {
|
||
if (cond) {
|
||
pass++;
|
||
console.log(' \u2713 ' + name);
|
||
} else {
|
||
fail++;
|
||
console.log(' \u2717 ' + name + ' ' + extra);
|
||
}
|
||
}
|
||
|
||
(async () => {
|
||
console.log('\n── 客户端中继往返(dart run bin/relay_check.dart)──');
|
||
|
||
// 清掉代理:本地 WS/HTTP 走代理会假失败(客户端 scripts/test.sh 同款处理)
|
||
const env = { ...process.env };
|
||
for (const k of [
|
||
'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY',
|
||
'http_proxy', 'https_proxy', 'all_proxy',
|
||
'NO_PROXY', 'no_proxy',
|
||
]) {
|
||
delete env[k];
|
||
}
|
||
|
||
let out = '';
|
||
let code = 0;
|
||
try {
|
||
out = execFileSync(
|
||
'dart',
|
||
['run', 'bin/relay_check.dart', '--server', GATEWAY, '--size', '8192'],
|
||
{ cwd: CLIENT, env, encoding: 'utf8', timeout: 120000 }
|
||
);
|
||
code = 0;
|
||
} catch (e) {
|
||
code = e.status == null ? -1 : e.status;
|
||
out = (e.stdout || '') + (e.stderr || '');
|
||
}
|
||
|
||
const line = out
|
||
.split(/\r?\n/)
|
||
.map((l) => l.trim())
|
||
.filter((l) => l.startsWith('{'))
|
||
.pop();
|
||
let j = null;
|
||
try {
|
||
j = JSON.parse(line || '');
|
||
} catch (_) {}
|
||
|
||
check('CLI 退出码 0', code === 0, `code=${code}`);
|
||
check('输出一行 JSON', j !== null, `out=${out.slice(0, 200)}`);
|
||
check('ok=true', j && j.ok === true, `j=${line}`);
|
||
check('字节数 = 8192', j && j.bytes === 8192, `bytes=${j && j.bytes}`);
|
||
check('有 sha256(收尾校验真实发生)', !!(j && j.sha && j.sha.length >= 32),
|
||
`sha=${j && j.sha}`);
|
||
check('耗时在合理区间(< 60s)', j && j.ms > 0 && j.ms < 60000, `ms=${j && j.ms}`);
|
||
|
||
console.log('\n========================================');
|
||
console.log(' 结果: ' + pass + ' 通过 / ' + fail + ' 失败');
|
||
console.log('========================================');
|
||
process.exit(fail ? 1 : 0);
|
||
})().catch((e) => {
|
||
console.error('测试异常:', e);
|
||
process.exit(1);
|
||
});
|