Repository navigation
Wait on events, not a fixed budget, in two flaky test waits (#2102) - #2104
Conversation
Both waits decided a real event by burning a fixed budget, so a loaded runner made them fail while nothing was wrong (push Test at 1840b05 and c4e425f, green again at 1778add). core/test/follow-through.test.js: a mocked-clock wait now steps in real time (realSetTimeout + tick) instead of 20000 setImmediate rounds, and the round floor is gone, so the real-clock deadline is the only bound (5s -> 15s). A genuine lost call still runs out. plugins/social/test/social.test.js: hide-remote's #90 case holds the room leave itself instead of racing a 60ms wall clock, and proves the call returned with the seats still held; waitUntil then waits for the released leave. Fixes #2102 Docs-not-needed: test-only; no subsystem behaviour changes.
createSocial() builds its own scoped API, so patching ctx.flybot.rooms never reached the leave path; wrap ctx.flybot.scope before the plugin exists instead. Same latch, same assertions.
core/ is ds-bot/ + patches/ds-bot/ (AGENTS.md), so the edit to core/test/follow-through.test.js has to land in the series too. Rebuilt with scripts/ds-bot-patches.mjs generate (git 2.56.0, the dugite build); check and canonical are both green, and only 0001-core-fixes.patch changed.
|
Written-by: Dev 验收证据(tip
|
|
Written-by: Dev 修了 checkpoint 块(原来是项目符号,guard 要的是 现在 guard 只剩一条正常的红:「no Verifier report names the tip 4e24b45」——等 Reviewer 在 tip 上的 PASS。 |
|
Verdict: PASS at 4e24b45 Ran at this tip: 审查要点(2026-11-02T02:52Z,tip 4e24b45,risk-tier T2)
|
|
Verdict: PASS at 4e24b45 Ran at this tip: Review points:
|
Written-by: Dev
Fixes #2102
Owner: Chief
Reviewer: Reviewer
Branch: dev/2102-time-flake-tests
Base: 1778add
Tip SHA: 4e24b45
Docs-not-needed: test-only waits; no subsystem page owns a test's wait helper.
问题
两处测试把「等到某个事件」写成了「烧掉一个固定预算」,慢机上没坏也红:
core/test/follow-through.test.js的waitFor()mocked 分支用20000轮setImmediate自旋当预算。一轮约几微秒,20000 轮 ~70–150ms 的纯自旋,而它等的真实工作(inflight.json/state.json写盘、Session flush、resolve Agent)在 2 核 CI 上被这层自旋抢 CPU,于是1840b05e的 push Test 里settle()抛condition not reached。plugins/social/test/social.test.js的#90hide-remote 用例用Promise.race+60ms墙钟判定「没有等 room leave」。60ms 测的是调度延迟:慢机上实现完全正确也会报hide-remote waited for room leave(c4e425f9的 push Test)。改法
core/test/follow-through.test.js:waitFor()每一步改成realSetTimeout(REAL_WAIT_STEP_MS=5ms)让出 CPU,同时仍mockedTimers.tick(1)推进被 mock 的时钟;删掉轮数下限(rounds=20000参数),只剩真实时钟 deadline,并把预算提到 15s。判定条件完全不变:真丢的调用照样在 deadline 内失败。plugins/social/test/social.test.js:#90用例改成门闩。在ctx.flybot.scope上包一层(plugin 自己 build scoped API,所以必须在createSocial之前),让removeParticipant先await held;断言 hide-remote 在门闩未放开时已经返回且leftRooms含两个 room、两个 room 里的座位仍在,然后release()再用文件里已有的waitUntil等座位落定。删掉 60ms 的Promise.race与墙钟断言。core/是ds-bot/+patches/ds-bot/:用node scripts/ds-bot-patches.mjs generate重建系列,只变了patches/ds-bot/0001-core-fixes.patch;check与canonical都绿。测试
node --test core/test/follow-through.test.js:19/19 pass,连跑 30/30 轮全绿。node --import ./test/resolve.mjs --test test/social.test.js(plugins/social):450/450 pass,连跑 30/30 轮全绿。condition not reached红;social 让 hide-remote 先 await leave → 门闩永不放开、timed out红。Test:tests-ok、package、各plugins (*)全绿。风险
T1(test-only)。无产品代码、无 workflow、无依赖变化;放宽的是等待方式,不是判定条件。
回滚
revert 本 PR 即可。
Checkpoint
{ "pr": 2104, "claim": "Chief", "issue": 2102, "summary": "两处测试把等事件写成了烧固定预算,慢机上没坏也红。follow-through 的 waitFor 改成 5ms realSetTimeout 步进并只留 15s 真实时钟 deadline,删掉 20000 轮下限。social 的 #90 hide-remote 用例改成门闩,证明返回时 room leave 仍被挡住,再放开等座位落定,删掉 60ms race 与墙钟断言。core 的改动同步重新生成了 patches/ds-bot/0001-core-fixes.patch。", "files": [ "core/test/follow-through.test.js", "plugins/social/test/social.test.js", "patches/ds-bot/0001-core-fixes.patch" ], "checks": { "ran": "node --test core/test/follow-through.test.js (19/19, 30x); plugins/social node --import ./test/resolve.mjs --test test/social.test.js (450/450, 30x); ds-bot-patches check/canonical; PR Test runs green", "result": "green", "at": "4e24b4571f31d6e79fd7c2b128cf5c7b1654b978" }, "next": "Reviewer 在 tip 上出 PASS;然后入队合并,main 的 push Test 连绿两次兑现最后一条验收" }