Skip to content

Commit 416ac38

Browse files
6a6f686e6e79claude
andcommitted
test(gateway): add regression tests for restart coalescing fix
- run-loop.test.ts: assert markGatewaySigusr1RestartHandled is called before request() in the restartIntent branch of onSigusr1; both call count and invocation order are verified. Uses waitForLoopCondition for reliable async completion rather than fixed setImmediate counts. - infra-runtime.test.ts: assert resetGatewayRestartStateForInProcessRestart aligns stale emit/consume token counters so a post-reset restart request is not coalesced. Both tests fail against current main and pass with the patch. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 7601e69 commit 416ac38

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

src/cli/gateway-cli/run-loop.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,33 @@ describe("runGatewayLoop", () => {
10901090
});
10911091
});
10921092

1093+
it("marks the in-flight token consumed before routing SIGUSR1 with a restart intent", async () => {
1094+
vi.clearAllMocks();
1095+
consumeGatewayRestartIntentPayloadSync.mockReturnValueOnce({ reason: "config.patch" });
1096+
1097+
await withIsolatedSignals(async ({ captureSignal }) => {
1098+
const { close, start } = await createSignaledLoopHarness();
1099+
const sigusr1 = captureSignal("SIGUSR1");
1100+
1101+
sigusr1();
1102+
await waitForLoopCondition(
1103+
() => close.mock.calls.length > 0,
1104+
"expected close to be called as part of the restart",
1105+
);
1106+
await waitForLoopCondition(
1107+
() => start.mock.calls.length >= 2,
1108+
"expected start to be called a second time after restart",
1109+
);
1110+
1111+
expect(markGatewaySigusr1RestartHandled).toHaveBeenCalledTimes(1);
1112+
expect(markGatewaySigusr1RestartHandled.mock.invocationCallOrder[0]).toBeLessThan(
1113+
close.mock.invocationCallOrder[0]!,
1114+
);
1115+
expect(close).toHaveBeenCalledTimes(1);
1116+
expect(start).toHaveBeenCalledTimes(2);
1117+
});
1118+
});
1119+
10931120
it("releases the lock before exiting on spawned restart", async () => {
10941121
vi.clearAllMocks();
10951122
peekGatewaySigusr1RestartReason.mockReturnValue(undefined);

src/infra/infra-runtime.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
isGatewaySigusr1RestartExternallyAllowed,
1414
markGatewaySigusr1RestartHandled,
1515
peekGatewaySigusr1RestartReason,
16+
resetGatewayRestartStateForInProcessRestart,
1617
scheduleGatewaySigusr1Restart,
1718
setGatewaySigusr1RestartPolicy,
1819
setPreRestartDeferralCheck,
@@ -463,6 +464,21 @@ describe("infra runtime", () => {
463464
process.removeListener("SIGUSR1", handler);
464465
}
465466
});
467+
468+
it("aligns stale emit/consume token counters on in-process restart boundary", () => {
469+
const handler = () => {};
470+
process.on("SIGUSR1", handler);
471+
try {
472+
emitGatewayRestart();
473+
// An unconsumed token permanently coalesces further restart requests.
474+
expect(scheduleGatewaySigusr1Restart({ delayMs: 0, reason: "check" }).coalesced).toBe(true);
475+
resetGatewayRestartStateForInProcessRestart();
476+
// After boundary reset the tokens are aligned; requests go through.
477+
expect(scheduleGatewaySigusr1Restart({ delayMs: 0, reason: "post-reset" }).coalesced).toBe(false);
478+
} finally {
479+
process.removeListener("SIGUSR1", handler);
480+
}
481+
});
466482
});
467483

468484
describe("pre-restart deferral check", () => {

0 commit comments

Comments
 (0)