Skip to content

test(qa-hub): 修四个静默失效 6 周的 e2e 套件 —— 实跑 4/4 从红到绿 (#861) - #863

Open
vansin wants to merge 1 commit into
mainfrom
fix/qa-hub-token-bound-alias
Open

test(qa-hub): 修四个静默失效 6 周的 e2e 套件 —— 实跑 4/4 从红到绿 (#861)#863
vansin wants to merge 1 commit into
mainfrom
fix/qa-hub-token-bound-alias

Conversation

@vansin

@vansin vansin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

#861 里实测确认的那四个套件。四个都从红跑到绿,证据在下面。

背景:它们红了 6 周,而没人知道

#861 里对它们做过实跑:4/4 退出码 1,全部死在第 2 步,错误完全相同
(alias_identity_mismatch)。原因是产品在它们最后一次改动之后 4 天
(925da933 / #203 / #376,2026-07-02)收紧了 report_status 的 token→alias 绑定。

没有任何 workflow 或 qa.sh 会跑它们,所以这次硬化没有触发任何信号。

三处独立的漂移

# 漂移 处理
report_status 要求 alias 等于 token 绑定的 alias(#203/#376) node_token(),为每个 alias 铸自己的 node token
send 侧对称检查:from_session 也必须匹配 发送方同样持有自己的 node token
qa-hub-10 第 3 步断言的是一个已被 #517 有意修掉的 bug 改断言(唯一被改的一条)

③ 值得单说:原断言是「utok 不带 network_id 发送 → permission_denied: network_id required」。
#517 的标题就是

节点发消息报 permission_denied: network_id required,而工具 schema 没有这个入参
(一晚三个节点抄送全部静默失败)

——那个报错本身是 bug,#517 的修法是单网络 utok 自动解析。
所以这里按 #804 / test682 的处理方式改断言,而不是把行为改回去。

取 token 的写法不是我发明的,与已注册且长期绿qa-hub-05-roundtrip 完全一致:

curl -fsS -X POST "$HUB_BASE/api/auth/node-token" \
  -H "Authorization: Bearer $UTOK" \
  -d "{\"network_id\":\"$NET\",\"node_name\":\"$ALIAS\"}"

🔴 一个我猜错、被实跑纠正的假设

我原本怀疑 qa-hub-13 的端点被改名了(/api/server/:host/health vs /api/server-health/:host),
依据是 git grep '/api/server/'server.ts0 命中。证据看起来很硬。

结论是错的。 修完之后那两步直接过了:

[4] /api/server/:host/health exposes latest alert + history for network A only   ✓
[5] /api/server/:host/agents exposes per-agent details and process telemetry     ✓

静态比对给出的是「哪里可疑」,不是「实际会怎样」。这条我也写进了 #861

验证

exact origin/main 上构建 + 运行,跑完逐个删镜像:

qa-hub-10  rc=0  PASS network scope regressions (#67 message ✓ / #54 SSE isolation ✓)   7 步
qa-hub-11  rc=0  PASS node-delete-sse (#74 node_deleted push ✓ / network isolation ✓)   5 步
qa-hub-12  rc=0  PASS servers endpoint (#119 host telemetry aggregation ✓)              5 步
qa-hub-13  rc=0  PASS server health/agents endpoints (#140 Hero 1+2 ✓)                  9 步

🔴 这些断言此前一次都没被执行过 —— 四个套件全部倒在第 2 步。
也就是说:这不是「让红变绿」,是让它们第一次真正跑起来

本 PR 不做注册

#861 里定的次序:先修好、确认能绿,决定要不要进 L1_TESTS
(那会影响 CI 时长,是另一个取舍)。若决定注册,tests/qa-*/** 已在 qa.yml
paths 里,不需要改 paths(见 #860)。

覆盖的东西

qa-hub-10  同一 alias 跨网络的消息/SSE 隔离(#67 / #54)
qa-hub-11  node_deleted 的网络作用域推送 + 残留行清理(#74)
qa-hub-12  /api/servers 主机聚合与网络作用域(#119)
qa-hub-13  /api/server/:host/health + /agents 的按网络隔离(#140)

其中两条是安全相关的作用域隔离 —— 而它们在过去 6 周里没有守住任何东西。

这四个套件从 2026-07-02 起就是红的,而**没有任何东西会跑它们**,所以没人知道。
#861 里实测过:4/4 退出码 1,全部死在第 2 步,错误完全相同。

## 三处独立的漂移,逐个修

**① report_status 的身份绑定(#203 / #376,2026-07-02)**

`server/src/tools.ts:584` 之后,用 network token 上报时 args 里的 alias 必须等于
该 token 绑定的 alias。而 `callerAlias` 的推导(`server/src/server.ts:733-735`)是:

    tokenName 以 "node:" 开头 → 取其后缀;否则**回落到用户名**

四个套件都拿 `register_user` 返回的 `network_token`(名字不是 `node:…`)去上报
任意 alias,于是一律 `alias_identity_mismatch`。

修法:加 `node_token()` helper,为每个要上报的 alias 铸它自己的 node token
(`POST /api/auth/node-token`)。取法与**已注册且长期绿**的 `qa-hub-05-roundtrip`
完全一致 —— 不是我发明的写法。
qa-hub-12/13 上报多个不同 alias,所以再加一层 `report_as()`:直接从 args 里取
alias 再铸 token,循环调用点不必逐个改、也不会漏。

**② send 侧的对称检查(fromIdentityMismatchReply)**

用 network token 发送时 `from_session` 也必须等于 token 绑定的 alias。
qa-hub-10 / qa-hub-13 的发送方同样改成持有自己 node token。
两处的断言(`"from":"alpha-sender"` 等)**原样保留**。

**③ qa-hub-10 第 3 步断言的是一个已被有意修掉的 bug(#517)**

原断言:utok 不带 network_id 发送 → `permission_denied: network_id required`。
`#517` 的标题就是「节点发消息报 permission_denied: network_id required,而工具 schema
没有这个入参(**一晚三个节点抄送全部静默失败**)」—— 那个报错本身是 bug,
修法是单网络 utok 自动解析。

所以这一条**改断言**(参照 #804 / test682:产品有意改掉的东西,该改断言而不是把行为改回去),
改成断言新的正确行为:自动解析并投递成功。这是四个套件里**唯一**被改掉的断言。

## 🔴 一个我猜错、被实跑纠正的假设

我原本怀疑 qa-hub-13 测的端点被改名了(`/api/server/:host/health` vs 产品里的
`/api/server-health/:host`),依据是 `git grep "/api/server/"` 在 `server.ts` 里 0 命中。
**证据看起来很硬,但结论是错的** —— 修完之后:

    [4] /api/server/:host/health exposes latest alert + history for network A only   ✓
    [5] /api/server/:host/agents exposes per-agent details and process telemetry     ✓

两个端点都在,都正常。静态比对给出的是「哪里可疑」,不是「实际会怎样」。

## 验证:四个都实跑到绿

    qa-hub-10  rc=0  PASS network scope regressions (#67 message ✓ / #54 SSE isolation ✓)   7 步
    qa-hub-11  rc=0  PASS node-delete-sse (#74 node_deleted push ✓ / network isolation ✓)   5 步
    qa-hub-12  rc=0  PASS servers endpoint (#119 host telemetry aggregation ✓)              5 步
    qa-hub-13  rc=0  PASS server health/agents endpoints (#140 Hero 1+2 ✓)                  9 步

exact `origin/main` 上构建运行,跑完逐个删镜像。
**这些断言此前一次都没被执行过** —— 它们全部倒在第 2 步。

## 本 PR 不做注册

按 #861 里定的次序:先修好、确认能绿,**再**谈要不要进 `L1_TESTS`。
注册是另一次改动(且会影响 CI 时长),应当单独决定。
若决定注册,`tests/qa-*/**` 已在 `qa.yml` 的 paths 里,不用改 paths(见 #860)。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61fc8474ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +121 to +124
echo "[3] single-network utok send_task auto-resolves the network (#517)"
NO_NET_ARGS=$(jq -nc '{alias:"shared-agent",task:"missing-network-id",from_session:"alice-dashboard"}')
NO_NET=$(mcp_call "$UTOK_A" "send_task" "$NO_NET_ARGS")
echo "$NO_NET" | jq -e '.ok == false and .error == "permission_denied" and (.message | contains("network_id required"))' >/dev/null || {
echo "FAIL: expected network_id required error, got: $NO_NET"
echo "$NO_NET" | jq -e '.ok == true and (.message_id | type == "string")' >/dev/null || {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist the four Docker rerun results

This change claims all four suites were rerun successfully, but none of their checked-in reports is refreshed; for example, docs/tests/report-test54-67.txt still records the old step 3 that expects a missing-network error rather than this new auto-resolution assertion, and the other reports likewise predate the token changes. Save the actual outputs from these runs so the green result is auditable rather than relying only on the commit message.

AGENTS.md reference: AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

Comment on lines +121 to +125
echo "[3] single-network utok send_task auto-resolves the network (#517)"
NO_NET_ARGS=$(jq -nc '{alias:"shared-agent",task:"missing-network-id",from_session:"alice-dashboard"}')
NO_NET=$(mcp_call "$UTOK_A" "send_task" "$NO_NET_ARGS")
echo "$NO_NET" | jq -e '.ok == false and .error == "permission_denied" and (.message | contains("network_id required"))' >/dev/null || {
echo "FAIL: expected network_id required error, got: $NO_NET"
echo "$NO_NET" | jq -e '.ok == true and (.message_id | type == "string")' >/dev/null || {
echo "FAIL: single-network utok should auto-resolve and deliver, got: $NO_NET"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the suite contract for auto-resolution

After this assertion changes to require successful single-network auto-resolution, the adjacent README.md still says user-token writes without network_id must fail and lists step 3 as expecting network_id required. Anyone using the README to understand this security suite now gets the opposite contract, so update its rationale and assertion table in the same change.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

@vansin

vansin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🔴 更正 PR 正文里一句写过头的话

我在正文里写了:

这些断言此前一次都没被执行过 —— 四个套件全部倒在第 2 步。

这句不准确,应当收窄。 四个套件都有历史运行报告,说明它们写好时是跑过并通过的:

qa-hub-10  docs/tests/report-test54-67.txt
qa-hub-11  docs/tests/report-test74-node-delete-sse.txt
           └─ 第 68 行:PASS qa-hub-11 node-delete-sse (#74 node_deleted push ✓ …)
              该报告随 2026-05-14 "fix(server): emit SSE event when deleting nodes" 一起提交
qa-hub-12  docs/tests/report-test119-servers-endpoint.md
qa-hub-13  docs/tests/report-test140-server-health-agents.md

准确的说法是:

自 2026-07-02(925da933 / #203 / #376)起,四个套件都倒在第 2 步,
因此第 3 步之后的断言在这 6 周里一次都没被执行过。

它们在被写下时是绿的、有归档证据;是产品硬化之后才失效,而失效期间没有任何东西会跑它们,
所以没人知道。这不影响本 PR 的内容(修的三处漂移、4/4 实跑到绿都成立),
只是把「从未执行」收窄成「自 07-02 起未执行」。

顺带记我自己的一个重复错误

我在 #861 里定过一条方法:判一个套件属于哪一组,要按内容查,不能按名字猜,
并且已经因为 test292-e2e-* 栽过一次(我把它们当成「48 个零记录」里的,
实际它们有报告、属于「有记录」的 125 组)。

这次我又犯了同一个错:我说「下一批候选:qa-hub-10/11/12/13,它们与已注册的
qa-hub-05..09 同族」——那是按名字挑的,没回去核它们属于哪一组。
一核就发现它们也有报告,同样属于 125 组,不属于 48 组。

同一个错在同一件事上犯两次,说明「知道这条规则」和「每次都执行」是两回事。
方法论写进 issue 不等于会照做 —— 得在每次选批次时当成一步动作,而不是当成一条心得。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant