test(prompt-async-gate): replace setTimeout sleeps with deterministic sync
Test-discipline.md forbids setTimeout(resolve, N) and sleep(N) in test bodies. Replace the 3 microtask and expiry sleeps with explicit microtask yields and deterministic clock advancement, preserving the prompt gate invariants without real-time waits. Closes BLOCKER-3 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -3,8 +3,8 @@ import { afterEach, describe, expect, test } from "bun:test"
|
|||||||
import {
|
import {
|
||||||
promptAfterSessionIdle,
|
promptAfterSessionIdle,
|
||||||
promptAsyncAfterSessionIdle,
|
promptAsyncAfterSessionIdle,
|
||||||
releasePromptAsyncReservation,
|
|
||||||
releaseAllPromptAsyncReservationsForTesting,
|
releaseAllPromptAsyncReservationsForTesting,
|
||||||
|
releasePromptAsyncReservation,
|
||||||
} from "./prompt-async-gate"
|
} from "./prompt-async-gate"
|
||||||
|
|
||||||
describe("promptAsyncAfterSessionIdle", () => {
|
describe("promptAsyncAfterSessionIdle", () => {
|
||||||
@@ -76,7 +76,7 @@ describe("promptAsyncAfterSessionIdle", () => {
|
|||||||
source: "test:hold:first",
|
source: "test:hold:first",
|
||||||
settleMs: 0,
|
settleMs: 0,
|
||||||
})
|
})
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0))
|
await new Promise<void>((resolve) => queueMicrotask(resolve))
|
||||||
const second = await promptAsyncAfterSessionIdle({
|
const second = await promptAsyncAfterSessionIdle({
|
||||||
client,
|
client,
|
||||||
sessionID: "ses_hold_after_dispatch",
|
sessionID: "ses_hold_after_dispatch",
|
||||||
@@ -85,7 +85,6 @@ describe("promptAsyncAfterSessionIdle", () => {
|
|||||||
settleMs: 0,
|
settleMs: 0,
|
||||||
})
|
})
|
||||||
const firstResult = await first
|
const firstResult = await first
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(firstResult.status).toBe("dispatched")
|
expect(firstResult.status).toBe("dispatched")
|
||||||
expect(second.status).toBe("reserved")
|
expect(second.status).toBe("reserved")
|
||||||
@@ -122,6 +121,9 @@ describe("promptAsyncAfterSessionIdle", () => {
|
|||||||
test("#given dispatch hold has expired #when the same session prompts again #then the next promptAsync is accepted", async () => {
|
test("#given dispatch hold has expired #when the same session prompts again #then the next promptAsync is accepted", async () => {
|
||||||
// given
|
// given
|
||||||
let promptCalls = 0
|
let promptCalls = 0
|
||||||
|
const originalDateNow = Date.now
|
||||||
|
let currentNow = originalDateNow()
|
||||||
|
Date.now = () => currentNow
|
||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
promptAsync: async () => {
|
promptAsync: async () => {
|
||||||
@@ -130,29 +132,33 @@ describe("promptAsyncAfterSessionIdle", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
try {
|
||||||
const first = await promptAsyncAfterSessionIdle({
|
// when
|
||||||
client,
|
const first = await promptAsyncAfterSessionIdle({
|
||||||
sessionID: "ses_expired_hold",
|
client,
|
||||||
input: { path: { id: "ses_expired_hold" }, body: { parts: [] } },
|
sessionID: "ses_expired_hold",
|
||||||
source: "test:expired:first",
|
input: { path: { id: "ses_expired_hold" }, body: { parts: [] } },
|
||||||
settleMs: 0,
|
source: "test:expired:first",
|
||||||
postDispatchHoldMs: 1,
|
settleMs: 0,
|
||||||
})
|
postDispatchHoldMs: 1,
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
})
|
||||||
const second = await promptAsyncAfterSessionIdle({
|
currentNow += 2
|
||||||
client,
|
const second = await promptAsyncAfterSessionIdle({
|
||||||
sessionID: "ses_expired_hold",
|
client,
|
||||||
input: { path: { id: "ses_expired_hold" }, body: { parts: [] } },
|
sessionID: "ses_expired_hold",
|
||||||
source: "test:expired:second",
|
input: { path: { id: "ses_expired_hold" }, body: { parts: [] } },
|
||||||
settleMs: 0,
|
source: "test:expired:second",
|
||||||
postDispatchHoldMs: 0,
|
settleMs: 0,
|
||||||
})
|
postDispatchHoldMs: 0,
|
||||||
|
})
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(first.status).toBe("dispatched")
|
expect(first.status).toBe("dispatched")
|
||||||
expect(second.status).toBe("dispatched")
|
expect(second.status).toBe("dispatched")
|
||||||
expect(promptCalls).toBe(2)
|
expect(promptCalls).toBe(2)
|
||||||
|
} finally {
|
||||||
|
Date.now = originalDateNow
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("#given a peer-message promptAsync hold #when an unrelated route releases the session #then the peer-message hold remains reserved", async () => {
|
test("#given a peer-message promptAsync hold #when an unrelated route releases the session #then the peer-message hold remains reserved", async () => {
|
||||||
@@ -425,7 +431,7 @@ describe("promptAsyncAfterSessionIdle", () => {
|
|||||||
source: "test:prompt-hold:first",
|
source: "test:prompt-hold:first",
|
||||||
settleMs: 0,
|
settleMs: 0,
|
||||||
})
|
})
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0))
|
await new Promise<void>((resolve) => queueMicrotask(resolve))
|
||||||
const second = await promptAfterSessionIdle({
|
const second = await promptAfterSessionIdle({
|
||||||
client,
|
client,
|
||||||
sessionID: "ses_prompt_hold_after_dispatch",
|
sessionID: "ses_prompt_hold_after_dispatch",
|
||||||
@@ -434,7 +440,6 @@ describe("promptAsyncAfterSessionIdle", () => {
|
|||||||
settleMs: 0,
|
settleMs: 0,
|
||||||
})
|
})
|
||||||
const firstResult = await first
|
const firstResult = await first
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(firstResult.status).toBe("dispatched")
|
expect(firstResult.status).toBe("dispatched")
|
||||||
expect(second.status).toBe("reserved")
|
expect(second.status).toBe("reserved")
|
||||||
|
|||||||
Reference in New Issue
Block a user