feat: add Filesystem util and load env util (#7487)

This commit is contained in:
Harminder Virk authored and GitHub committed 2024-05-28 15:31:13 +05:30
1 parent 164bb988e7
commit ef6d748784
24 files changed
+389 -194

No files matched your search

@@ -1,186 +0,0 @@
import { GracefulShutdownServer } from "../graceful-shutdown-server"
describe("GracefulShutdownServer", () => {
beforeEach(() => {
jest.clearAllTimers()
jest.clearAllMocks()
})
afterEach(() => {})
it('should add "isShuttingDown" property to the existing server', () => {
const server = GracefulShutdownServer.create({ on: jest.fn() } as any)
expect(server).toHaveProperty("isShuttingDown")
expect(server.isShuttingDown).toEqual(false)
})
it("should listen for client connections and store reference to them", async () => {
const onEventMock = jest.fn()
GracefulShutdownServer.create({ on: onEventMock } as any)
expect(onEventMock).toBeCalledTimes(3)
expect(onEventMock.mock.calls[2][0]).toEqual("request")
const connectEvent: (socket) => any = onEventMock.mock.calls[0][1]
const onSocketClose = jest.fn()
const socket = { on: onSocketClose }
connectEvent(socket)
expect(socket).toEqual(
expect.objectContaining({
_idle: true,
_connectionId: 1,
})
)
const socket2 = { on: onSocketClose }
connectEvent(socket2)
expect(socket2).toEqual(
expect.objectContaining({
_idle: true,
_connectionId: 2,
})
)
const requestMock = onEventMock.mock.calls[2][1]
expect(typeof requestMock).toEqual("function")
const socket3 = { on: onSocketClose }
const req = { socket: socket3, on: jest.fn() }
const res = { on: jest.fn() }
connectEvent(socket3)
requestMock(req, res)
const finishRequestMock = res.on.mock.calls[0][1]
expect(socket3).toEqual(
expect.objectContaining({
_idle: false,
_connectionId: 3,
})
)
finishRequestMock()
expect(socket3).toEqual(
expect.objectContaining({
_idle: true,
_connectionId: 3,
})
)
expect(onSocketClose).toBeCalledTimes(3)
expect(onSocketClose.mock.calls[0][0]).toEqual("close")
})
it("waits requests to complete before shutting the server down", (done: Function) => {
jest.useFakeTimers()
const onEventMock = jest.fn()
const setIntervalSpy = jest.spyOn(global, "setInterval")
const setTimeoutSpy = jest.spyOn(global, "setTimeout")
const clearIntervalSpy = jest.spyOn(global, "clearInterval")
const waitTime = 200
let closeServerCallback: Function
const server = GracefulShutdownServer.create(
{
close: (callback) => {
closeServerCallback = callback
},
on: onEventMock,
} as any,
waitTime
)
const requestMock = onEventMock.mock.calls[2][1]
const connectEvent: (socket) => any = onEventMock.mock.calls[0][1]
expect(typeof requestMock).toEqual("function")
const socket = { on: jest.fn(), destroy: jest.fn() }
const req = { socket, on: jest.fn() }
const res = { on: jest.fn() }
connectEvent(socket)
requestMock(req, res)
const finishRequestMock = res.on.mock.calls[0][1]
server.shutdown().then(() => {
done()
})
expect(setTimeoutSpy).toBeCalledTimes(0)
expect(setIntervalSpy).toBeCalledTimes(1)
expect(setIntervalSpy.mock.calls[0][1]).toEqual(waitTime)
expect(clearIntervalSpy).toBeCalledTimes(0)
expect(socket.destroy).toBeCalledTimes(0)
jest.advanceTimersByTime(200)
expect(socket.destroy).toBeCalledTimes(0)
finishRequestMock()
expect(socket.destroy).toBeCalledTimes(0)
jest.advanceTimersByTime(waitTime)
expect(socket.destroy).toBeCalledTimes(1)
closeServerCallback!()
})
it("should force close all connections after the timeout is reached", (done: Function) => {
jest.useFakeTimers()
const onEventMock = jest.fn()
const setIntervalSpy = jest.spyOn(global, "setInterval")
const setTimeoutSpy = jest.spyOn(global, "setTimeout")
const clearIntervalSpy = jest.spyOn(global, "clearInterval")
const waitTime = 300
let closeServerCallback: Function
const server = GracefulShutdownServer.create(
{
close: (callback) => {
closeServerCallback = callback
},
on: onEventMock,
} as any,
waitTime
)
const requestMock = onEventMock.mock.calls[2][1]
const connectEvent: (socket) => any = onEventMock.mock.calls[0][1]
expect(typeof requestMock).toEqual("function")
const socket = { on: jest.fn(), destroy: jest.fn() }
const req = { socket, on: jest.fn() }
const res = { on: jest.fn() }
connectEvent(socket)
requestMock(req, res) // pending request
const forceTimeout = 600
server.shutdown(forceTimeout).then(() => {
done()
})
expect(setTimeoutSpy).toBeCalledTimes(1)
expect(setTimeoutSpy.mock.calls[0][1]).toEqual(forceTimeout)
expect(setIntervalSpy).toBeCalledTimes(1)
expect(setIntervalSpy.mock.calls[0][1]).toEqual(waitTime)
expect(clearIntervalSpy).toBeCalledTimes(0)
expect(socket.destroy).toBeCalledTimes(0)
jest.advanceTimersByTime(waitTime)
expect(socket.destroy).toBeCalledTimes(0)
jest.advanceTimersByTime(forceTimeout)
expect(socket.destroy).toBeCalledTimes(1)
closeServerCallback!()
})
})
@@ -1,96 +0,0 @@
import { Server } from "http"
import { Socket } from "net"
import Timeout = NodeJS.Timeout
interface SocketState extends Socket {
_idle: boolean
_connectionId: number
}
export abstract class GracefulShutdownServer {
public isShuttingDown: boolean
public abstract shutdown(timeout?: number): Promise<void>
public static create<T extends Server>(
originalServer: T,
waitingResponseTime: number = 200
): T & GracefulShutdownServer {
let connectionId = 0
let shutdownPromise: Promise<void>
const allSockets: { [id: number]: SocketState } = {}
const server = originalServer as T & GracefulShutdownServer
server.isShuttingDown = false
server.shutdown = async (timeout: number = 0): Promise<void> => {
if (server.isShuttingDown) {
return shutdownPromise
}
server.isShuttingDown = true
shutdownPromise = new Promise((ok, nok) => {
let forceQuit = false
let cleanInterval: Timeout
try {
// stop accepting new incoming connections
server.close(() => {
clearInterval(cleanInterval)
ok()
})
if (+timeout > 0) {
setTimeout(() => {
forceQuit = true
}, timeout).unref()
}
cleanInterval = setInterval(() => {
if (!Object.keys(allSockets).length) {
clearInterval(cleanInterval)
}
for (const key of Object.keys(allSockets)) {
const socketId = +key
if (forceQuit || allSockets[socketId]._idle) {
allSockets[socketId].destroy()
delete allSockets[socketId]
}
}
}, waitingResponseTime)
} catch (error) {
clearInterval(cleanInterval!)
return nok(error)
}
})
return shutdownPromise
}
const onConnect = (originalSocket) => {
connectionId++
const socket = originalSocket as SocketState
socket._idle = true
socket._connectionId = connectionId
allSockets[connectionId] = socket
socket.on("close", () => {
delete allSockets[socket._connectionId]
})
}
server.on("connection", onConnect)
server.on("secureConnection", onConnect)
server.on("request", (req, res) => {
const customSocket = req.socket as SocketState
customSocket._idle = false
res.on("finish", () => {
customSocket._idle = true
})
})
return server
}
}
-1
View File
@@ -3,4 +3,3 @@ export * from "./exception-formatter"
export * from "./middlewares"
export * from "./omit-deep"
export * from "./remove-undefined-properties"
export * from "./graceful-shutdown-server"