mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 09:41:04 +01:00
test(runner): add direct tests for the capability live-process wait helper
The waitForCapabilityLiveProcess helper had no direct test. Add tests for a first-attempt success, a retry after a thrown error, a deadline failure that carries the last observed error as its cause, and a deadline failure when the callback never settles. The tests use fake timers, so they run in milliseconds instead of the real 10,000 ms deadline. Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 file changed
+94
@@ -0,0 +1,94 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
CAPABILITY_LIVE_PROCESS_WAIT_DEADLINE_MS,
|
||||
waitForCapabilityLiveProcess,
|
||||
} from "../../test/wait-for-live-process.js";
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe("waitForCapabilityLiveProcess", () => {
|
||||
it("returns the callback's value when the first attempt succeeds", async () => {
|
||||
const result = await waitForCapabilityLiveProcess(
|
||||
"first-attempt-succeeds",
|
||||
() => "ok",
|
||||
);
|
||||
|
||||
expect(result).toBe("ok");
|
||||
});
|
||||
|
||||
it("retries a callback that throws, and returns the value when a later attempt succeeds", async () => {
|
||||
vi.useFakeTimers();
|
||||
let attempts = 0;
|
||||
const promise = waitForCapabilityLiveProcess(
|
||||
"retries-then-succeeds",
|
||||
() => {
|
||||
attempts += 1;
|
||||
if (attempts === 1) throw new Error("first attempt failed");
|
||||
return "recovered";
|
||||
},
|
||||
);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(100);
|
||||
|
||||
await expect(promise).resolves.toBe("recovered");
|
||||
expect(attempts).toBe(2);
|
||||
});
|
||||
|
||||
it("fails at the deadline when every attempt throws, naming the label and the elapsed time and carrying the last observed error as its cause", async () => {
|
||||
vi.useFakeTimers();
|
||||
const lastError = new Error("connection refused");
|
||||
const promise = waitForCapabilityLiveProcess("always-fails", () => {
|
||||
throw lastError;
|
||||
});
|
||||
const caughtError = promise.catch((error: unknown) => error);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(
|
||||
CAPABILITY_LIVE_PROCESS_WAIT_DEADLINE_MS + 200,
|
||||
);
|
||||
|
||||
const error = (await caughtError) as Error;
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error.message).toContain("always-fails");
|
||||
expect(error.message).toContain(
|
||||
`did not settle within ${CAPABILITY_LIVE_PROCESS_WAIT_DEADLINE_MS}ms`,
|
||||
);
|
||||
expect(error.message).toMatch(/observed \d+ms/);
|
||||
expect(error.message).toContain("connection refused");
|
||||
expect(error.cause).toBe(lastError);
|
||||
});
|
||||
|
||||
it("fails at the deadline, instead of running past it, when the callback never settles, and raises no unhandled rejection", async () => {
|
||||
vi.useFakeTimers();
|
||||
const unhandledRejections: unknown[] = [];
|
||||
const onUnhandledRejection = (reason: unknown) => {
|
||||
unhandledRejections.push(reason);
|
||||
};
|
||||
process.on("unhandledRejection", onUnhandledRejection);
|
||||
|
||||
try {
|
||||
const promise = waitForCapabilityLiveProcess(
|
||||
"never-settles",
|
||||
() => new Promise<never>(() => {}),
|
||||
);
|
||||
const caughtError = promise.catch((error: unknown) => error);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(
|
||||
CAPABILITY_LIVE_PROCESS_WAIT_DEADLINE_MS + 200,
|
||||
);
|
||||
|
||||
const error = (await caughtError) as Error;
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error.message).toContain("never-settles");
|
||||
expect(error.message).toContain(
|
||||
`did not settle within ${CAPABILITY_LIVE_PROCESS_WAIT_DEADLINE_MS}ms`,
|
||||
);
|
||||
} finally {
|
||||
process.off("unhandledRejection", onUnhandledRejection);
|
||||
}
|
||||
|
||||
expect(unhandledRejections).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user