summaryrefslogtreecommitdiffhomepage
path: root/src/core/chunks/retry-banner.test.ts
blob: 1d8c1cdd816510663d74be0bddaa400e5f145908 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import type { TurnProviderRetryEvent } from "@dispatch/wire";
import { describe, expect, it } from "vitest";
import { formatRetryDelay, viewProviderRetry } from "./retry-banner";

const retry = (
  attempt: number,
  delayMs: number,
  message = "HTTP 429: overloaded",
  code?: string,
): TurnProviderRetryEvent =>
  code !== undefined
    ? {
        type: "provider-retry",
        conversationId: "c1",
        turnId: "t1",
        attempt,
        delayMs,
        message,
        code,
      }
    : { type: "provider-retry", conversationId: "c1", turnId: "t1", attempt, delayMs, message };

describe("formatRetryDelay", () => {
  it("formats sub-minute delays as seconds", () => {
    expect(formatRetryDelay(5000)).toBe("5s");
    expect(formatRetryDelay(10000)).toBe("10s");
    expect(formatRetryDelay(30000)).toBe("30s");
  });

  it("formats minute+ delays as minutes", () => {
    expect(formatRetryDelay(60000)).toBe("1m");
    expect(formatRetryDelay(300000)).toBe("5m");
    expect(formatRetryDelay(1800000)).toBe("30m");
  });

  it("rounds to the nearest whole unit", () => {
    expect(formatRetryDelay(5500)).toBe("6s"); // 5.5s -> 6s
    expect(formatRetryDelay(90000)).toBe("2m"); // 1.5m -> 2m
  });
});

describe("viewProviderRetry", () => {
  it("labels the attempt 1-based (attempt 0 = Retry #1)", () => {
    expect(viewProviderRetry(retry(0, 5000)).attemptLabel).toBe("Retry #1");
    expect(viewProviderRetry(retry(1, 10000)).attemptLabel).toBe("Retry #2");
    expect(viewProviderRetry(retry(7, 1800000)).attemptLabel).toBe("Retry #8");
  });

  it("derives the delay label from delayMs", () => {
    expect(viewProviderRetry(retry(0, 5000)).delayLabel).toBe("5s");
    expect(viewProviderRetry(retry(4, 300000)).delayLabel).toBe("5m");
  });

  it("passes the endpoint error verbatim", () => {
    const msg = 'HTTP 429: {"error":{"type":"overloaded_error","message":"overloaded"}}';
    expect(viewProviderRetry(retry(0, 5000, msg)).message).toBe(msg);
  });

  it("surfaces the code when present, null when absent", () => {
    expect(viewProviderRetry(retry(0, 5000, "msg", "429")).code).toBe("429");
    expect(viewProviderRetry(retry(0, 5000, "msg")).code).toBeNull();
  });
});