-
-
Notifications
You must be signed in to change notification settings - Fork 813
Expand file tree
/
Copy pathshutdown.test.ts
More file actions
133 lines (115 loc) · 4.04 KB
/
shutdown.test.ts
File metadata and controls
133 lines (115 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const callHook = vi.fn().mockResolvedValue(undefined);
vi.mock("../../src/runtime/internal/app.ts", () => ({
useNitroApp: () => ({
hooks: { callHook },
}),
}));
import {
resolveGracefulShutdownConfig,
setupShutdownHooks,
} from "../../src/runtime/internal/shutdown.ts";
describe("resolveGracefulShutdownConfig", () => {
const env = process.env;
afterEach(() => {
process.env = env;
});
it("returns undefined by default", () => {
process.env = { ...env };
delete process.env.NITRO_SHUTDOWN_DISABLED;
delete process.env.NITRO_SHUTDOWN_TIMEOUT;
expect(resolveGracefulShutdownConfig()).toBeUndefined();
});
it.each([
{ value: "true", expected: false },
{ value: "false", expected: undefined },
{ value: "", expected: undefined },
{ value: "1", expected: undefined },
{ value: "yes", expected: undefined },
])("NITRO_SHUTDOWN_DISABLED=$value returns $expected", ({ value, expected }) => {
process.env = { ...env, NITRO_SHUTDOWN_DISABLED: value };
delete process.env.NITRO_SHUTDOWN_TIMEOUT;
expect(resolveGracefulShutdownConfig()).toBe(expected);
});
it("returns gracefulTimeout in seconds from NITRO_SHUTDOWN_TIMEOUT ms", () => {
process.env = { ...env, NITRO_SHUTDOWN_TIMEOUT: "10000" };
delete process.env.NITRO_SHUTDOWN_DISABLED;
expect(resolveGracefulShutdownConfig()).toEqual({ gracefulTimeout: 10 });
});
it("disabled takes priority over timeout", () => {
process.env = {
...env,
NITRO_SHUTDOWN_DISABLED: "true",
NITRO_SHUTDOWN_TIMEOUT: "10000",
};
expect(resolveGracefulShutdownConfig()).toBe(false);
});
it("ignores non-numeric timeout", () => {
process.env = { ...env, NITRO_SHUTDOWN_TIMEOUT: "abc" };
delete process.env.NITRO_SHUTDOWN_DISABLED;
expect(resolveGracefulShutdownConfig()).toBeUndefined();
});
});
describe("setupShutdownHooks", () => {
let savedSIGTERM: Function[];
let savedSIGINT: Function[];
beforeEach(() => {
savedSIGTERM = process.listeners("SIGTERM").slice();
savedSIGINT = process.listeners("SIGINT").slice();
callHook.mockClear();
callHook.mockResolvedValue(undefined);
});
afterEach(() => {
process.removeAllListeners("SIGTERM");
process.removeAllListeners("SIGINT");
for (const fn of savedSIGTERM) process.on("SIGTERM", fn as () => void);
for (const fn of savedSIGINT) process.on("SIGINT", fn as () => void);
});
it("registers SIGTERM and SIGINT handlers", () => {
const beforeTERM = process.listenerCount("SIGTERM");
const beforeINT = process.listenerCount("SIGINT");
setupShutdownHooks();
expect(process.listenerCount("SIGTERM")).toBe(beforeTERM + 1);
expect(process.listenerCount("SIGINT")).toBe(beforeINT + 1);
});
it("calls close hook on SIGTERM", async () => {
setupShutdownHooks();
process.emit("SIGTERM", "SIGTERM");
await vi.waitFor(() => {
expect(callHook).toHaveBeenCalledWith("close");
});
});
it("calls close hook on SIGINT", async () => {
setupShutdownHooks();
process.emit("SIGINT", "SIGINT");
await vi.waitFor(() => {
expect(callHook).toHaveBeenCalledWith("close");
});
});
it("awaits the close hook promise", async () => {
let resolved = false;
callHook.mockImplementation(
() =>
new Promise<void>((r) => {
setTimeout(() => {
resolved = true;
r();
}, 50);
})
);
setupShutdownHooks();
process.emit("SIGTERM", "SIGTERM");
await vi.waitFor(() => expect(resolved).toBe(true));
});
it("logs error if close hook throws", async () => {
const error = new Error("cleanup failed");
callHook.mockRejectedValueOnce(error);
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
setupShutdownHooks();
process.emit("SIGTERM", "SIGTERM");
await vi.waitFor(() => {
expect(consoleSpy).toHaveBeenCalledWith("[nitro] Error running close hook:", error);
});
consoleSpy.mockRestore();
});
});