Files
inkwatch/tests/relay-client-e2e.js
PMon Dev e8e6fb3ec1 feat(relay): 中继客户端收口到连接域,并补两层端到端验证
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 词,已在下一轮处理)。
2026-10-17 10:54:34 +08:00

89 lines
2.8 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.
/**
* 中继客户端端到端 —— 用**客户端自己的 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);
});