/* * Copyright (C) 2025 TomTom Navigation B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { EventEmitter } from "events"; import { registerErrorHandlers } from "./uncaughtErrorHandlers"; import type { Logger } from "./logger"; describe("registerErrorHandlers", () => { let mockProcess: EventEmitter & { exit: ReturnType }; let mockLogger: Logger; beforeEach(() => { // Create a mock process object that extends EventEmitter mockProcess = Object.assign(new EventEmitter(), { exit: vi.fn(), }) as EventEmitter & { exit: ReturnType }; // Create a mock logger mockLogger = { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), }; }); it("should log uncaught exceptions and exit with code 1", () => { registerErrorHandlers(mockProcess as unknown as NodeJS.Process, mockLogger); const testError = new Error("Test uncaught exception"); mockProcess.emit("uncaughtException", testError); expect(mockLogger.error).toHaveBeenCalledWith( { error: "Test uncaught exception", stack: expect.any(String) }, "Uncaught Exception" ); expect(mockProcess.exit).toHaveBeenCalledWith(1); }); it("should log unhandled rejections with Error reason and exit with code 1", () => { registerErrorHandlers(mockProcess as unknown as NodeJS.Process, mockLogger); const testError = new Error("Test unhandled rejection"); const testPromise = Promise.reject(testError); // Catch the rejection to prevent it from propagating to the test runner testPromise.catch(() => {}); mockProcess.emit("unhandledRejection", testError, testPromise); expect(mockLogger.error).toHaveBeenCalledWith( { reason: "Test unhandled rejection", stack: expect.any(String), promise: expect.stringContaining("Promise"), }, "Unhandled Promise Rejection" ); expect(mockProcess.exit).toHaveBeenCalledWith(1); }); it("should log unhandled rejections with non-Error reason and exit with code 1", () => { registerErrorHandlers(mockProcess as unknown as NodeJS.Process, mockLogger); const testReason = "String rejection reason"; const testPromise = Promise.reject(testReason); // Catch the rejection to prevent it from propagating to the test runner testPromise.catch(() => {}); mockProcess.emit("unhandledRejection", testReason, testPromise); expect(mockLogger.error).toHaveBeenCalledWith( { reason: "String rejection reason", stack: undefined, promise: expect.stringContaining("Promise"), }, "Unhandled Promise Rejection" ); expect(mockProcess.exit).toHaveBeenCalledWith(1); }); it("should register handlers on the provided process instance", () => { registerErrorHandlers(mockProcess as unknown as NodeJS.Process, mockLogger); expect(mockProcess.listenerCount("uncaughtException")).toBe(1); expect(mockProcess.listenerCount("unhandledRejection")).toBe(1); }); });