| 1 | import { WS_METHODS } from "@t3tools/contracts"; |
| 2 | import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; |
| 3 | |
| 4 | import { |
| 5 | acknowledgeRpcRequest, |
| 6 | getSlowRpcAckRequests, |
| 7 | resetRequestLatencyStateForTests, |
| 8 | trackRpcRequestSent, |
| 9 | LONG_RUNNING_RPC_ACK_THRESHOLD_MS, |
| 10 | SLOW_RPC_ACK_THRESHOLD_MS, |
| 11 | MAX_TRACKED_RPC_ACK_REQUESTS, |
| 12 | } from "./requestLatencyState"; |
| 13 | |
| 14 | describe("requestLatencyState", () => { |
| 15 | beforeEach(() => { |
| 16 | vi.useFakeTimers(); |
| 17 | resetRequestLatencyStateForTests(); |
| 18 | }); |
| 19 | |
| 20 | afterEach(() => { |
| 21 | vi.useRealTimers(); |
| 22 | }); |
| 23 | |
| 24 | it("marks unary requests as slow when the ack threshold is exceeded", () => { |
| 25 | trackRpcRequestSent("1", "server.getConfig"); |
| 26 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS - 1); |
| 27 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 28 | |
| 29 | vi.advanceTimersByTime(1); |
| 30 | expect(getSlowRpcAckRequests()).toMatchObject([ |
| 31 | { |
| 32 | requestId: "1", |
| 33 | tag: "server.getConfig", |
| 34 | thresholdMs: SLOW_RPC_ACK_THRESHOLD_MS, |
| 35 | }, |
| 36 | ]); |
| 37 | }); |
| 38 | |
| 39 | it("clears the slow request once the server acknowledges it", () => { |
| 40 | trackRpcRequestSent("1", "git.status"); |
| 41 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS); |
| 42 | expect(getSlowRpcAckRequests()).toHaveLength(1); |
| 43 | |
| 44 | acknowledgeRpcRequest("1"); |
| 45 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 46 | }); |
| 47 | |
| 48 | it("ignores long-lived subscribe requests", () => { |
| 49 | trackRpcRequestSent("1", "subscribeServerConfig"); |
| 50 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS * 2); |
| 51 | |
| 52 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 53 | }); |
| 54 | |
| 55 | it("ignores usage summary requests", () => { |
| 56 | trackRpcRequestSent("1", WS_METHODS.serverGetUsageSummary); |
| 57 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS * 2); |
| 58 | |
| 59 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 60 | }); |
| 61 | |
| 62 | it.each(Object.values(WS_METHODS).filter((method) => method.startsWith("pullRequests.")))( |
| 63 | "ignores pull request workspace request %s", |
| 64 | (method) => { |
| 65 | trackRpcRequestSent("1", method); |
| 66 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS * 2); |
| 67 | |
| 68 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 69 | }, |
| 70 | ); |
| 71 | |
| 72 | it("keeps ignoring untracked methods when a display tag is supplied", () => { |
| 73 | trackRpcRequestSent( |
| 74 | "1", |
| 75 | WS_METHODS.serverGetUsageSummary, |
| 76 | `${WS_METHODS.serverGetUsageSummary} · env-1`, |
| 77 | ); |
| 78 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS * 2); |
| 79 | |
| 80 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 81 | }); |
| 82 | |
| 83 | it("gives provider updates a longer threshold before warning", () => { |
| 84 | trackRpcRequestSent("1", WS_METHODS.serverUpdateProvider, "server.updateProvider · env-1"); |
| 85 | vi.advanceTimersByTime(LONG_RUNNING_RPC_ACK_THRESHOLD_MS - 1); |
| 86 | expect(getSlowRpcAckRequests()).toEqual([]); |
| 87 | |
| 88 | vi.advanceTimersByTime(1); |
| 89 | expect(getSlowRpcAckRequests()).toMatchObject([ |
| 90 | { |
| 91 | requestId: "1", |
| 92 | tag: "server.updateProvider · env-1", |
| 93 | thresholdMs: LONG_RUNNING_RPC_ACK_THRESHOLD_MS, |
| 94 | }, |
| 95 | ]); |
| 96 | }); |
| 97 | |
| 98 | it("evicts the oldest pending requests once the tracker reaches capacity", () => { |
| 99 | for (let index = 0; index < MAX_TRACKED_RPC_ACK_REQUESTS + 1; index += 1) { |
| 100 | trackRpcRequestSent(String(index), "server.getConfig"); |
| 101 | } |
| 102 | |
| 103 | vi.advanceTimersByTime(SLOW_RPC_ACK_THRESHOLD_MS); |
| 104 | |
| 105 | const slowRequests = getSlowRpcAckRequests(); |
| 106 | expect(slowRequests).toHaveLength(MAX_TRACKED_RPC_ACK_REQUESTS); |
| 107 | expect(slowRequests[0]?.requestId).toBe("1"); |
| 108 | expect(slowRequests.at(-1)?.requestId).toBe(String(MAX_TRACKED_RPC_ACK_REQUESTS)); |
| 109 | }); |
| 110 | }); |