Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions server/src/task-lifecycle-watcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ afterAll(() => {
try { server?.stop(true); } catch {}
});

/**
* 轮询到 `ready()` 为真,或到期抛错。
*
* 定长 sleep 的问题不是「慢」,是**报错报在错的层**:超时之后测试红在
* 「事件没写」这条断言上,读的人会去查 watcher,而真实原因可能是子进程
* 还没起来。这里到期时把那句话直接说出来。
*/
async function waitUntil(ready: () => boolean, timeoutMs: number, what: string | null): Promise<void> {
const deadline = Date.now() + timeoutMs;
for (;;) {
if (ready()) return;
if (Date.now() >= deadline) {
// what === null:到期本身就是期望结果(调用方随后自己断言),不抛。
if (what === null) return;
throw new Error(`timed out after ${timeoutMs}ms: ${what}`);
}
await Bun.sleep(25);
}
}

describe("#167 Hub delivered-stale lifecycle watcher", () => {
test("30s/60s thresholds are exact and non-delivered tasks stay silent", () => {
const first = recordDeliveredStaleEvents(NOW);
Expand Down Expand Up @@ -132,7 +152,14 @@ describe("#167 Hub delivered-stale lifecycle watcher", () => {
stdout: "pipe",
stderr: "pipe",
});
await Bun.sleep(800);
// 这里**不**等「hub 就绪」——因为没有一个只有 hub 起来了才成立的廉价判据:
// db 文件在上面那步 init 里就已经存在了,拿它当条件的话这个等待恒真,等于没等。
// 真正需要「hub 起来了」的是下面那条断言,而它已经改成轮询到目标状态,
// hub 起得慢只是让它多等几轮。
//
// 这一步保留的是原来那条断言的原意:**子进程没有立刻崩**。所以只给它一个
// 短窗口,并且如果它在窗口内退出就立刻停下来报错,不用把 800ms 睡满。
await waitUntil(() => child.exitCode !== null, 800, null);
expect(child.exitCode).toBeNull();

const taskId = "stale-live-wiring";
Expand All @@ -147,14 +174,23 @@ describe("#167 Hub delivered-stale lifecycle watcher", () => {
expect(childDb.query<{ count: number }, [string]>(
"SELECT COUNT(*) AS count FROM task_events WHERE task_id = ?1",
).get(taskId)!.count).toBe(0);
await Bun.sleep(3_200);
expect(childDb.query<{ count: number }, [string]>(
// 巡检周期是 COMMHUB_DELIVERED_STALE_PATROL_MS=25ms —— 事件在插入后
// 几十毫秒内就该出现。原来这里是定长 sleep(3_200),纯粹是余量:
// 4.0s 的 sleep 装在 bun 默认的 5.0s 单测预算里,只剩 1s 给两次进程启动。
// 实测在 CI 上被这一条打红过(#798 让这个文件第一次进 CI 才暴露)。
// 改成「轮询到目标状态,或到期报错」:常态下快 ~60 倍,慢的时候等得起。
const countStale = () => childDb.query<{ count: number }, [string]>(
"SELECT COUNT(*) AS count FROM task_events WHERE task_id = ?1 AND event_type = 'task.warning.delivered_stale_30s'",
).get(taskId)!.count).toBe(1);
).get(taskId)!.count;
await waitUntil(() => countStale() === 1, 20_000,
"watcher did not write the delivered_stale_30s event");
expect(countStale()).toBe(1);
} finally {
childDb.close();
try { child.kill("SIGTERM"); } catch {}
await child.exited;
}
});
// 🔴 显式超时:这一条要起两个真 bun 进程,bun 默认的 5s 对它不成立。
// 上面两处已改成轮询,常态用不到这个上限;它只保证「慢」不会被报成「坏」。
}, 30_000);
});
Loading