














@@ -1401,6 +1401,91 @@ describe("runGatewayLoop", () => {
14011401await expect(exited).resolves.toBe(0);
14021402});
14031403});
1404+1405+it("catches SIGTERM handler errors, logs them, and falls back to stop (#83131)", async () => {
1406+vi.clearAllMocks();
1407+consumeGatewayRestartIntentPayloadSync.mockImplementationOnce(() => {
1408+throw new Error("dynamic import failed");
1409+});
1410+1411+await withIsolatedSignals(async ({ captureSignal }) => {
1412+const { close, runtime, exited } = await createSignaledLoopHarness();
1413+const sigterm = captureSignal("SIGTERM");
1414+1415+sigterm();
1416+1417+await expect(exited).resolves.toBe(0);
1418+expect(gatewayLog.error).toHaveBeenCalledWith(
1419+"failed to handle SIGTERM: Error: dynamic import failed",
1420+);
1421+expect(close).toHaveBeenCalledWith({
1422+reason: "gateway stopping",
1423+restartExpectedMs: null,
1424+});
1425+expect(runtime.exit).toHaveBeenCalledWith(0);
1426+});
1427+});
1428+1429+it("catches SIGUSR1 handler errors even when token cleanup throws (#83131)", async () => {
1430+vi.clearAllMocks();
1431+consumeGatewayRestartIntentPayloadSync.mockImplementationOnce(() => {
1432+throw new Error("lifecycle module corrupted");
1433+});
1434+markGatewaySigusr1RestartHandled.mockImplementationOnce(() => {
1435+throw new Error("recovery import also failed");
1436+});
1437+1438+await withIsolatedSignals(async ({ captureSignal }) => {
1439+const { close, start, exited } = await createSignaledLoopHarness();
1440+const sigusr1 = captureSignal("SIGUSR1");
1441+const sigterm = captureSignal("SIGTERM");
1442+1443+sigusr1();
1444+await new Promise<void>((resolve) => setImmediate(resolve));
1445+await new Promise<void>((resolve) => setImmediate(resolve));
1446+1447+expect(gatewayLog.error).toHaveBeenCalledWith(
1448+"SIGUSR1 handler failed: lifecycle module corrupted",
1449+);
1450+expect(markGatewaySigusr1RestartHandled).toHaveBeenCalled();
1451+expect(close).not.toHaveBeenCalled();
1452+expect(start).toHaveBeenCalledTimes(1);
1453+1454+sigterm();
1455+await expect(exited).resolves.toBe(0);
1456+});
1457+});
1458+1459+it("catches SIGUSR1 handler errors, clears restart token, and does not crash (#83131)", async () => {
1460+vi.clearAllMocks();
1461+consumeGatewayRestartIntentPayloadSync.mockImplementationOnce(() => {
1462+throw new Error("sigusr1 lifecycle import failed");
1463+});
1464+1465+await withIsolatedSignals(async ({ captureSignal }) => {
1466+const { close, start, exited } = await createSignaledLoopHarness();
1467+const sigusr1 = captureSignal("SIGUSR1");
1468+const sigterm = captureSignal("SIGTERM");
1469+1470+sigusr1();
1471+// The catch handler clears the restart token from the eagerly-loaded
1472+// lifecycle runtime, so wait for the async signal body to reject.
1473+await new Promise<void>((resolve) => setImmediate(resolve));
1474+await new Promise<void>((resolve) => setImmediate(resolve));
1475+1476+expect(gatewayLog.error).toHaveBeenCalledWith(
1477+"SIGUSR1 handler failed: sigusr1 lifecycle import failed",
1478+);
1479+// Restart token must be cleared so future SIGUSR1 restarts are not
1480+// permanently coalesced as "already in-flight".
1481+expect(markGatewaySigusr1RestartHandled).toHaveBeenCalled();
1482+expect(close).not.toHaveBeenCalled();
1483+expect(start).toHaveBeenCalledTimes(1);
1484+1485+sigterm();
1486+await expect(exited).resolves.toBe(0);
1487+});
1488+});
14041489});
1405149014061491describe("gateway discover routing helpers", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。