chore: close yonexus pairing follow-ups
This commit is contained in:
26
TASKLIST.md
26
TASKLIST.md
@@ -553,7 +553,7 @@
|
|||||||
|
|
||||||
### YNX-0603 实现 Discord DM 配对通知
|
### YNX-0603 实现 Discord DM 配对通知
|
||||||
**状态**
|
**状态**
|
||||||
- [x] 骨架已完成,真实 DM 需外部依赖(2026-04-08)
|
- [x] 已完成(2026-04-09)
|
||||||
|
|
||||||
**目标**
|
**目标**
|
||||||
- Server 通过 `notifyBotToken` 向 `adminUserId` 发送 pairing code
|
- Server 通过 `notifyBotToken` 向 `adminUserId` 发送 pairing code
|
||||||
@@ -568,12 +568,12 @@
|
|||||||
- 通知成功时 Client 才能进入可确认状态
|
- 通知成功时 Client 才能进入可确认状态
|
||||||
- 通知失败时不会继续配对成功路径
|
- 通知失败时不会继续配对成功路径
|
||||||
|
|
||||||
**进展说明**
|
**已完成内容**
|
||||||
- 已新增 `Yonexus.Server/plugin/notifications/discord.ts` 作为通知服务骨架
|
- 已将 `Yonexus.Server/plugin/notifications/discord.ts` 从 stub 升级为基于 Discord REST API 的真实 DM 发送实现
|
||||||
- 已实现 `formatPairingMessage()` 格式化 DM 内容
|
- 已实现两段式调用:先创建/获取 DM channel,再向该 channel 发送 pairing message
|
||||||
- 已实现 mock/stub 实现用于测试
|
- 已保留 `formatPairingMessage()` 与 mock service,便于测试与本地替身注入
|
||||||
- **待完成**:接入真实 Discord DM 发送需要 `discord.js` 依赖和 Bot Token 配置
|
- 已在通知层补齐配置缺失、HTTP 非 2xx、返回 payload 缺少 channel id 等失败处理,失败时返回 `false` 让 runtime 落到 `admin_notification_failed`
|
||||||
- runtime 已在 pairing 创建后调用通知服务并记录 sent/failed 元数据
|
- 已新增 `Yonexus.Server/tests/notifications.test.ts`,覆盖消息格式、成功发送、DM channel 创建失败、配置缺失四类场景
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1191,17 +1191,23 @@
|
|||||||
- CF-01:已建立连接在异常 close(network partition)后会按退避策略发起重连
|
- CF-01:已建立连接在异常 close(network partition)后会按退避策略发起重连
|
||||||
- 已扩展 `Yonexus.Server/tests/state-recovery.test.ts`
|
- 已扩展 `Yonexus.Server/tests/state-recovery.test.ts`
|
||||||
- SR-02:server restart 后不恢复内存 session,保留 durable paired trust,并要求 client 重新 `hello` 后进入 `auth_required`
|
- SR-02:server restart 后不恢复内存 session,保留 durable paired trust,并要求 client 重新 `hello` 后进入 `auth_required`
|
||||||
- 已同步更新 `tests/failure-path/MATRIX.md`,标记 CF-01、CF-02、SR-02 为已覆盖
|
- 已新增 `Yonexus.Server/tests/notifications.test.ts`
|
||||||
|
- PF-04:覆盖 Discord DM 消息格式、成功发送、DM channel 创建失败、配置缺失
|
||||||
|
- 已扩展 `tests/failure-path/pairing-failures.test.ts`
|
||||||
|
- PF-08:已配对且在线的 client 再次发起 `pair_confirm` 时会被拒绝,且旧 trust material 保持不变
|
||||||
|
- 已扩展 `Yonexus.Client/tests/runtime-flow.test.ts`
|
||||||
|
- PF-10:client 在 waiting_pair_confirm 阶段重启后,可重新 hello 并恢复到等待 out-of-band pairing code 的流程
|
||||||
|
- 已同步更新 `tests/failure-path/MATRIX.md`,标记 CF-01、CF-02、PF-08、PF-10、SR-02 为已覆盖
|
||||||
|
|
||||||
**当前剩余未覆盖重点**
|
**当前剩余未覆盖重点**
|
||||||
- AF-04:当前实现未单独暴露 `invalid_secret` 分支,需先决定是否保留该错误码语义
|
- AF-04:当前实现未单独暴露 `invalid_secret` 分支,需先决定是否保留该错误码语义
|
||||||
- RP-03 / RP-04:管理员主动撤销与 key rotation 语义仍未实现
|
- RP-03 / RP-04:管理员主动撤销与 key rotation 语义仍未实现
|
||||||
- PF-08 / PF-10 以及少量连接/恢复边界场景仍待补齐
|
- PF-04 当前已覆盖运行时失败路径与通知服务单测,但仍缺少真实 Discord 环境端到端验证
|
||||||
|
|
||||||
**待完成**
|
**待完成**
|
||||||
- AF-04:当前实现未单独暴露 `invalid_secret` 分支,需先决定是否保留该错误码语义
|
- AF-04:当前实现未单独暴露 `invalid_secret` 分支,需先决定是否保留该错误码语义
|
||||||
- RP-03 / RP-04:管理员主动撤销与 key rotation 语义仍未实现
|
- RP-03 / RP-04:管理员主动撤销与 key rotation 语义仍未实现
|
||||||
- PF-08 / PF-10 等剩余边界场景测试
|
- 补少量真实环境 smoke test / live validation(非阻塞 v1 交付)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Submodule Yonexus.Client updated: b10ebc541e...7cdda2e335
Submodule Yonexus.Server updated: b67166fd12...2972c4750e
@@ -22,9 +22,9 @@ This document defines the systematic test coverage for pairing and authenticatio
|
|||||||
| PF-05 | Empty pairing code | Client submits empty string | `pair_failed(invalid_code)` | ✅ |
|
| PF-05 | Empty pairing code | Client submits empty string | `pair_failed(invalid_code)` | ✅ |
|
||||||
| PF-06 | Malformed pair_confirm payload | Missing required fields | Protocol error, no state change | ✅ |
|
| PF-06 | Malformed pair_confirm payload | Missing required fields | Protocol error, no state change | ✅ |
|
||||||
| PF-07 | Double pairing attempt | Client calls pair_confirm twice | Second attempt rejected if already paired | ✅ |
|
| PF-07 | Double pairing attempt | Client calls pair_confirm twice | Second attempt rejected if already paired | ✅ |
|
||||||
| PF-08 | Pairing during active session | Paired client tries to pair again | Reject, maintain existing trust | ⬜ |
|
| PF-08 | Pairing during active session | Paired client tries to pair again | Reject, maintain existing trust | ✅ |
|
||||||
| PF-09 | Server restart during pairing | Server restarts before confirm | Pairing state preserved, code still valid | ✅ |
|
| PF-09 | Server restart during pairing | Server restarts before confirm | Pairing state preserved, code still valid | ✅ |
|
||||||
| PF-10 | Client restart during pairing | Client restarts before submit | Client must restart pairing flow | ⬜ |
|
| PF-10 | Client restart during pairing | Client restarts before submit | Client must restart pairing flow | ✅ |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ npm test -- failure-paths
|
|||||||
|
|
||||||
- AF-04 (`invalid_secret`) 仍未单独覆盖:现有实现把“错误 secret 导致的验签失败”统一落到 `invalid_signature`,是否拆分错误码仍待确认。
|
- AF-04 (`invalid_secret`) 仍未单独覆盖:现有实现把“错误 secret 导致的验签失败”统一落到 `invalid_signature`,是否拆分错误码仍待确认。
|
||||||
- RP-04(key rotation)当前仍视为 v2+ 议题;v1 尚未承诺“无重配对换 key”语义,因此暂不强行补测试。
|
- RP-04(key rotation)当前仍视为 v2+ 议题;v1 尚未承诺“无重配对换 key”语义,因此暂不强行补测试。
|
||||||
- 本轮已补齐 AF-01/02/03/05/06/09/10/11、RP-01/02、CF-01/02/03/04/05/07、HF-01/02、PF-09、SR-01/02/03/04/05/06。
|
- 本轮已补齐 AF-01/02/03/05/06/09/10/11、RP-01/02、CF-01/02/03/04/05/07、HF-01/02、PF-08/09/10、SR-01/02/03/04/05/06。
|
||||||
|
|
||||||
### Adding New Test Cases
|
### Adding New Test Cases
|
||||||
|
|
||||||
|
|||||||
@@ -528,6 +528,56 @@ describe("YNX-1105b: Pairing Failure Paths", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Edge Cases", () => {
|
describe("Edge Cases", () => {
|
||||||
|
it("PF-08: pairing attempt during an active paired session is rejected without losing trust", async () => {
|
||||||
|
const store = createMockStore([{
|
||||||
|
identifier: "client-a",
|
||||||
|
pairingStatus: "paired",
|
||||||
|
publicKey: "existing-key",
|
||||||
|
secret: "existing-secret",
|
||||||
|
status: "online",
|
||||||
|
recentNonces: [],
|
||||||
|
recentHandshakeAttempts: [],
|
||||||
|
createdAt: now - 1000,
|
||||||
|
updatedAt: now - 10,
|
||||||
|
pairedAt: now - 500,
|
||||||
|
lastAuthenticatedAt: now - 5,
|
||||||
|
lastHeartbeatAt: now - 5
|
||||||
|
}]);
|
||||||
|
const { transport, sent } = createMockTransport();
|
||||||
|
const runtime = createYonexusServerRuntime({
|
||||||
|
config: {
|
||||||
|
followerIdentifiers: ["client-a"],
|
||||||
|
notifyBotToken: "test-token",
|
||||||
|
adminUserId: "admin",
|
||||||
|
listenHost: "127.0.0.1",
|
||||||
|
listenPort: 8787
|
||||||
|
},
|
||||||
|
store,
|
||||||
|
transport,
|
||||||
|
now: () => now
|
||||||
|
});
|
||||||
|
|
||||||
|
await runtime.start();
|
||||||
|
|
||||||
|
const conn = createConnection("client-a");
|
||||||
|
await runtime.handleMessage(conn, encodeBuiltin(buildPairConfirm(
|
||||||
|
{ identifier: "client-a", pairingCode: "NEW-PAIR-CODE" },
|
||||||
|
{ timestamp: now }
|
||||||
|
)));
|
||||||
|
|
||||||
|
const lastMessage = decodeBuiltin(sent.at(-1)!.message);
|
||||||
|
expect(lastMessage.type).toBe("pair_failed");
|
||||||
|
expect((lastMessage.payload as PairFailedPayload).reason).toBe("internal_error");
|
||||||
|
|
||||||
|
const record = runtime.state.registry.clients.get("client-a");
|
||||||
|
expect(record).toMatchObject({
|
||||||
|
pairingStatus: "paired",
|
||||||
|
secret: "existing-secret",
|
||||||
|
publicKey: "existing-key",
|
||||||
|
status: "online"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("handles concurrent pair_confirm from different connections with same identifier", async () => {
|
it("handles concurrent pair_confirm from different connections with same identifier", async () => {
|
||||||
const store = createMockStore();
|
const store = createMockStore();
|
||||||
const { transport, sent } = createMockTransport();
|
const { transport, sent } = createMockTransport();
|
||||||
|
|||||||
Reference in New Issue
Block a user