Skip to content
77 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 DeadlineExceeded,
6 LONG_DEADLINE_MS,
7 READ_DEADLINE_MS,
8 SLOW_READ_DEADLINE_MS,
9 WRITE_DEADLINE_MS,
10 deadlineFor,
11 withDeadline,
12} from "./deadline.ts";
13import { STUCK_NAVIGATION_MS } from "./stuck.ts";
14
15test("an answer in time is passed through", async () => {
16 assert.equal(await withDeadline("repos get_repo", 1000, async () => "ok"), "ok");
17});
18
19test("a call that never answers fails at its deadline, and its signal aborts", async () => {
20 let seen: AbortSignal | undefined;
21 const never = withDeadline("repos get_repo", 20, (signal) => {
22 seen = signal;
23 return new Promise<never>(() => {});
24 });
25 await assert.rejects(never, (error: unknown) => error instanceof DeadlineExceeded && /repos get_repo did not answer within 20ms/.test(error.message));
26 assert.equal(seen?.aborted, true);
27});
28
29test("a call that ignores its signal is still let go", async () => {
30 await assert.rejects(withDeadline("chat sidebar", 10, () => new Promise<never>(() => {})), DeadlineExceeded);
31});
32
33test("a failure in time is the call's own, not a deadline", async () => {
34 await assert.rejects(
35 withDeadline("work get_pull", 1000, async () => {
36 throw new Error("get_pull failed with status 500");
37 }),
38 /status 500/,
39 );
40});
41
42test("the caller's own signal aborts the call", async () => {
43 const outer = new AbortController();
44 let seen: AbortSignal | undefined;
45 const call = withDeadline(
46 "repos get_repo",
47 1000,
48 (signal) =>
49 new Promise<never>((_, reject) => {
50 seen = signal;
51 signal.addEventListener("abort", () => reject(signal.reason));
52 }),
53 outer.signal,
54 );
55 outer.abort(new Error("visitor left"));
56 await assert.rejects(call, /visitor left/);
57 assert.equal(seen?.aborted, true);
58});
59
60test("each call gets the time its work needs", () => {
61 assert.equal(deadlineFor("repos", "get_repo", false), READ_DEADLINE_MS);
62 assert.equal(deadlineFor("work", "merge_pull", true), WRITE_DEADLINE_MS);
63 assert.equal(deadlineFor("security", "overview", false), SLOW_READ_DEADLINE_MS);
64 assert.equal(deadlineFor("repos", "blame", false), SLOW_READ_DEADLINE_MS);
65 assert.equal(deadlineFor("integrations", "github_import", true), LONG_DEADLINE_MS);
66 assert.equal(deadlineFor("agents", "computer_sleep", true), LONG_DEADLINE_MS);
67});
68
69test("a long method of one service is not long in another", () => {
70 assert.equal(deadlineFor("agents", "draft", true), LONG_DEADLINE_MS);
71 assert.equal(deadlineFor("work", "draft", true), WRITE_DEADLINE_MS);
72 assert.equal(deadlineFor("identity", "overview", false), READ_DEADLINE_MS);
73});
74
75test("no read outlasts the browser's patience with a navigation", () => {
76 assert.ok(SLOW_READ_DEADLINE_MS < STUCK_NAVIGATION_MS && READ_DEADLINE_MS < STUCK_NAVIGATION_MS);
77});