54 lines
2.7 KiB
TypeScript
54 lines
2.7 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { parseStrictDuration, readOnlyLongPollTransportTimeoutMs, runReadOnlyLongPoll } from "./read-only-long-poll";
|
|
|
|
describe("read-only long poll", () => {
|
|
test("returns an immediate terminal observation", async () => {
|
|
const result = await runReadOnlyLongPoll({ observe: async () => "done", completed: (value) => value === "done", waitForChange: async () => { throw new Error("unexpected wait"); }, cursor: (value) => value });
|
|
expect(result).toMatchObject({ completed: true, timedOut: false, observations: 1, cursor: "done" });
|
|
});
|
|
|
|
test("returns after a backend-held change", async () => {
|
|
let now = 0;
|
|
let state = "running";
|
|
const result = await runReadOnlyLongPoll({
|
|
observe: async () => state,
|
|
completed: (value) => value === "done",
|
|
waitForChange: async () => { now += 25; state = "done"; },
|
|
cursor: (value) => value,
|
|
timeoutMs: 100,
|
|
clock: { now: () => now },
|
|
});
|
|
expect(result).toMatchObject({ completed: true, timedOut: false, elapsedMs: 25, observations: 2 });
|
|
});
|
|
|
|
test("returns the current state after an empty timeout", async () => {
|
|
let now = 0;
|
|
const result = await runReadOnlyLongPoll({
|
|
observe: async () => ({ state: "running", id: "run-1" }),
|
|
completed: () => false,
|
|
waitForChange: async (remainingMs) => { now += remainingMs; },
|
|
cursor: (value) => value.id,
|
|
timeoutMs: 100,
|
|
clock: { now: () => now },
|
|
});
|
|
expect(result).toMatchObject({ completed: false, timedOut: true, cancelled: false, elapsedMs: 100, cursor: "run-1" });
|
|
});
|
|
|
|
test("preserves backend errors and cancellation", async () => {
|
|
await expect(runReadOnlyLongPoll({ observe: async () => "running", completed: () => false, waitForChange: async () => { throw new Error("watch disconnected"); }, cursor: (value) => value, timeoutMs: 10 })).rejects.toThrow("watch disconnected");
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
const cancelled = await runReadOnlyLongPoll({ observe: async () => "running", completed: () => false, waitForChange: async () => {}, cursor: (value) => value, signal: controller.signal });
|
|
expect(cancelled).toMatchObject({ completed: false, timedOut: false, cancelled: true });
|
|
});
|
|
|
|
test("validates strict duration syntax", () => {
|
|
expect(parseStrictDuration("120s")).toBe(120_000);
|
|
expect(parseStrictDuration("250ms")).toBe(250);
|
|
expect(() => parseStrictDuration("1.5s")).toThrow();
|
|
expect(() => parseStrictDuration("120")).toThrow();
|
|
expect(() => parseStrictDuration("0s")).toThrow();
|
|
expect(readOnlyLongPollTransportTimeoutMs(60 * 60_000)).toBe(60 * 60_000 + 15_000);
|
|
});
|
|
});
|