test: refactored test suites to eliminate time-based sleeps and polling loops
- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests. - Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks. - Updated catalog model definitions, metadata, and configurations.
This commit is contained in:
@@ -2216,7 +2216,7 @@ describe("agentLoop with AgentMessage", () => {
|
||||
let steerReady = false;
|
||||
let drained = false;
|
||||
let observedAbort = false;
|
||||
let resolvedByTimeout = false;
|
||||
const toolRelease = Promise.withResolvers<void>();
|
||||
|
||||
const tool: AgentTool<typeof toolSchema, Record<string, never>> = {
|
||||
name: "wait",
|
||||
@@ -2226,24 +2226,7 @@ describe("agentLoop with AgentMessage", () => {
|
||||
interruptible: params => params.op === "wait",
|
||||
async execute(_toolCallId, _params, signal) {
|
||||
steerReady = true;
|
||||
const { promise, resolve } = Promise.withResolvers<void>();
|
||||
if (signal?.aborted) {
|
||||
resolve();
|
||||
} else {
|
||||
const timer = setTimeout(() => {
|
||||
resolvedByTimeout = true;
|
||||
resolve();
|
||||
}, 300);
|
||||
signal?.addEventListener(
|
||||
"abort",
|
||||
() => {
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
await promise;
|
||||
if (!signal?.aborted) await toolRelease.promise;
|
||||
observedAbort = signal?.aborted === true;
|
||||
return { content: [{ type: "text", text: "waited" }], details: {} };
|
||||
},
|
||||
@@ -2260,7 +2243,11 @@ describe("agentLoop with AgentMessage", () => {
|
||||
model: mock.model,
|
||||
convertToLlm: identityConverter,
|
||||
interruptMode: "immediate",
|
||||
hasSteeringMessages: () => steerReady && !drained,
|
||||
hasSteeringMessages: () => {
|
||||
const queued = steerReady && !drained;
|
||||
if (queued) toolRelease.resolve();
|
||||
return queued;
|
||||
},
|
||||
getSteeringMessages: async () => {
|
||||
if (steerReady && !drained) {
|
||||
drained = true;
|
||||
@@ -2276,7 +2263,7 @@ describe("agentLoop with AgentMessage", () => {
|
||||
}
|
||||
|
||||
expect(observedAbort).toBe(false);
|
||||
expect(resolvedByTimeout).toBe(true);
|
||||
expect(steerReady).toBe(true);
|
||||
expect(drained).toBe(true);
|
||||
expect(
|
||||
events.some(e => e.type === "message_start" && e.message.role === "user" && e.message.content === "interrupt"),
|
||||
@@ -3203,7 +3190,12 @@ describe("agentLoop event-driven steering watch", () => {
|
||||
// drain
|
||||
}
|
||||
})();
|
||||
const completed = await Promise.race([drain.then(() => true), Bun.sleep(1000).then(() => false)]);
|
||||
// This is the behavior under test, so retain a deadline; cancel its timer
|
||||
// when teardown succeeds instead of leaving a losing sleep alive.
|
||||
const timeout = Promise.withResolvers<boolean>();
|
||||
const timeoutId = setTimeout(() => timeout.resolve(false), 1000);
|
||||
const completed = await Promise.race([drain.then(() => true), timeout.promise]);
|
||||
clearTimeout(timeoutId);
|
||||
try {
|
||||
expect(completed).toBe(true);
|
||||
expect(executed).toEqual(["only"]);
|
||||
|
||||
@@ -36,17 +36,27 @@ describe("agentPauseGate", () => {
|
||||
const context: AgentContext = { systemPrompt: ["Test"], messages: [], tools: [] };
|
||||
const config: AgentLoopConfig = { model: mock.model, convertToLlm: identityConverter };
|
||||
|
||||
const parked = Promise.withResolvers<void>();
|
||||
const originalWait = agentPauseGate.waitUntilResumed;
|
||||
agentPauseGate.waitUntilResumed = (signal?: AbortSignal) => {
|
||||
parked.resolve();
|
||||
return originalWait.call(agentPauseGate, signal);
|
||||
};
|
||||
expect(agentPauseGate.pause()).toBe(true);
|
||||
expect(agentPauseGate.pause()).toBe(false); // already engaged
|
||||
|
||||
const result = agentLoop([createUserMessage("hi")], context, config, undefined, mock.stream).result();
|
||||
await Bun.sleep(20);
|
||||
await parked.promise;
|
||||
expect(mock.calls.length).toBe(0); // parked before the first provider call
|
||||
|
||||
expect(agentPauseGate.resume()).toBeGreaterThanOrEqual(0);
|
||||
const messages = await result;
|
||||
expect(mock.calls.length).toBe(1);
|
||||
expect(messages[messages.length - 1].role).toBe("assistant");
|
||||
try {
|
||||
expect(agentPauseGate.resume()).toBeGreaterThanOrEqual(0);
|
||||
const messages = await result;
|
||||
expect(mock.calls.length).toBe(1);
|
||||
expect(messages[messages.length - 1].role).toBe("assistant");
|
||||
} finally {
|
||||
agentPauseGate.waitUntilResumed = originalWait;
|
||||
}
|
||||
});
|
||||
|
||||
it("holds tool execution at the tool boundary when paused mid-turn", async () => {
|
||||
@@ -96,6 +106,12 @@ describe("agentPauseGate", () => {
|
||||
const config: AgentLoopConfig = { model: mock.model, convertToLlm: identityConverter };
|
||||
const abortController = new AbortController();
|
||||
|
||||
const parked = Promise.withResolvers<void>();
|
||||
const originalWait = agentPauseGate.waitUntilResumed;
|
||||
agentPauseGate.waitUntilResumed = (signal?: AbortSignal) => {
|
||||
parked.resolve();
|
||||
return originalWait.call(agentPauseGate, signal);
|
||||
};
|
||||
agentPauseGate.pause();
|
||||
const result = agentLoop(
|
||||
[createUserMessage("hi")],
|
||||
@@ -104,19 +120,23 @@ describe("agentPauseGate", () => {
|
||||
abortController.signal,
|
||||
mock.stream,
|
||||
).result();
|
||||
await Bun.sleep(20);
|
||||
await parked.promise;
|
||||
abortController.abort("user interrupt");
|
||||
|
||||
// The run must terminate as aborted promptly (not stay parked until
|
||||
// resume). The provider request itself carries the aborted signal, so
|
||||
// whether the transport is entered at all is an implementation detail.
|
||||
const messages = await result;
|
||||
const last = messages[messages.length - 1];
|
||||
expect(last.role).toBe("assistant");
|
||||
if (last.role === "assistant") {
|
||||
expect(last.stopReason).toBe("aborted");
|
||||
try {
|
||||
const messages = await result;
|
||||
const last = messages[messages.length - 1];
|
||||
expect(last.role).toBe("assistant");
|
||||
if (last.role === "assistant") {
|
||||
expect(last.stopReason).toBe("aborted");
|
||||
}
|
||||
expect(agentPauseGate.paused).toBe(true); // aborting one run never resumes the process
|
||||
} finally {
|
||||
agentPauseGate.waitUntilResumed = originalWait;
|
||||
}
|
||||
expect(agentPauseGate.paused).toBe(true); // aborting one run never resumes the process
|
||||
});
|
||||
|
||||
it("re-parks a waiter when the gate is re-engaged in the same tick as resume", async () => {
|
||||
@@ -128,7 +148,7 @@ describe("agentPauseGate", () => {
|
||||
|
||||
agentPauseGate.resume();
|
||||
agentPauseGate.pause(); // re-engage before the waiter's microtask runs
|
||||
await Bun.sleep(10);
|
||||
await Promise.resolve();
|
||||
expect(released).toBe(false);
|
||||
|
||||
agentPauseGate.resume();
|
||||
|
||||
Reference in New Issue
Block a user