




















@@ -29,7 +29,7 @@ describe("supervised gateway lock recovery", () => {
2929expect(startLoop).toHaveBeenCalledTimes(1);
3030});
313132-it("leaves a healthy supervised gateway in control", async () => {
32+it("leaves a healthy launchd-supervised gateway in control", async () => {
3333const startLoop = vi.fn(async () => {
3434throw new GatewayLockError("gateway already running");
3535});
@@ -38,7 +38,7 @@ describe("supervised gateway lock recovery", () => {
38383939await __testing.runGatewayLoopWithSupervisedLockRecovery({
4040 startLoop,
41-supervisor: "systemd",
41+supervisor: "launchd",
4242port: 18789,
4343healthHost: "0.0.0.0",
4444 log,
@@ -48,11 +48,38 @@ describe("supervised gateway lock recovery", () => {
4848expect(startLoop).toHaveBeenCalledTimes(1);
4949expect(probeHealth).toHaveBeenCalledWith({ host: "0.0.0.0", port: 18789 });
5050expect(log.info).toHaveBeenCalledWith(
51-"gateway already running under systemd; existing gateway is healthy, leaving it in control",
51+"gateway already running under launchd; existing gateway is healthy, leaving it in control",
5252);
5353expect(log.warn).not.toHaveBeenCalled();
5454});
555556+it("uses exit 78 semantics for healthy systemd-supervised lock conflicts", async () => {
57+const startLoop = vi.fn(async () => {
58+throw new GatewayLockError("another gateway instance is already listening");
59+});
60+const probeHealth = vi.fn(async () => true);
61+62+await expect(
63+__testing.runGatewayLoopWithSupervisedLockRecovery({
64+ startLoop,
65+supervisor: "systemd",
66+port: 18789,
67+healthHost: "127.0.0.1",
68+log: createLogger(),
69+ probeHealth,
70+}),
71+).rejects.toThrow("exiting with code 78 to prevent a systemd Restart=always loop");
72+73+expect(startLoop).toHaveBeenCalledTimes(1);
74+expect(probeHealth).toHaveBeenCalledWith({ host: "127.0.0.1", port: 18789 });
75+expect(
76+__testing.resolveGatewayLockErrorExitCode(
77+new GatewayLockError("gateway already running under systemd; existing gateway is healthy"),
78+"systemd",
79+),
80+).toBe(78);
81+});
82+5683it("bounds supervised retries when the existing gateway stays unhealthy", async () => {
5784let now = 0;
5885const startLoop = vi.fn(async () => {
@@ -85,6 +112,49 @@ describe("supervised gateway lock recovery", () => {
85112expect(sleep).toHaveBeenNthCalledWith(3, 2);
86113});
87114115+it("bounds supervised retries for EADDRINUSE lock errors", async () => {
116+let now = 0;
117+const startLoop = vi.fn(async () => {
118+throw new GatewayLockError(
119+"another gateway instance is already listening on ws://127.0.0.1:18789",
120+);
121+});
122+const sleep = vi.fn(async (ms: number) => {
123+now += ms;
124+});
125+126+await expect(
127+__testing.runGatewayLoopWithSupervisedLockRecovery({
128+ startLoop,
129+supervisor: "systemd",
130+port: 18789,
131+healthHost: "127.0.0.1",
132+log: createLogger(),
133+probeHealth: vi.fn(async () => false),
134+now: () => now,
135+ sleep,
136+retryMs: 5,
137+timeoutMs: 12,
138+}),
139+).rejects.toThrow(
140+"gateway already running under systemd; existing gateway did not become healthy after 12ms",
141+);
142+143+expect(startLoop).toHaveBeenCalledTimes(4);
144+expect(sleep).toHaveBeenNthCalledWith(1, 5);
145+expect(sleep).toHaveBeenNthCalledWith(2, 5);
146+expect(sleep).toHaveBeenNthCalledWith(3, 2);
147+});
148+149+it("keeps unmanaged duplicate starts on the existing exit-success path", () => {
150+expect(
151+__testing.resolveGatewayLockErrorExitCode(
152+new GatewayLockError("another gateway instance is already listening"),
153+null,
154+),
155+).toBe(0);
156+});
157+88158it("normalizes wildcard bind hosts for local health probes", () => {
89159expect(__testing.normalizeGatewayHealthProbeHost("0.0.0.0")).toBe("127.0.0.1");
90160expect(__testing.normalizeGatewayHealthProbeHost("::")).toBe("127.0.0.1");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。