Files
pikasTech-unidesk/scripts/src/read-only-long-poll.test.ts
T
2026-07-15 06:35:25 +02:00

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);
});
});