


























@@ -55,6 +55,7 @@ vi.mock("./model-pricing-cache.js", () => ({
5555const {
5656 activateGatewayScheduledServices,
5757 runGatewayPostReadyMaintenance,
58+ scheduleGatewayPostReadyMaintenance,
5859 startGatewayRuntimeServices,
5960} = await import("./server-runtime-services.js");
6061@@ -245,6 +246,64 @@ describe("server-runtime-services", () => {
245246expect(recordPostReadyMemory).toHaveBeenCalledTimes(1);
246247});
247248249+it("returns a cancellable post-ready maintenance timer", async () => {
250+vi.useFakeTimers();
251+const startMaintenance = vi.fn(async () => null);
252+const onStarted = vi.fn();
253+const handle = scheduleGatewayPostReadyMaintenance(
254+createPostReadyMaintenanceScheduleParams({
255+delayMs: 25,
256+ onStarted,
257+ startMaintenance,
258+}),
259+);
260+261+clearTimeout(handle);
262+await vi.advanceTimersByTimeAsync(25);
263+264+expect(onStarted).not.toHaveBeenCalled();
265+expect(startMaintenance).not.toHaveBeenCalled();
266+});
267+268+it("clears delayed maintenance handles when close starts during maintenance startup", async () => {
269+vi.useFakeTimers();
270+let closing = false;
271+let resolveMaintenance!: (maintenance: ReturnType<typeof createMaintenanceHandles>) => void;
272+const startMaintenance = vi.fn(
273+() =>
274+new Promise<ReturnType<typeof createMaintenanceHandles>>((resolve) => {
275+resolveMaintenance = resolve;
276+}),
277+);
278+const applyMaintenance = vi.fn();
279+const cron = { start: vi.fn(async () => undefined) };
280+const recordPostReadyMemory = vi.fn();
281+282+scheduleGatewayPostReadyMaintenance(
283+createPostReadyMaintenanceScheduleParams({
284+delayMs: 25,
285+isClosing: () => closing,
286+ startMaintenance,
287+ applyMaintenance,
288+ cron,
289+ recordPostReadyMemory,
290+}),
291+);
292+293+await vi.advanceTimersByTimeAsync(25);
294+expect(startMaintenance).toHaveBeenCalledTimes(1);
295+296+closing = true;
297+resolveMaintenance(createMaintenanceHandles());
298+await Promise.resolve();
299+await Promise.resolve();
300+301+expect(applyMaintenance).not.toHaveBeenCalled();
302+expect(cron.start).not.toHaveBeenCalled();
303+expect(recordPostReadyMemory).not.toHaveBeenCalled();
304+expect(vi.getTimerCount()).toBe(0);
305+});
306+248307it("keeps scheduled services disabled for minimal test gateways", () => {
249308const cron = { start: vi.fn(async () => undefined) };
250309@@ -279,3 +338,30 @@ function createLog() {
279338error: vi.fn(),
280339};
281340}
341+342+function createPostReadyMaintenanceScheduleParams(
343+overrides: Partial<Parameters<typeof scheduleGatewayPostReadyMaintenance>[0]> = {},
344+): Parameters<typeof scheduleGatewayPostReadyMaintenance>[0] {
345+return {
346+delayMs: 1,
347+isClosing: () => false,
348+startMaintenance: vi.fn(async () => null),
349+applyMaintenance: vi.fn(),
350+shouldStartCron: () => true,
351+markCronStartHandled: vi.fn(),
352+cron: { start: vi.fn(async () => undefined) },
353+logCron: { error: vi.fn() },
354+log: createLog(),
355+recordPostReadyMemory: vi.fn(),
356+ ...overrides,
357+};
358+}
359+360+function createMaintenanceHandles() {
361+return {
362+tickInterval: setInterval(() => undefined, 60_000),
363+healthInterval: setInterval(() => undefined, 60_000),
364+dedupeCleanup: setInterval(() => undefined, 60_000),
365+mediaCleanup: setInterval(() => undefined, 60_000),
366+};
367+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。