

























@@ -175,12 +175,13 @@ describe("runDiscordGatewayLifecycle", () => {
175175threadStop: ReturnType<typeof vi.fn>;
176176waitCalls: number;
177177gatewaySupervisor: { detachLifecycle: ReturnType<typeof vi.fn> };
178+detachCalls?: number;
178179}) {
179180expect(waitForDiscordGatewayStopMock).toHaveBeenCalledTimes(params.waitCalls);
180181expect(unregisterGatewayMock).toHaveBeenCalledWith("default");
181182expect(stopGatewayLoggingMock).toHaveBeenCalledTimes(1);
182183expect(params.threadStop).toHaveBeenCalledTimes(1);
183-expect(params.gatewaySupervisor.detachLifecycle).toHaveBeenCalledTimes(1);
184+expect(params.gatewaySupervisor.detachLifecycle).toHaveBeenCalledTimes(params.detachCalls ?? 1);
184185}
185186186187it("resolves gateway READY timeouts from config, env, then defaults", () => {
@@ -509,6 +510,60 @@ describe("runDiscordGatewayLifecycle", () => {
509510});
510511});
511512513+it("treats abort-time live reconnect exhaustion as expected shutdown", async () => {
514+const abortController = new AbortController();
515+let liveGatewayHandler: ((event: DiscordGatewayEvent) => void) | undefined;
516+const { lifecycleParams, threadStop, runtimeLog, runtimeError, gatewaySupervisor } =
517+createLifecycleHarness();
518+lifecycleParams.abortSignal = abortController.signal;
519+gatewaySupervisor.attachLifecycle.mockImplementation(
520+(handler: (event: DiscordGatewayEvent) => void) => {
521+liveGatewayHandler = handler;
522+},
523+);
524+abortController.signal.addEventListener(
525+"abort",
526+() => {
527+if (!liveGatewayHandler) {
528+throw new Error("discord gateway lifecycle handler was not attached");
529+}
530+liveGatewayHandler(
531+createGatewayEvent(
532+"reconnect-exhausted",
533+"Max reconnect attempts (50) reached after close code 1005",
534+),
535+);
536+},
537+{ once: true },
538+);
539+waitForDiscordGatewayStopMock.mockImplementationOnce(async (waitParams) => {
540+const actual =
541+await vi.importActual<typeof import("../monitor.gateway.js")>("../monitor.gateway.js");
542+const waitPromise = actual.waitForDiscordGatewayStop(waitParams);
543+abortController.abort(new Error("shutdown"));
544+return await waitPromise;
545+});
546+547+await expect(runDiscordGatewayLifecycle(lifecycleParams)).resolves.toBeUndefined();
548+549+expect(gatewaySupervisor.attachLifecycle).toHaveBeenCalledTimes(1);
550+expect(runtimeLog).toHaveBeenCalledWith(
551+expect.stringContaining("treating reconnect-exhausted during expected shutdown as clean"),
552+);
553+expect(runtimeLog).toHaveBeenCalledWith(
554+expect.stringContaining("Max reconnect attempts (50) reached after close code 1005"),
555+);
556+expect(runtimeError).not.toHaveBeenCalledWith(
557+expect.stringContaining("discord gateway reconnect-exhausted"),
558+);
559+expectLifecycleCleanup({
560+ threadStop,
561+waitCalls: 1,
562+ gatewaySupervisor,
563+detachCalls: 2,
564+});
565+});
566+512567it("surfaces fatal startup gateway errors while waiting for READY", async () => {
513568vi.useFakeTimers();
514569try {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。