Skip to content

Commit 921edcb

Browse files
committed
fix shutdown manager tests
1 parent 59afe71 commit 921edcb

File tree

3 files changed

+68
-46
lines changed

3 files changed

+68
-46
lines changed

packages/core/src/v3/serverOnly/shutdownManager.test.ts

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,55 @@
11
import { describe, test, expect, vi, beforeEach } from "vitest";
2-
import { shutdownManager } from "./shutdownManager.js";
3-
4-
// Type assertion to access private members for testing
5-
type PrivateShutdownManager = {
6-
handlers: Map<string, { handler: NodeJS.SignalsListener; signals: Array<"SIGTERM" | "SIGINT"> }>;
7-
shutdown: (signal: "SIGTERM" | "SIGINT") => Promise<void>;
8-
_reset: () => void;
9-
};
2+
import { ShutdownManager } from "./shutdownManager.js";
103

114
describe("ShutdownManager", { concurrent: false }, () => {
12-
const manager = shutdownManager as unknown as PrivateShutdownManager;
135
// Mock process.exit to prevent actual exit
146
const mockExit = vi.spyOn(process, "exit").mockImplementation(() => undefined as never);
157

168
beforeEach(() => {
17-
// Clear all mocks and reset the manager before each test
189
vi.clearAllMocks();
19-
manager._reset();
2010
});
2111

2212
test("should successfully register a new handler", () => {
13+
const manager = new ShutdownManager(false);
14+
2315
const handler = vi.fn();
24-
shutdownManager.register("test-handler", handler);
16+
manager.register("test-handler", handler);
2517

26-
expect(manager.handlers.has("test-handler")).toBe(true);
27-
const registeredHandler = manager.handlers.get("test-handler");
18+
expect(manager._getHandlersForTesting().has("test-handler")).toBe(true);
19+
const registeredHandler = manager._getHandlersForTesting().get("test-handler");
2820
expect(registeredHandler?.handler).toBe(handler);
2921
expect(registeredHandler?.signals).toEqual(["SIGTERM", "SIGINT"]);
3022
});
3123

3224
test("should throw error when registering duplicate handler name", () => {
25+
const manager = new ShutdownManager(false);
26+
3327
const handler = vi.fn();
34-
shutdownManager.register("duplicate-handler", handler);
28+
manager.register("duplicate-handler", handler);
3529

3630
expect(() => {
37-
shutdownManager.register("duplicate-handler", handler);
31+
manager.register("duplicate-handler", handler);
3832
}).toThrow('Shutdown handler "duplicate-handler" already registered');
3933
});
4034

4135
test("should register handler with custom signals", () => {
36+
const manager = new ShutdownManager(false);
37+
4238
const handler = vi.fn();
43-
shutdownManager.register("custom-signals", handler, ["SIGTERM"]);
39+
manager.register("custom-signals", handler, ["SIGTERM"]);
4440

45-
const registeredHandler = manager.handlers.get("custom-signals");
41+
const registeredHandler = manager._getHandlersForTesting().get("custom-signals");
4642
expect(registeredHandler?.signals).toEqual(["SIGTERM"]);
4743
});
4844

4945
test("should call registered handlers when shutdown is triggered", async () => {
46+
const manager = new ShutdownManager(false);
47+
5048
const handler1 = vi.fn();
5149
const handler2 = vi.fn();
5250

53-
shutdownManager.register("handler1", handler1);
54-
shutdownManager.register("handler2", handler2);
51+
manager.register("handler1", handler1);
52+
manager.register("handler2", handler2);
5553

5654
await manager.shutdown("SIGTERM");
5755

@@ -61,11 +59,13 @@ describe("ShutdownManager", { concurrent: false }, () => {
6159
});
6260

6361
test("should only call handlers registered for specific signal", async () => {
62+
const manager = new ShutdownManager(false);
63+
6464
const handler1 = vi.fn();
6565
const handler2 = vi.fn();
6666

67-
shutdownManager.register("handler1", handler1, ["SIGTERM"]);
68-
shutdownManager.register("handler2", handler2, ["SIGINT"]);
67+
manager.register("handler1", handler1, ["SIGTERM"]);
68+
manager.register("handler2", handler2, ["SIGINT"]);
6969

7070
await manager.shutdown("SIGTERM");
7171

@@ -75,11 +75,13 @@ describe("ShutdownManager", { concurrent: false }, () => {
7575
});
7676

7777
test("should handle errors in shutdown handlers gracefully", async () => {
78+
const manager = new ShutdownManager(false);
79+
7880
const successHandler = vi.fn();
7981
const errorHandler = vi.fn().mockRejectedValue(new Error("Handler failed"));
8082

81-
shutdownManager.register("success-handler", successHandler);
82-
shutdownManager.register("error-handler", errorHandler);
83+
manager.register("success-handler", successHandler);
84+
manager.register("error-handler", errorHandler);
8385

8486
await manager.shutdown("SIGTERM");
8587

@@ -89,8 +91,10 @@ describe("ShutdownManager", { concurrent: false }, () => {
8991
});
9092

9193
test("should only run shutdown sequence once even if called multiple times", async () => {
94+
const manager = new ShutdownManager(false);
95+
9296
const handler = vi.fn();
93-
shutdownManager.register("test-handler", handler);
97+
manager.register("test-handler", handler);
9498

9599
await Promise.all([manager.shutdown("SIGTERM"), manager.shutdown("SIGTERM")]);
96100

@@ -99,23 +103,27 @@ describe("ShutdownManager", { concurrent: false }, () => {
99103
expect(mockExit).toHaveBeenCalledWith(128 + 15);
100104
});
101105

102-
test("should exit with correct signal number", async () => {
103-
const handler = vi.fn();
104-
shutdownManager.register("test-handler", handler);
106+
test("should exit with correct signal number on SIGINT", async () => {
107+
const manager = new ShutdownManager(false);
108+
109+
manager.register("test-handler", vi.fn());
105110

106111
await manager.shutdown("SIGINT");
107112
expect(mockExit).toHaveBeenCalledWith(128 + 2); // SIGINT number
113+
});
108114

109-
vi.clearAllMocks();
110-
manager._reset();
111-
shutdownManager.register("test-handler", handler);
115+
test("should exit with correct signal number on SIGTERM", async () => {
116+
const manager = new ShutdownManager(false);
117+
118+
manager.register("test-handler", vi.fn());
112119

113120
await manager.shutdown("SIGTERM");
114121
expect(mockExit).toHaveBeenCalledWith(128 + 15); // SIGTERM number
115122
});
116123

117124
test("should only exit after all handlers have finished", async () => {
118125
const sequence: string[] = [];
126+
const manager = new ShutdownManager(false);
119127

120128
const handler1 = vi.fn().mockImplementation(async () => {
121129
sequence.push("handler1 start");
@@ -144,9 +152,9 @@ describe("ShutdownManager", { concurrent: false }, () => {
144152
return undefined as never;
145153
});
146154

147-
shutdownManager.register("handler1", handler1);
148-
shutdownManager.register("handler2", handler2);
149-
shutdownManager.register("handler3", handler3);
155+
manager.register("handler1", handler1);
156+
manager.register("handler2", handler2);
157+
manager.register("handler3", handler3);
150158

151159
await manager.shutdown("SIGTERM");
152160

packages/core/src/v3/serverOnly/shutdownManager.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import { isTest } from "std-env";
12
import { SimpleStructuredLogger } from "../utils/structuredLogger.js";
23
import { singleton } from "./singleton.js";
34

45
type ShutdownHandler = NodeJS.SignalsListener;
56
// We intentionally keep these limited to avoid unexpected issues with signal handling
67
type ShutdownSignal = Extract<NodeJS.Signals, "SIGTERM" | "SIGINT">;
78

8-
class ShutdownManager {
9+
export class ShutdownManager {
910
private isShuttingDown = false;
1011
private signalNumbers: Record<ShutdownSignal, number> = {
1112
SIGINT: 2,
@@ -16,7 +17,9 @@ class ShutdownManager {
1617
private handlers: Map<string, { handler: ShutdownHandler; signals: ShutdownSignal[] }> =
1718
new Map();
1819

19-
constructor() {
20+
constructor(private disableForTesting = true) {
21+
if (disableForTesting) return;
22+
2023
process.on("SIGTERM", () => this.shutdown("SIGTERM"));
2124
process.on("SIGINT", () => this.shutdown("SIGINT"));
2225
}
@@ -26,13 +29,17 @@ class ShutdownManager {
2629
handler: ShutdownHandler,
2730
signals: ShutdownSignal[] = ["SIGTERM", "SIGINT"]
2831
) {
32+
if (!this.isEnabled()) return;
33+
2934
if (this.handlers.has(name)) {
3035
throw new Error(`Shutdown handler "${name}" already registered`);
3136
}
3237
this.handlers.set(name, { handler, signals });
3338
}
3439

3540
unregister(name: string) {
41+
if (!this.isEnabled()) return;
42+
3643
if (!this.handlers.has(name)) {
3744
throw new Error(`Shutdown handler "${name}" not registered`);
3845
}
@@ -41,6 +48,8 @@ class ShutdownManager {
4148
}
4249

4350
async shutdown(signal: ShutdownSignal) {
51+
if (!this.isEnabled()) return;
52+
4453
if (this.isShuttingDown) return;
4554
this.isShuttingDown = true;
4655

@@ -83,10 +92,20 @@ class ShutdownManager {
8392
}
8493
}
8594

86-
// For testing purposes only - keep this
87-
private _reset() {
88-
this.isShuttingDown = false;
89-
this.handlers.clear();
95+
private isEnabled() {
96+
if (!this.disableForTesting) {
97+
return true;
98+
}
99+
100+
return !isTest;
101+
}
102+
103+
// Only for testing
104+
public _getHandlersForTesting(): ReadonlyMap<
105+
string,
106+
{ handler: ShutdownHandler; signals: ShutdownSignal[] }
107+
> {
108+
return new Map(this.handlers);
90109
}
91110
}
92111

packages/redis-worker/src/worker.test.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import { redisTest } from "@internal/testcontainers";
22
import { Logger } from "@trigger.dev/core/logger";
33
import { describe } from "node:test";
4-
import { afterEach, expect } from "vitest";
4+
import { expect } from "vitest";
55
import { z } from "zod";
66
import { Worker } from "./worker.js";
77
import { createRedisClient } from "@internal/redis";
8-
import { shutdownManager } from "@trigger.dev/core/v3/serverOnly";
98

109
describe("Worker", () => {
11-
afterEach(() => {
12-
(shutdownManager as unknown as { _reset: () => void })._reset();
13-
});
14-
1510
redisTest("Process items that don't throw", { timeout: 30_000 }, async ({ redisContainer }) => {
1611
const processedItems: number[] = [];
1712
const worker = new Worker({

0 commit comments

Comments
 (0)