dsh-remote-control 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cordis.patch.yml +6 -0
- package/dist/bundle/client.cjs +146 -0
- package/dist/bundle/index.js +667 -264
- package/package.json +13 -6
- package/dist/src/core/clock.d.ts +0 -45
- package/dist/src/core/clock.js +0 -83
- package/dist/src/core/clock.js.map +0 -1
- package/dist/src/core/keys.d.ts +0 -98
- package/dist/src/core/keys.js +0 -176
- package/dist/src/core/keys.js.map +0 -1
- package/dist/src/core/pairing-window.d.ts +0 -95
- package/dist/src/core/pairing-window.js +0 -92
- package/dist/src/core/pairing-window.js.map +0 -1
- package/dist/src/core/runtime.d.ts +0 -104
- package/dist/src/core/runtime.js +0 -491
- package/dist/src/core/runtime.js.map +0 -1
- package/dist/src/core/sleep-policy.d.ts +0 -81
- package/dist/src/core/sleep-policy.js +0 -134
- package/dist/src/core/sleep-policy.js.map +0 -1
- package/dist/src/core/window.d.ts +0 -88
- package/dist/src/core/window.js +0 -185
- package/dist/src/core/window.js.map +0 -1
- package/dist/src/index.d.ts +0 -70
- package/dist/src/index.js +0 -882
- package/dist/src/index.js.map +0 -1
- package/dist/src/platform/carrier-services.d.ts +0 -96
- package/dist/src/platform/carrier-services.js +0 -982
- package/dist/src/platform/carrier-services.js.map +0 -1
- package/dist/src/platform/guard.d.ts +0 -60
- package/dist/src/platform/guard.js +0 -124
- package/dist/src/platform/guard.js.map +0 -1
- package/dist/src/platform/mock-kernel.d.ts +0 -95
- package/dist/src/platform/mock-kernel.js +0 -180
- package/dist/src/platform/mock-kernel.js.map +0 -1
- package/dist/src/platform/qr.d.ts +0 -28
- package/dist/src/platform/qr.js +0 -80
- package/dist/src/platform/qr.js.map +0 -1
- package/dist/src/platform/sleep-posix.d.ts +0 -17
- package/dist/src/platform/sleep-posix.js +0 -91
- package/dist/src/platform/sleep-posix.js.map +0 -1
- package/dist/src/ports/index.d.ts +0 -246
- package/dist/src/ports/index.js +0 -2
- package/dist/src/ports/index.js.map +0 -1
- package/dist/src/shell/config.d.ts +0 -56
- package/dist/src/shell/config.js +0 -212
- package/dist/src/shell/config.js.map +0 -1
- package/dist/src/shell/pairing-text.d.ts +0 -38
- package/dist/src/shell/pairing-text.js +0 -22
- package/dist/src/shell/pairing-text.js.map +0 -1
- package/dist/src/shell/status.d.ts +0 -39
- package/dist/src/shell/status.js +0 -119
- package/dist/src/shell/status.js.map +0 -1
- package/dist/src/transport/relay.d.ts +0 -107
- package/dist/src/transport/relay.js +0 -359
- package/dist/src/transport/relay.js.map +0 -1
- package/dist/tests/carrier-events.test.d.ts +0 -1
- package/dist/tests/carrier-events.test.js +0 -88
- package/dist/tests/carrier-events.test.js.map +0 -1
- package/dist/tests/carrier-services.test.d.ts +0 -1
- package/dist/tests/carrier-services.test.js +0 -562
- package/dist/tests/carrier-services.test.js.map +0 -1
- package/dist/tests/carrier-vocabulary.test.d.ts +0 -1
- package/dist/tests/carrier-vocabulary.test.js +0 -149
- package/dist/tests/carrier-vocabulary.test.js.map +0 -1
- package/dist/tests/clock-probe-timers.test.d.ts +0 -1
- package/dist/tests/clock-probe-timers.test.js +0 -73
- package/dist/tests/clock-probe-timers.test.js.map +0 -1
- package/dist/tests/command-result.test.d.ts +0 -1
- package/dist/tests/command-result.test.js +0 -185
- package/dist/tests/command-result.test.js.map +0 -1
- package/dist/tests/config.test.d.ts +0 -1
- package/dist/tests/config.test.js +0 -223
- package/dist/tests/config.test.js.map +0 -1
- package/dist/tests/guard.test.d.ts +0 -1
- package/dist/tests/guard.test.js +0 -175
- package/dist/tests/guard.test.js.map +0 -1
- package/dist/tests/ids-and-keys.test.d.ts +0 -1
- package/dist/tests/ids-and-keys.test.js +0 -211
- package/dist/tests/ids-and-keys.test.js.map +0 -1
- package/dist/tests/pairing-text.test.d.ts +0 -1
- package/dist/tests/pairing-text.test.js +0 -62
- package/dist/tests/pairing-text.test.js.map +0 -1
- package/dist/tests/pairing-window.test.d.ts +0 -1
- package/dist/tests/pairing-window.test.js +0 -165
- package/dist/tests/pairing-window.test.js.map +0 -1
- package/dist/tests/relay-client.test.d.ts +0 -1
- package/dist/tests/relay-client.test.js +0 -557
- package/dist/tests/relay-client.test.js.map +0 -1
- package/dist/tests/runtime.test.d.ts +0 -1
- package/dist/tests/runtime.test.js +0 -963
- package/dist/tests/runtime.test.js.map +0 -1
- package/dist/tests/sleep.test.d.ts +0 -1
- package/dist/tests/sleep.test.js +0 -264
- package/dist/tests/sleep.test.js.map +0 -1
- package/dist/tests/status.test.d.ts +0 -1
- package/dist/tests/status.test.js +0 -238
- package/dist/tests/status.test.js.map +0 -1
- package/dist/tests/window.test.d.ts +0 -1
- package/dist/tests/window.test.js +0 -210
- package/dist/tests/window.test.js.map +0 -1
|
@@ -1,557 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* relay-client.test — `transport/relay.ts`:主机侧中继客户端的配对与密封簿记。
|
|
3
|
-
*
|
|
4
|
-
* 三条来自线上事故的硬要求(取证 docs/legacy-spec/relay-and-wireformat.md §2.3、
|
|
5
|
-
* HANDOFF.md §4.5、docs/DESIGN.md §4 的 D1/D3):
|
|
6
|
-
*
|
|
7
|
-
* 1. `pair-ready.ttlMs` 是**服务端权威 TTL**,必须改写本地过期时间。忘了这一步:
|
|
8
|
-
* 主机提前清掉 PSK → `peer-joined` 找不到密钥 → 静默丢 peer →
|
|
9
|
-
* 手机端表现为"配对显示成功,但会话列表永远空白"。
|
|
10
|
-
* 2. `peer-joined.pairingToken` 是唯一合法的取密钥依据。多码并存时取"最新那一张"
|
|
11
|
-
* 就是当初那起事故(每一帧都解不开,且没有任何提示)。
|
|
12
|
-
* 3. 中继重启(generation 变化)后旧配对全部作废;客户端断连(`peer-left`)时
|
|
13
|
-
* 会话**必须留着**(D3),否则手机每次回前台都要重新扫码。
|
|
14
|
-
*
|
|
15
|
-
* 说明:`RelayClient` 自己 `new WebSocket(...)`,源码里没有 socket 注入点,
|
|
16
|
-
* 也没有可注入的时钟以外的握手缝。所以这里用白盒方式把一只假 socket 接到
|
|
17
|
-
* 私有字段上、再喂中继来帧文本——测的是帧处理与密钥簿记这些**真正的判据**,
|
|
18
|
-
* 而不是 ws 的实现。若之后给 `RelayClientOptions` 加了 socket 工厂,
|
|
19
|
-
* 把 `attachSocket()/feedFrame()` 换成注入即可,断言一条都不用改。
|
|
20
|
-
*/
|
|
21
|
-
import { test } from 'node:test';
|
|
22
|
-
import assert from 'node:assert/strict';
|
|
23
|
-
import { Buffer } from 'node:buffer';
|
|
24
|
-
import { PAYLOAD_TYPES, derivePskKey, open, seal } from 'dsh-remote-wire';
|
|
25
|
-
import { RelayClient } from '../src/transport/relay.js';
|
|
26
|
-
import { PairingSlots } from '../src/core/keys.js';
|
|
27
|
-
import { FakeClock } from '../src/core/clock.js';
|
|
28
|
-
/** `ws` 的 `WebSocket.OPEN` 常量值;`raw()` 只比较这个数。 */
|
|
29
|
-
const SOCKET_OPEN = 1;
|
|
30
|
-
function makeSocket() {
|
|
31
|
-
return {
|
|
32
|
-
readyState: SOCKET_OPEN,
|
|
33
|
-
sent: [],
|
|
34
|
-
send(text) {
|
|
35
|
-
this.sent.push(String(text));
|
|
36
|
-
},
|
|
37
|
-
close() {
|
|
38
|
-
this.readyState = 3; // CLOSED
|
|
39
|
-
},
|
|
40
|
-
terminate() {
|
|
41
|
-
this.readyState = 3;
|
|
42
|
-
},
|
|
43
|
-
removeAllListeners() {
|
|
44
|
-
/* 假 socket 没有监听器 */
|
|
45
|
-
},
|
|
46
|
-
on() {
|
|
47
|
-
/* 同上:假 socket 不自己发事件,能挂上就够 */
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function harness() {
|
|
52
|
-
const clock = new FakeClock();
|
|
53
|
-
const slots = new PairingSlots(() => clock.now());
|
|
54
|
-
const socket = makeSocket();
|
|
55
|
-
const joined = [];
|
|
56
|
-
const gone = [];
|
|
57
|
-
const left = [];
|
|
58
|
-
const commands = [];
|
|
59
|
-
const states = [];
|
|
60
|
-
const logs = [];
|
|
61
|
-
const client = new RelayClient({
|
|
62
|
-
url: 'ws://127.0.0.1:1',
|
|
63
|
-
hostId: 'h_test',
|
|
64
|
-
label: '测试主机',
|
|
65
|
-
token: 'fake-host-token-not-a-real-secret-0123',
|
|
66
|
-
clock,
|
|
67
|
-
log: (message, fields) => {
|
|
68
|
-
logs.push(`${message} ${JSON.stringify(fields ?? {})}`);
|
|
69
|
-
},
|
|
70
|
-
onCommand: (conversationId, cmd, clientId) => {
|
|
71
|
-
commands.push({ conversationId, cmd, ...(clientId === undefined ? {} : { clientId }) });
|
|
72
|
-
},
|
|
73
|
-
onPeerJoined: (conversationId, pairingToken) => joined.push({ conversationId, token: pairingToken }),
|
|
74
|
-
onConversationGone: (conversationId) => gone.push(conversationId),
|
|
75
|
-
onClientLeft: (conversationId, clientId) => left.push({ conversationId, clientId }),
|
|
76
|
-
lookupPairingSlot: (token) => slots.resolveFor(token),
|
|
77
|
-
onPairReady: (token, ttlMs) => {
|
|
78
|
-
// 与 index.ts 的接法一致:服务端 TTL 改写本地过期。
|
|
79
|
-
if (!slots.applyServerTtl(token, ttlMs))
|
|
80
|
-
logs.push(`pair-ready for an unknown token ${token}`);
|
|
81
|
-
},
|
|
82
|
-
onState: (info) => states.push(info),
|
|
83
|
-
});
|
|
84
|
-
attachSocket(client, socket);
|
|
85
|
-
const out = () => socket.sent.map((text) => JSON.parse(text));
|
|
86
|
-
return {
|
|
87
|
-
clock,
|
|
88
|
-
slots,
|
|
89
|
-
client,
|
|
90
|
-
socket,
|
|
91
|
-
out,
|
|
92
|
-
outOf: (type) => out().filter((frame) => frame.t === type),
|
|
93
|
-
feed: (frame) => feedFrame(client, JSON.stringify(frame)),
|
|
94
|
-
feedText: (text) => feedFrame(client, text),
|
|
95
|
-
joined,
|
|
96
|
-
gone,
|
|
97
|
-
left,
|
|
98
|
-
commands,
|
|
99
|
-
states,
|
|
100
|
-
logs,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function attachSocket(client, socket) {
|
|
104
|
-
;
|
|
105
|
-
client.socket = socket;
|
|
106
|
-
}
|
|
107
|
-
function feedFrame(client, text) {
|
|
108
|
-
;
|
|
109
|
-
client.onFrame(text);
|
|
110
|
-
}
|
|
111
|
-
/** 手机侧那一本镜像密钥:它拿 PSK + convId 自己派生,主机看不到它的密钥。 */
|
|
112
|
-
function phoneKeys(psk, conversationId) {
|
|
113
|
-
return {
|
|
114
|
-
kC2H: derivePskKey(psk, 'c2h', conversationId),
|
|
115
|
-
kH2C: derivePskKey(psk, 'h2c', conversationId),
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
test('发布的 pair-begin 只带 6 位码,PSK 一个字节都不上过网(D1:中继结构性零知识)', () => {
|
|
119
|
-
const { client, slots, outOf } = harness();
|
|
120
|
-
const slot = slots.create(120_000);
|
|
121
|
-
client.publishPairing(slot);
|
|
122
|
-
const frames = outOf('pair-begin');
|
|
123
|
-
assert.equal(frames.length, 1, '一条 pair-begin 都没发出去:手机扫码后永远等不到配对结果');
|
|
124
|
-
const frame = frames[0];
|
|
125
|
-
assert.equal(frame.pairingToken, slot.token, 'pairingToken 必须是这张码本身:中继按它路由并回显');
|
|
126
|
-
const text = JSON.stringify(frame);
|
|
127
|
-
assert.equal(text.includes('psk'), false, 'pair-begin 里出现了 psk 字段:中继内存里就会多一份密钥副本(D1 的出发点)');
|
|
128
|
-
assert.equal(text.includes(slot.psk), false, 'PSK 真值被发了出去:零知识声明与实现不一致');
|
|
129
|
-
});
|
|
130
|
-
test('pair-ready 的 ttlMs 必须改写本地过期:忘了这一步就是"配对成功但列表永远空白"', async () => {
|
|
131
|
-
const { client, clock, slots, feed, joined } = harness();
|
|
132
|
-
const slot = slots.create(1_000); // 本地只给 1 秒
|
|
133
|
-
client.publishPairing(slot);
|
|
134
|
-
await clock.advance(5_000); // 本地认为它早过期了
|
|
135
|
-
assert.equal(slot.expiresAt < clock.now(), true, '夹具自检:此刻本地已判过期');
|
|
136
|
-
// 服务端说还能用两分钟——权威值必须覆盖本地值。
|
|
137
|
-
feed({ t: 'pair-ready', pairingToken: slot.token, ttlMs: 120_000 });
|
|
138
|
-
assert.equal(slots.resolveFor(slot.token) !== null, true, '服务端 TTL 没有改写本地过期:主机会在配对发生之前就把 PSK 清掉');
|
|
139
|
-
feed({ t: 'peer-joined', sessionId: 'c_aaa111222333', clientId: 'k_mp', pairingToken: slot.token });
|
|
140
|
-
assert.equal(joined.length, 1, 'peer-joined 取不到 PSK 被静默丢弃 → 手机显示配对成功、会话列表却是空的');
|
|
141
|
-
assert.equal(client.hasPeer('c_aaa111222333'), true, '会话没建起来:后续每一条下行都返回 false');
|
|
142
|
-
});
|
|
143
|
-
test('peer-joined 缺 pairingToken 时不建会话(那是重连通知,不是新客户端加入)', () => {
|
|
144
|
-
const { client, slots, feed, joined } = harness();
|
|
145
|
-
slots.create(120_000);
|
|
146
|
-
feed({ t: 'peer-joined', sessionId: 'c_bbb111222333', clientId: 'k_mp' });
|
|
147
|
-
assert.equal(joined.length, 0, '没有 token 也建了会话:那是拿"最新那张码"的 PSK 猜密钥(多码事故的形状)');
|
|
148
|
-
assert.equal(client.conversationCount, 0, `凭空建了 ${client.conversationCount} 条会话`);
|
|
149
|
-
assert.equal(client.hasPeer('c_bbb111222333'), false, 'hasPeer 谎报有对端');
|
|
150
|
-
});
|
|
151
|
-
test('多码并存时按手机实际使用的那张码取 PSK:用 A 码建的会话必须用 A 的密钥(旧事故回归)', async () => {
|
|
152
|
-
const { client, clock, slots, feed, joined } = harness();
|
|
153
|
-
const older = slots.create(120_000); // 自动补发的那一张
|
|
154
|
-
await clock.advance(10);
|
|
155
|
-
const newer = slots.create(120_000); // 用户又手工 /drc pair 了一张
|
|
156
|
-
assert.equal(older.token !== newer.token, true, '夹具自检:两张码必须不同');
|
|
157
|
-
assert.equal(slots.latest()?.token, newer.token, '夹具自检:latest() 会给出更新的那一张(旧实现就是取了它)');
|
|
158
|
-
feed({ t: 'peer-joined', sessionId: 'c_ccc111222333', clientId: 'k_mp', pairingToken: older.token });
|
|
159
|
-
assert.equal(joined.length, 1, '用较早那张码配对被拒了:手机扫的是它屏幕上那张,不是最新那张');
|
|
160
|
-
const conversation = client.conversations.get('c_ccc111222333');
|
|
161
|
-
assert.ok(conversation !== undefined, '会话没建出来');
|
|
162
|
-
assert.equal(conversation.psk, older.psk, `取到了错的 PSK(期望 older=${older.psk},实际 ${conversation.psk})`);
|
|
163
|
-
// 最硬的一条:手机那一侧用 A 的 PSK 派生密钥,两侧必须能互解。
|
|
164
|
-
const phone = phoneKeys(older.psk, 'c_ccc111222333');
|
|
165
|
-
const wrongPhone = phoneKeys(newer.psk, 'c_ccc111222333');
|
|
166
|
-
const payload = { t: PAYLOAD_TYPES.evResult, cmdId: 'cmd_1', ok: true };
|
|
167
|
-
assert.equal(client.send('c_ccc111222333', payload), true, '下行发送失败');
|
|
168
|
-
const frame = client.conversations.get('c_ccc111222333');
|
|
169
|
-
assert.ok(frame !== undefined);
|
|
170
|
-
// 把刚才那条 outbound 密文取回来,用手机的密钥解:解不开就是"配对成功、每一帧都解不开"。
|
|
171
|
-
const sent = JSON.parse(String(client.socket.sent.at(-1)));
|
|
172
|
-
assert.equal(open(phone.kH2C, { ciphertext: sent.ciphertext }) !== null, true, '手机用 A 码的 PSK 解不开主机下行 → 会话列表空白、没有任何提示');
|
|
173
|
-
assert.equal(open(wrongPhone.kH2C, { ciphertext: sent.ciphertext }), null, '拿错码(B)的密钥竟然解得开:说明密钥没绑定到所用那张码');
|
|
174
|
-
});
|
|
175
|
-
test('上行命令用同一张码的 c2h 密钥解密并交给 runtime(两把方向密钥不许混用)', () => {
|
|
176
|
-
const { client, slots, feed, commands } = harness();
|
|
177
|
-
const slot = slots.create(120_000);
|
|
178
|
-
feed({ t: 'peer-joined', sessionId: 'c_ddd111222333', clientId: 'k_mp', pairingToken: slot.token });
|
|
179
|
-
const phone = phoneKeys(slot.psk, 'c_ddd111222333');
|
|
180
|
-
const cmd = { t: PAYLOAD_TYPES.cmdListSessions, cmdId: 'cmd_ls_1' };
|
|
181
|
-
feed({ t: 'enc', sessionId: 'c_ddd111222333', clientId: 'k_mp', ciphertext: seal(phone.kC2H, cmd).ciphertext });
|
|
182
|
-
assert.equal(commands.length, 1, '解得开却没交给 runtime:手机点了"刷新列表"没有任何反应');
|
|
183
|
-
assert.equal(commands[0]?.cmd.t, PAYLOAD_TYPES.cmdListSessions, '命令类型丢了');
|
|
184
|
-
assert.equal((commands[0]?.cmd).cmdId, 'cmd_ls_1', 'cmdId 必须逐字交给 runtime:回执对答靠它');
|
|
185
|
-
assert.equal(commands[0]?.clientId, 'k_mp', 'clientId 要带上来(D4 的成员校验要用)');
|
|
186
|
-
// 用错方向的密钥密封:主机解不开,但绝不能崩。
|
|
187
|
-
feed({ t: 'enc', sessionId: 'c_ddd111222333', ciphertext: seal(phone.kH2C, cmd).ciphertext });
|
|
188
|
-
assert.equal(commands.length, 1, '方向搞反的密文被当成合法命令放行了:B5 的方向分离就白做了');
|
|
189
|
-
});
|
|
190
|
-
test('MAC 校验通过但载荷不是任何一条已定义命令:不进 runtime,也不许攒成"解不开两次就作废"', () => {
|
|
191
|
-
const { client, slots, feed, commands, logs } = harness();
|
|
192
|
-
const slot = slots.create(120_000);
|
|
193
|
-
feed({ t: 'peer-joined', sessionId: 'c_dtd111222333', clientId: 'k_mp', pairingToken: slot.token });
|
|
194
|
-
const phone = phoneKeys(slot.psk, 'c_dtd111222333');
|
|
195
|
-
// 手机(或被改过的客户端)密封了一条我们没定义过的命令。
|
|
196
|
-
feed({
|
|
197
|
-
t: 'enc',
|
|
198
|
-
sessionId: 'c_dtd111222333',
|
|
199
|
-
ciphertext: seal(phone.kC2H, { t: 'cmd.drop_everything', cmdId: 'x_1' }).ciphertext,
|
|
200
|
-
});
|
|
201
|
-
assert.equal(commands.length, 0, '未定义的载荷被原样递进 runtime:下游是 switch + 真实内核调用,等于让一条密文决定宿主被怎么调');
|
|
202
|
-
assert.ok(logs.some((line) => line.includes('failed cmd schema')), '拒收必须留痕:否则"手机点了没反应"没有答案');
|
|
203
|
-
assert.equal(client.hasPeer('c_dtd111222333'), true, '形状错不是密钥错,不该计入"连续两次解不开就作废"');
|
|
204
|
-
// 同一条通道上的合法命令仍然进得来(这次拒收没有把通道打死)。
|
|
205
|
-
feed({
|
|
206
|
-
t: 'enc',
|
|
207
|
-
sessionId: 'c_dtd111222333',
|
|
208
|
-
ciphertext: seal(phone.kC2H, { t: PAYLOAD_TYPES.cmdListSessions, cmdId: 'cmd_ok' }).ciphertext,
|
|
209
|
-
});
|
|
210
|
-
assert.equal(commands.length, 1, '一次形状拒收之后整条通道都哑了');
|
|
211
|
-
});
|
|
212
|
-
test('enc-batch 逐项解密并保持数组顺序(F12/T3:客户端没有排序能力)', () => {
|
|
213
|
-
const { client, slots, feed, commands } = harness();
|
|
214
|
-
const slot = slots.create(120_000);
|
|
215
|
-
feed({ t: 'peer-joined', sessionId: 'c_eee111222333', clientId: 'k_mp', pairingToken: slot.token });
|
|
216
|
-
const phone = phoneKeys(slot.psk, 'c_eee111222333');
|
|
217
|
-
const items = ['a', 'b', 'c'].map((tag) => ({
|
|
218
|
-
seq: 1,
|
|
219
|
-
ciphertext: seal(phone.kC2H, {
|
|
220
|
-
t: PAYLOAD_TYPES.cmdInterrupt,
|
|
221
|
-
cmdId: `cmd_${tag}`,
|
|
222
|
-
sessionId: 'ses_x',
|
|
223
|
-
}).ciphertext,
|
|
224
|
-
}));
|
|
225
|
-
feed({ t: 'enc-batch', sessionId: 'c_eee111222333', items });
|
|
226
|
-
assert.ok(commands.length >= 3, `批量帧解出 ${commands.length} 条:整批丢弃或漏项都会让手机的操作"点了一下没反应"`);
|
|
227
|
-
assert.deepEqual(commands.map((item) => item.cmd.cmdId), ['cmd_a', 'cmd_b', 'cmd_c'], '批量帧内的顺序变了:同一会话的中断与发指令谁先谁后不可推断(T3 要求串行投递)');
|
|
228
|
-
});
|
|
229
|
-
test('hello-ok 之后 generation 递增:旧代配对全部作废并通知上层(中继重启后旧 convId 没人路由了)', () => {
|
|
230
|
-
const { client, slots, feed, gone, outOf } = harness();
|
|
231
|
-
feed({ t: 'hello-ok', role: 'host', hostId: 'h_test' });
|
|
232
|
-
assert.equal(client.conversationCount, 0, '夹具自检:新连接还没有会话');
|
|
233
|
-
const slot = slots.create(120_000);
|
|
234
|
-
feed({ t: 'peer-joined', sessionId: 'c_fff111222333', clientId: 'k_mp', pairingToken: slot.token });
|
|
235
|
-
assert.equal(client.hasPeer('c_fff111222333'), true, '夹具自检:会话已建立');
|
|
236
|
-
// 中继重启:第二次 hello-ok 意味着新的代次。
|
|
237
|
-
feed({ t: 'hello-ok', role: 'host', hostId: 'h_test' });
|
|
238
|
-
assert.equal(client.conversationCount, 0, `代次变了还留着旧会话(${client.conversationCount} 条):主机对着一个不存在的通道发密文`);
|
|
239
|
-
assert.deepEqual(gone, ['c_fff111222333'], '没有通知上层作废:手机端的"会话已失效,请重新配对"中文提示就发不出去');
|
|
240
|
-
assert.equal(client.send('c_fff111222333', { t: PAYLOAD_TYPES.evRunState, sessionId: 'ses_x', state: 'running' }), false, '旧会话还能发出去');
|
|
241
|
-
const resync = outOf('resync');
|
|
242
|
-
assert.ok(resync.length >= 1, '鉴权成功后没发 resync:中继不知道该保留哪些会话(复核 R1 的恢复路径)');
|
|
243
|
-
assert.deepEqual(resync[0].sessionIds, [], 'resync 应该只列出本端仍持有密钥的会话');
|
|
244
|
-
});
|
|
245
|
-
test('连续两次解不开同一会话就主动作废:对称于手机端"两帧解不开就丢配对"', () => {
|
|
246
|
-
const { client, slots, feed, gone } = harness();
|
|
247
|
-
const slot = slots.create(120_000);
|
|
248
|
-
feed({ t: 'peer-joined', sessionId: 'c_000111222333', clientId: 'k_mp', pairingToken: slot.token });
|
|
249
|
-
// 一条形状合法(标准 base64)、MAC 解不开的密文——像是手机端密钥已经被换掉。
|
|
250
|
-
const junk = Buffer.alloc(24 + 16 + 4, 7).toString('base64');
|
|
251
|
-
feed({ t: 'enc', sessionId: 'c_000111222333', ciphertext: junk });
|
|
252
|
-
assert.equal(client.hasPeer('c_000111222333'), true, '一次解不开就丢配对太激进:小程序自己也给两帧机会');
|
|
253
|
-
assert.equal(gone.length, 0, '第一次就通知作废了');
|
|
254
|
-
feed({ t: 'enc', sessionId: 'c_000111222333', ciphertext: junk });
|
|
255
|
-
assert.equal(client.conversationCount, 0, '两帧都解不开却还留着通道:手机会对着一个听不见的对端一直说话');
|
|
256
|
-
assert.deepEqual(gone, ['c_000111222333'], '必须通知上层,让手机端看到"请重新配对"的中文提示');
|
|
257
|
-
const leave = client.conversations.get('c_000111222333');
|
|
258
|
-
assert.equal(leave, undefined, '密钥簿里还留着这条会话');
|
|
259
|
-
});
|
|
260
|
-
test('解不开计数在一帧成功解密后清零:偶发噪声不该攒成一次误废弃', () => {
|
|
261
|
-
const { client, slots, feed, gone } = harness();
|
|
262
|
-
const slot = slots.create(120_000);
|
|
263
|
-
feed({ t: 'peer-joined', sessionId: 'c_111222333444', clientId: 'k_mp', pairingToken: slot.token });
|
|
264
|
-
const phone = phoneKeys(slot.psk, 'c_111222333444');
|
|
265
|
-
const junk = Buffer.alloc(24 + 16 + 4, 9).toString('base64');
|
|
266
|
-
feed({ t: 'enc', sessionId: 'c_111222333444', ciphertext: junk });
|
|
267
|
-
feed({
|
|
268
|
-
t: 'enc',
|
|
269
|
-
sessionId: 'c_111222333444',
|
|
270
|
-
ciphertext: seal(phone.kC2H, { t: PAYLOAD_TYPES.cmdListSessions, cmdId: 'cmd_ok' }).ciphertext,
|
|
271
|
-
});
|
|
272
|
-
feed({ t: 'enc', sessionId: 'c_111222333444', ciphertext: junk });
|
|
273
|
-
assert.equal(gone.length, 0, '中间成功过一帧,却被当成连续两次解不开而废弃:会话被误删,手机要重新扫码');
|
|
274
|
-
assert.equal(client.hasPeer('c_111222333444'), true, '会话不该被误废');
|
|
275
|
-
});
|
|
276
|
-
test('base64 字符集不合法的密文只被拒收,不计入解不开次数(严进但不能因此丢配对)', () => {
|
|
277
|
-
const { client, slots, feed, gone, logs } = harness();
|
|
278
|
-
const slot = slots.create(120_000);
|
|
279
|
-
feed({ t: 'peer-joined', sessionId: 'c_222333444555', clientId: 'k_mp', pairingToken: slot.token });
|
|
280
|
-
for (const bad of ['####not-base64####', '', 'a']) {
|
|
281
|
-
feed({ t: 'enc', sessionId: 'c_222333444555', ciphertext: bad.length === 0 ? 'AAAA' : bad });
|
|
282
|
-
}
|
|
283
|
-
assert.equal(gone.length, 0, `形状非法的帧被计成了"解不开"并导致废弃配对(gone=${JSON.stringify(gone)}):中继早就该拦下它`);
|
|
284
|
-
assert.equal(client.hasPeer('c_222333444555'), true, '会话必须还在');
|
|
285
|
-
assert.ok(logs.length >= 0, '夹具自检:日志通道可用');
|
|
286
|
-
});
|
|
287
|
-
test('peer-left 只摘掉那个客户端,会话与密钥必须留着(D3:手机回前台不该要求重新扫码)', () => {
|
|
288
|
-
const { client, slots, feed, left, out } = harness();
|
|
289
|
-
const slot = slots.create(120_000);
|
|
290
|
-
feed({ t: 'peer-joined', sessionId: 'c_333444555666', clientId: 'k_mp', pairingToken: slot.token });
|
|
291
|
-
assert.equal(client.conversationCount, 1, '夹具自检:会话已建立');
|
|
292
|
-
feed({ t: 'peer-left', sessionId: 'c_333444555666', clientId: 'k_mp' });
|
|
293
|
-
assert.deepEqual(left, [{ conversationId: 'c_333444555666', clientId: 'k_mp' }], '离开通知没交给上层:runtime 不知道该推一次列表');
|
|
294
|
-
assert.equal(client.hasPeer('c_333444555666'), true, '客户端一走就把会话删了 → 手机切后台再回来必须重新扫码(旧实现的产品缺陷)');
|
|
295
|
-
assert.equal(client.conversations.get('c_333444555666')?.psk, slot.psk, '密钥跟着丢了:同一条 convId 回来时再也解不开');
|
|
296
|
-
assert.equal(out().some((frame) => frame.t === 'session-leave'), false, '不该因为 peer-left 就主动退出会话');
|
|
297
|
-
const sent = client.send('c_333444555666', {
|
|
298
|
-
t: PAYLOAD_TYPES.evRunState,
|
|
299
|
-
sessionId: 'ses_x',
|
|
300
|
-
state: 'running',
|
|
301
|
-
});
|
|
302
|
-
assert.equal(sent, true, '会话还在却发不出去:出站路径被 peer-left 悄悄断了');
|
|
303
|
-
});
|
|
304
|
-
test('主动作废已知会话:发 session-leave + 通知上层,且同一条通道只声明一次', () => {
|
|
305
|
-
const { client, slots, feed, gone, outOf } = harness();
|
|
306
|
-
const slot = slots.create(120_000);
|
|
307
|
-
feed({ t: 'peer-joined', sessionId: 'c_444555666777', clientId: 'k_mp', pairingToken: slot.token });
|
|
308
|
-
client.voidConversation('c_444555666777');
|
|
309
|
-
assert.deepEqual(gone, ['c_444555666777'], '/drc unpair 之后上层不知道:手机还会以为配对仍在');
|
|
310
|
-
const leave = outOf('session-leave');
|
|
311
|
-
assert.equal(leave.length, 1, '没发 session-leave:中继会继续把这条会话的密文转过来');
|
|
312
|
-
assert.equal(leave[0].sessionId, 'c_444555666777', '退出的必须是配对通道 id(c_ 前缀),不是 DSH 会话 id(F3)');
|
|
313
|
-
client.voidConversation('c_444555666777'); // 同一原因再来一次
|
|
314
|
-
assert.equal(leave.length, 1, `重复作废多发了 ${leave.length - 1} 条 session-leave:必须幂等`);
|
|
315
|
-
assert.equal(gone.length, 1, '重复作废重复回调 gone:上层会收到两次"配对失效"');
|
|
316
|
-
});
|
|
317
|
-
/**
|
|
318
|
-
* 复核 R1①:本端**没有**这条通道的记录时也必须声明 `session-leave`。
|
|
319
|
-
*
|
|
320
|
-
* 场景是这条链路上最难查的一个:主机进程重启 → 内存里的 PSK 全没了,中继却不知道,
|
|
321
|
-
* 于是它继续把手机发来的密文转给一个解不开的对端。手机既等不到回复,也等不到
|
|
322
|
-
* `unknown_session`(那个码只由"中继路由表未命中"产生,而这里表是命中的),
|
|
323
|
-
* 表现是"发什么都没反应、也不提示重新配对"——整条链路上唯一的出口就是主机自己声明作废。
|
|
324
|
-
* 第一版这里写的是 `if (!has(id)) return`,等于把出口堵死了。
|
|
325
|
-
*/
|
|
326
|
-
test('对本端没有记录的 convId 也必须发 session-leave(主机重启后手机唯一的出口,复核 R1①)', () => {
|
|
327
|
-
const { client, gone, outOf } = harness();
|
|
328
|
-
client.voidConversation('c_重启之后不认识的会话');
|
|
329
|
-
const leave = outOf('session-leave');
|
|
330
|
-
assert.equal(leave.length, 1, '没钥匙又不声明:中继会一直把手机的密文转给一个解不开的对端,手机永远转圈');
|
|
331
|
-
assert.equal(leave[0].sessionId, 'c_重启之后不认识的会话', '作废的必须是手机在用的那条 convId');
|
|
332
|
-
assert.deepEqual(gone, ['c_重启之后不认识的会话'], '上层不知道这条已经死了:status.json 与自动补发都不会动');
|
|
333
|
-
});
|
|
334
|
-
/** 同一条路上更常见的触发方式不是有人调 API,而是手机发来一帧本端没有钥匙的密文。 */
|
|
335
|
-
test('enc 打到一个本端不认识的 convId:立刻作废并声明,而不是静默丢帧', () => {
|
|
336
|
-
const { client, slots, feed, gone, outOf } = harness();
|
|
337
|
-
const slot = slots.create(120_000);
|
|
338
|
-
feed({ t: 'peer-joined', sessionId: 'c_aabbccddeeff', clientId: 'k_mp', pairingToken: slot.token });
|
|
339
|
-
client.conversations.close('c_aabbccddeeff'); // 模拟主机重启:内存里的密钥簿空了
|
|
340
|
-
assert.equal(client.conversationCount, 0, '夹具自检:此刻本端一条会话都不该有');
|
|
341
|
-
// 手机仍在旧 convId 上发。密文内容无所谓——本端连密钥都没有,走的是"查不到会话"那条分支。
|
|
342
|
-
feed({ t: 'enc', sessionId: 'c_ffffffff0000', seq: 1, ciphertext: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' });
|
|
343
|
-
assert.equal(outOf('session-leave').length, 1, '解不开又不声明:手机对着一个不存在密钥的通道说话');
|
|
344
|
-
assert.deepEqual(gone, ['c_ffffffff0000'], '上层没被告知这条通道作废');
|
|
345
|
-
assert.equal(client.hasPeer('c_ffffffff0000'), false, '不该顺手把陌生 convId 建成本端会话');
|
|
346
|
-
});
|
|
347
|
-
test('pair-fail 与未知帧名都要留痕:中继侧加一个名字,主机不许永远是哑的(F1)', () => {
|
|
348
|
-
const { client, slots, feed, logs } = harness();
|
|
349
|
-
slots.create(120_000);
|
|
350
|
-
feed({ t: 'pair-fail', reason: 'already_used' });
|
|
351
|
-
assert.ok(logs.some((line) => line.includes('pair failed') && line.includes('already_used')), `pair-fail 无人处理也不留痕(日志:${JSON.stringify(logs)}):手机扫完码主机侧完全无痕`);
|
|
352
|
-
// 未定义的帧名:解析层就拒了,同样要留下一条"收到但丢弃"的痕迹。
|
|
353
|
-
feed({ t: 'a-frame-we-do-not-know-yet' });
|
|
354
|
-
assert.ok(logs.some((line) => line.includes('relay frame rejected')), '坏帧/未知帧静默蒸发:线上只能靠猜');
|
|
355
|
-
assert.equal(client.conversationCount, 0, '上面两帧都不该建出会话');
|
|
356
|
-
});
|
|
357
|
-
test('出站 seq 逐帧递增且外层 sessionId 恒为配对通道 id(F3 的双 sessionId 不许混)', () => {
|
|
358
|
-
const { client, slots, feed } = harness();
|
|
359
|
-
const slot = slots.create(120_000);
|
|
360
|
-
feed({ t: 'peer-joined', sessionId: 'c_555666777888', clientId: 'k_mp', pairingToken: slot.token });
|
|
361
|
-
const socket = client.socket;
|
|
362
|
-
socket.sent.length = 0;
|
|
363
|
-
const payload = {
|
|
364
|
-
t: PAYLOAD_TYPES.evMessageDelta,
|
|
365
|
-
sessionId: 'ses_dsh_1',
|
|
366
|
-
messageId: 'msg_1',
|
|
367
|
-
delta: '你好',
|
|
368
|
-
};
|
|
369
|
-
client.send('c_555666777888', payload);
|
|
370
|
-
client.send('c_555666777888', payload);
|
|
371
|
-
const frames = socket.sent.map((text) => JSON.parse(text));
|
|
372
|
-
assert.equal(frames.length, 2, '两条下行只发出一条:手机会少一段文本');
|
|
373
|
-
assert.deepEqual(frames.map((frame) => frame.seq), [1, 2], `seq 序列是 ${JSON.stringify(frames.map((f) => f.seq))}:本端序号是"顺序可核对"的唯一依据`);
|
|
374
|
-
assert.deepEqual(frames.map((frame) => frame.sessionId), ['c_555666777888', 'c_555666777888'], '外层 sessionId 必须是 convId:写成 DSH 会话 id 中继就路由不回去');
|
|
375
|
-
assert.equal(JSON.stringify(frames).includes('ses_dsh_1'), false, '载荷里的 DSH 会话 id 泄漏到明文帧上了:中继会看到业务明文(红线 24)');
|
|
376
|
-
});
|
|
377
|
-
test('broadcast 只对活着的会话计数:断连期间不许假装"已发送"', () => {
|
|
378
|
-
const { client, slots, feed } = harness();
|
|
379
|
-
const first = slots.create(120_000);
|
|
380
|
-
const second = slots.create(120_000);
|
|
381
|
-
feed({ t: 'peer-joined', sessionId: 'c_666777888999', clientId: 'k1', pairingToken: first.token });
|
|
382
|
-
feed({ t: 'peer-joined', sessionId: 'c_777888999aaa', clientId: 'k2', pairingToken: second.token });
|
|
383
|
-
const payload = { t: PAYLOAD_TYPES.evSessionChanged, sessions: [] };
|
|
384
|
-
assert.equal(client.broadcast(payload), 2, '两条会话都该收到广播');
|
|
385
|
-
client.voidConversation('c_777888999aaa');
|
|
386
|
-
assert.equal(client.broadcast(payload), 1, '作废之后广播数没降下来:调用方以为还有人在听');
|
|
387
|
-
});
|
|
388
|
-
test('畸形帧与未知帧名一律静默忽略且绝不抛:一条坏帧不该打断消息处理链', () => {
|
|
389
|
-
const { client, feedText } = harness();
|
|
390
|
-
assert.doesNotThrow(() => feedText('{not json'), '非法 JSON 把处理链抛断了:中继那边只是发了一帧脏数据');
|
|
391
|
-
assert.doesNotThrow(() => feedText(JSON.stringify({ t: 'totally-new-frame', x: 1 })), '未知帧名抛了出去:F1 要求新帧名必须被静默忽略');
|
|
392
|
-
assert.doesNotThrow(() => feedText(JSON.stringify({ t: 'pong', ts: 1 })), 'pong 必须被接受并忽略(T4:应用层不发心跳)');
|
|
393
|
-
assert.doesNotThrow(() => feedText(JSON.stringify({ t: 'paired', sessionId: 'c_x', hostId: 'h_test' })), 'paired 是发给客户端的,主机侧必须忽略');
|
|
394
|
-
assert.doesNotThrow(() => feedText(JSON.stringify({ t: 'error', code: 'unknown_session' })), 'error 帧要记进状态而不是抛');
|
|
395
|
-
assert.equal(client.conversationCount, 0, '上面这些帧不该建出任何会话');
|
|
396
|
-
});
|
|
397
|
-
test('error 帧只进日志与状态,不主动清会话:清不清由 enc 路径判定(哪条通道只有那里知道)', () => {
|
|
398
|
-
const { client, slots, feed, gone, logs } = harness();
|
|
399
|
-
const slot = slots.create(120_000);
|
|
400
|
-
feed({ t: 'peer-joined', sessionId: 'c_888999aaabbb', clientId: 'k_mp', pairingToken: slot.token });
|
|
401
|
-
feed({ t: 'error', code: 'bad_token', message: 'token 不对' });
|
|
402
|
-
assert.equal(gone.length, 0, '一条 error 帧就把配对清了:手机上表现为"莫名其妙要重新扫码"');
|
|
403
|
-
assert.equal(client.hasPeer('c_888999aaabbb'), true, '会话必须还在');
|
|
404
|
-
assert.ok(logs.some((line) => line.includes('bad_token')), '中继错误要进日志:否则"为什么没反应"没有答案(status.json 的 relayProblem 同源)');
|
|
405
|
-
});
|
|
406
|
-
test('stop() 之后一切出站都如实返回 false:调用方据此知道没人能收', () => {
|
|
407
|
-
const { client, slots, feed, states } = harness();
|
|
408
|
-
const slot = slots.create(120_000);
|
|
409
|
-
feed({ t: 'peer-joined', sessionId: 'c_999aabbbccc0', clientId: 'k_mp', pairingToken: slot.token });
|
|
410
|
-
assert.equal(client.send('c_999aabbbccc0', { t: PAYLOAD_TYPES.evRunState, state: 'idle' }), true, '夹具自检:停机前发得出去');
|
|
411
|
-
client.stop();
|
|
412
|
-
assert.equal(client.send('c_999aabbbccc0', { t: PAYLOAD_TYPES.evRunState, state: 'idle' }), false, 'socket 已关却返回 true:旧实现所有调用点都忽略返回值,这里必须诚实');
|
|
413
|
-
assert.equal(client.broadcast({ t: PAYLOAD_TYPES.evRunState, state: 'idle' }), 0, '停机后广播数必须为 0');
|
|
414
|
-
const last = states[states.length - 1];
|
|
415
|
-
assert.equal(last?.relay, 'offline', `停机后的状态是 ${String(last?.relay)}:status.json 会一直谎报 online`);
|
|
416
|
-
});
|
|
417
|
-
test('ConversationBook 与 PairingSlots 的协作:同一条 convId 重复 joined 必须换回新密钥', () => {
|
|
418
|
-
const { client, slots, feed } = harness();
|
|
419
|
-
const older = slots.create(120_000);
|
|
420
|
-
const newer = slots.create(120_000);
|
|
421
|
-
feed({ t: 'peer-joined', sessionId: 'c_aaabbcccddd', clientId: 'k1', pairingToken: older.token });
|
|
422
|
-
const before = client.conversations.get('c_aaabbcccddd')?.psk;
|
|
423
|
-
feed({ t: 'peer-joined', sessionId: 'c_aaabbcccddd', clientId: 'k2', pairingToken: newer.token });
|
|
424
|
-
const after = client.conversations.get('c_aaabbcccddd');
|
|
425
|
-
assert.equal(after?.psk, newer.psk, `同一条 convId 的二次 joined 没有换钥(before=${before} after=${after?.psk})`);
|
|
426
|
-
assert.equal(client.conversationCount, 1, '一条 convId 攒出了多条会话:簿记键必须是 convId 本身');
|
|
427
|
-
// 手机此刻拿的是新那张码的 PSK:主机下行必须能用它解开。
|
|
428
|
-
const socket = client.socket;
|
|
429
|
-
socket.sent.length = 0;
|
|
430
|
-
client.send('c_aaabbcccddd', { t: PAYLOAD_TYPES.evRunState, sessionId: 'ses_x', state: 'idle' });
|
|
431
|
-
const frame = JSON.parse(socket.sent[0]);
|
|
432
|
-
assert.equal(open(phoneKeys(newer.psk, 'c_aaabbcccddd').kH2C, { ciphertext: frame.ciphertext }) !== null, true, '换钥之后下行仍用旧钥密封:手机解不开');
|
|
433
|
-
});
|
|
434
|
-
test('中继来帧的 sessionId 逐字用作会话键(F3:两套 id 一旦混用整页无输出)', () => {
|
|
435
|
-
const { client, slots, feed } = harness();
|
|
436
|
-
const slot = slots.create(120_000);
|
|
437
|
-
const convId = 'c_0123456789ab';
|
|
438
|
-
feed({ t: 'peer-joined', sessionId: convId, clientId: 'k_mp', pairingToken: slot.token });
|
|
439
|
-
assert.equal(client.conversations.ids().length, 1, '会话没建出来');
|
|
440
|
-
assert.equal(client.conversations.ids()[0], convId, '会话键被加工过(加/去前缀都会让路由对不上)');
|
|
441
|
-
assert.equal(client.conversationIds()[0], convId, 'conversationIds() 必须原样透出:runtime 靠它挑通道发审批卡');
|
|
442
|
-
assert.equal(String(client.conversations.ids()[0]).startsWith('c_'), true, '前缀 c_ 是冻结字符串(B9)');
|
|
443
|
-
});
|
|
444
|
-
test('状态回调如实汇报会话数与代次:status.json 的 conversations/generation 就是从这来的', () => {
|
|
445
|
-
const { client, slots, feed, states } = harness();
|
|
446
|
-
feed({ t: 'hello-ok', role: 'host', hostId: 'h_test' });
|
|
447
|
-
const slot = slots.create(120_000);
|
|
448
|
-
feed({ t: 'peer-joined', sessionId: 'c_bbccddeeff00', clientId: 'k_mp', pairingToken: slot.token });
|
|
449
|
-
const last = states[states.length - 1];
|
|
450
|
-
assert.ok(last !== undefined, '一次状态都没回调:status.json 里 relay 会永远停在 connecting');
|
|
451
|
-
assert.equal(last.relay, 'online', `状态是 ${last.relay}:手机上"连不上"与"配上了但一切不通"看起来一模一样`);
|
|
452
|
-
assert.equal(last.conversations, 1, '会话数不对');
|
|
453
|
-
assert.equal(last.generation, 1, '代次不对:发布循环靠它判断中继重启过没有');
|
|
454
|
-
});
|
|
455
|
-
test('配对槽位簿记与传输层用的是同一张表:pair-ready 对未知 token 要留下痕迹', () => {
|
|
456
|
-
const { feed, slots, logs } = harness();
|
|
457
|
-
feed({ t: 'pair-ready', pairingToken: '424242', ttlMs: 60_000 });
|
|
458
|
-
assert.equal(slots.resolveFor('424242'), null, '夹具自检:本地根本没有这张码');
|
|
459
|
-
assert.ok(logs.some((line) => line.includes('pair-ready for an unknown token')), '服务端为一张我们没发过的码发来了 TTL:这必须留痕,否则配对流程出现了第二份真相');
|
|
460
|
-
});
|
|
461
|
-
/**
|
|
462
|
-
* 配对通道剪枝。D3 让通道跨断连长存,而**中继侧的空闲 TTL 是 7 天**
|
|
463
|
-
* (`DRC_CONV_IDLE_TTL_MS`),所以"等中继先忘掉"等于永远不剪:
|
|
464
|
-
* 每天配一次对的主机两周就能攒两百多条 PSK,并且每次广播都对废弃通道逐个密封。
|
|
465
|
-
* 关键约束是:剪枝必须走 `voidConversation`(发 `session-leave` + 回调上层),
|
|
466
|
-
* 只删内存就等于复核 R1 那个静默黑洞换了个入口重来一次。
|
|
467
|
-
*/
|
|
468
|
-
test('空闲超时的通道被剪掉时必须发 session-leave 并通知上层(不能只删内存)', async () => {
|
|
469
|
-
const { client, clock, slots, feed, gone, outOf } = harness();
|
|
470
|
-
const first = slots.create(120_000);
|
|
471
|
-
feed({ t: 'peer-joined', sessionId: 'c_old000000001', clientId: 'k1', pairingToken: first.token });
|
|
472
|
-
// 第二条:刚活动过,不该被剪。
|
|
473
|
-
await clock.advance(10);
|
|
474
|
-
const second = slots.create(120_000);
|
|
475
|
-
feed({ t: 'peer-joined', sessionId: 'c_new000000002', clientId: 'k2', pairingToken: second.token });
|
|
476
|
-
await clock.advance(25 * 3600 * 1000); // 一天多没有任何收发
|
|
477
|
-
client.send('c_new000000002', {
|
|
478
|
-
t: PAYLOAD_TYPES.evKeepAwakeState,
|
|
479
|
-
enabled: true,
|
|
480
|
-
active: false,
|
|
481
|
-
platform: 'darwin',
|
|
482
|
-
backend: 'caffeinate',
|
|
483
|
-
});
|
|
484
|
-
const pruned = client.pruneConversations();
|
|
485
|
-
assert.deepEqual(pruned, ['c_old000000001'], `剪掉的应该是那条一天没动静的,实际剪了 ${JSON.stringify(pruned)}`);
|
|
486
|
-
assert.equal(client.hasPeer('c_old000000001'), false, '剪了内存却没关通道');
|
|
487
|
-
assert.deepEqual(gone, ['c_old000000001'], '上层不知道:status.json 与自动补发都不会动');
|
|
488
|
-
const leave = outOf('session-leave');
|
|
489
|
-
assert.equal(leave.length, 1, '没发 session-leave:中继会继续把手机的密文转给一条已经没有钥匙的通道');
|
|
490
|
-
assert.equal(leave[0].sessionId, 'c_old000000001');
|
|
491
|
-
assert.equal(client.hasPeer('c_new000000002'), true, '刚有收发的通道被顺手剪了');
|
|
492
|
-
});
|
|
493
|
-
test('条数上界是真上界:超出后从最不活跃的开始剪,而不是拒绝新建', () => {
|
|
494
|
-
const { client, clock, slots, feed } = harness();
|
|
495
|
-
const policy = { idleTtlMs: 24 * 3600 * 1000, maxConversations: 3 };
|
|
496
|
-
client.options.prunePolicy = policy;
|
|
497
|
-
for (let index = 0; index < 5; index++) {
|
|
498
|
-
const slot = slots.create(120_000);
|
|
499
|
-
feed({ t: 'peer-joined', sessionId: `c_cap${index}`, clientId: `k${index}`, pairingToken: slot.token });
|
|
500
|
-
void clock.advance(1); // 保证活跃时刻有先后
|
|
501
|
-
}
|
|
502
|
-
assert.equal(client.conversationCount, 5, '夹具自检:还没剪之前是 5 条');
|
|
503
|
-
// 关键一步:让**创建最早**的那条重新活跃。不这样做的话"按最后活动剪"与
|
|
504
|
-
// "按创建序剪"两种实现给出的结果一模一样,这条断言就什么都没守(变异验证抓出来的)。
|
|
505
|
-
assert.equal(client.send('c_cap0', {
|
|
506
|
-
t: PAYLOAD_TYPES.evKeepAwakeState,
|
|
507
|
-
enabled: true,
|
|
508
|
-
active: false,
|
|
509
|
-
platform: 'darwin',
|
|
510
|
-
backend: 'caffeinate',
|
|
511
|
-
}), true, '夹具自检:c_cap0 要有收发记录');
|
|
512
|
-
const pruned = client.pruneConversations();
|
|
513
|
-
assert.equal(pruned.length, 2, `上界 3 却剪了 ${pruned.length} 条`);
|
|
514
|
-
assert.deepEqual(pruned, ['c_cap1', 'c_cap2'], `必须按"最后活动"从最不活跃开始剪(刚有收发的 c_cap0 必须留下),实际剪了 ${JSON.stringify(pruned)}`);
|
|
515
|
-
assert.equal(client.conversationCount, 3);
|
|
516
|
-
assert.equal(client.hasPeer('c_cap0'), true, '刚发过数据的通道被剪:手机会突然撞上"会话已失效"');
|
|
517
|
-
});
|
|
518
|
-
test('停机时 socket 还停在 CONNECTING:close() 的异步 error 不许升级成 uncaughtException', async () => {
|
|
519
|
-
// 这条**必须用真 ws**(所以这里不 attachSocket)。机制来自 ws:socket 还在 CONNECTING
|
|
520
|
-
// 时 close() 走 abortHandshake,那条路径是 process.nextTick 抛 error ——
|
|
521
|
-
// 假 socket(EventEmitter)复现不了,而**同步的 try/catch 也拦不住异步抛出**。
|
|
522
|
-
// 于是 removeAllListeners() 摘掉 error 监听器之后它没人接,升级成 uncaughtException
|
|
523
|
-
// 把进程带崩。表现是 e2e 偶发红:'WebSocket was closed before the connection was
|
|
524
|
-
// established'(只有"连接还没建好就停机"这个窗口才触发,所以时红时绿,很难查)。
|
|
525
|
-
const caught = [];
|
|
526
|
-
const onUncaught = (e) => {
|
|
527
|
-
caught.push(e);
|
|
528
|
-
};
|
|
529
|
-
process.on('uncaughtException', onUncaught);
|
|
530
|
-
try {
|
|
531
|
-
const clock = new FakeClock();
|
|
532
|
-
const slots = new PairingSlots(() => clock.now());
|
|
533
|
-
const client = new RelayClient({
|
|
534
|
-
url: 'ws://127.0.0.1:1', // 没人监听 → 一直停在 CONNECTING
|
|
535
|
-
hostId: 'h_connecting',
|
|
536
|
-
label: '测试主机',
|
|
537
|
-
token: 'fake-host-token-not-a-real-secret-0123',
|
|
538
|
-
clock,
|
|
539
|
-
log: () => { },
|
|
540
|
-
onCommand: () => { },
|
|
541
|
-
onPeerJoined: () => { },
|
|
542
|
-
onConversationGone: () => { },
|
|
543
|
-
onClientLeft: () => { },
|
|
544
|
-
lookupPairingSlot: (token) => slots.resolveFor(token),
|
|
545
|
-
onPairReady: () => { },
|
|
546
|
-
onState: () => { },
|
|
547
|
-
});
|
|
548
|
-
client.connect();
|
|
549
|
-
client.stop(); // 就在"连接还没建好"这个窗口里停机
|
|
550
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
551
|
-
assert.deepEqual(caught.map((e) => e.message), [], '停机时 close() 一个 CONNECTING 的 socket 会异步抛 error;必须在摘完监听器后补一个 error 兜底');
|
|
552
|
-
}
|
|
553
|
-
finally {
|
|
554
|
-
process.removeListener('uncaughtException', onUncaught);
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
//# sourceMappingURL=relay-client.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"relay-client.test.js","sourceRoot":"","sources":["../../tests/relay-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|