




















@@ -1,7 +1,7 @@
11import { afterEach, describe, expect, it, vi } from "vitest";
22import type { HealthSummary } from "../commands/health.js";
33import type { ChatAbortControllerEntry } from "./chat-abort.js";
4-import { DEDUPE_MAX } from "./server-constants.js";
4+import { DEDUPE_MAX, DEDUPE_TTL_MS } from "./server-constants.js";
5566const cleanOldMediaMock = vi.fn(async () => {});
77@@ -16,14 +16,18 @@ vi.mock("../media/store.js", async () => {
1616const MEDIA_CLEANUP_TTL_MS = 24 * 60 * 60_000;
1717const ABORTED_RUN_TTL_MS = 60 * 60_000;
181819-function createActiveRun(sessionKey: string): ChatAbortControllerEntry {
19+function createActiveRun(
20+sessionKey: string,
21+kind?: ChatAbortControllerEntry["kind"],
22+): ChatAbortControllerEntry {
2023const now = Date.now();
2124return {
2225controller: new AbortController(),
2326sessionId: "sess-1",
2427 sessionKey,
2528startedAtMs: now,
2629expiresAtMs: now + ABORTED_RUN_TTL_MS,
30+ kind,
2731};
2832}
2933@@ -224,6 +228,34 @@ describe("startGatewayMaintenanceTimers", () => {
224228stopMaintenanceTimers(timers);
225229});
226230231+it("keeps active agent dedupe entries past the normal ttl", async () => {
232+vi.useFakeTimers();
233+vi.setSystemTime(new Date("2026-03-22T00:00:00Z"));
234+const { startGatewayMaintenanceTimers } = await import("./server-maintenance.js");
235+const deps = createMaintenanceTimerDeps();
236+const now = Date.now();
237+deps.chatAbortControllers.set("active-agent", createActiveRun("agent:main:main", "agent"));
238+deps.dedupe.set("agent:active-agent", {
239+ts: now - DEDUPE_TTL_MS - 1,
240+ok: true,
241+payload: { runId: "active-agent", status: "accepted" },
242+});
243+deps.dedupe.set("agent:stale-agent", {
244+ts: now - DEDUPE_TTL_MS - 1,
245+ok: true,
246+payload: { runId: "stale-agent", status: "accepted" },
247+});
248+249+const timers = startGatewayMaintenanceTimers(deps);
250+251+await vi.advanceTimersByTimeAsync(60_000);
252+253+expect(deps.dedupe.has("agent:active-agent")).toBe(true);
254+expect(deps.dedupe.has("agent:stale-agent")).toBe(false);
255+256+stopMaintenanceTimers(timers);
257+});
258+227259it("evicts dedupe overflow by oldest timestamp even after reinsertion", async () => {
228260vi.useFakeTimers();
229261vi.setSystemTime(new Date("2026-03-22T00:00:00Z"));
@@ -306,4 +338,35 @@ describe("startGatewayMaintenanceTimers", () => {
306338307339stopMaintenanceTimers(timers);
308340});
341+342+it("does not evict active agent dedupe entries while trimming overflow", async () => {
343+vi.useFakeTimers();
344+vi.setSystemTime(new Date("2026-03-22T00:00:00Z"));
345+const { startGatewayMaintenanceTimers } = await import("./server-maintenance.js");
346+const deps = createMaintenanceTimerDeps();
347+const now = Date.now();
348+349+for (let index = 0; index < DEDUPE_MAX; index += 1) {
350+deps.dedupe.set(`stable-${index}`, { ts: now - 1_000 + index, ok: true });
351+}
352+deps.chatAbortControllers.set("active-oldest", createActiveRun("agent:main:main", "agent"));
353+deps.dedupe.set("agent:active-oldest", {
354+ts: now - 10_000,
355+ok: true,
356+payload: { runId: "active-oldest", status: "accepted" },
357+});
358+deps.dedupe.set("overflow-newest", { ts: now, ok: true });
359+360+const timers = startGatewayMaintenanceTimers(deps);
361+362+await vi.advanceTimersByTimeAsync(60_000);
363+364+expect(deps.dedupe.size).toBe(DEDUPE_MAX);
365+expect(deps.dedupe.has("agent:active-oldest")).toBe(true);
366+expect(deps.dedupe.has("stable-0")).toBe(false);
367+expect(deps.dedupe.has("stable-1")).toBe(false);
368+expect(deps.dedupe.has("overflow-newest")).toBe(true);
369+370+stopMaintenanceTimers(timers);
371+});
309372});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。