import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { debug, debugVerbose } from "../../utils/debug"; describe("Debug utils", () => { let originalEnv: NodeJS.ProcessEnv; let consoleSpy: any; beforeEach(() => { originalEnv = { ...process.env }; consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); }); afterEach(() => { process.env = originalEnv; consoleSpy.mockRestore(); }); describe("debug function", () => { it("should log when DEBUG_CACHE_HANDLER is set", () => { process.env.DEBUG_CACHE_HANDLER = "true"; debug("red", "Test message", "arg1", "arg2"); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[31m", "DEBUG CACHE HANDLER: ", "Test message", "arg1", "arg2" ); }); it("should not log when DEBUG_CACHE_HANDLER is not set", () => { process.env.DEBUG_CACHE_HANDLER = undefined; debug("red", "Test message", "arg1", "arg2"); expect(consoleSpy).not.toHaveBeenCalled(); }); it("should not log when DEBUG_CACHE_HANDLER is set to false", () => { process.env.DEBUG_CACHE_HANDLER = ""; debug("red", "Test message", "arg1", "arg2"); expect(consoleSpy).not.toHaveBeenCalled(); }); it("should handle different color codes", () => { process.env.DEBUG_CACHE_HANDLER = "true"; const colors = ["red", "green", "blue", "yellow", "white", "cyan"]; colors.forEach((color) => { debug(color as any, `Test ${color} message`); }); expect(consoleSpy).toHaveBeenCalledTimes(colors.length); }); it("should include color codes in output when environment supports it", () => { process.env.DEBUG_CACHE_HANDLER = "true"; debug("red", "Red message"); debug("green", "Green message"); debug("blue", "Blue message"); // Check that color codes are included (ANSI escape sequences) const calls = consoleSpy.mock.calls; expect(calls[0][0]).toBe("\u001b[31m"); // Red expect(calls[1][0]).toBe("\u001b[32m"); // Green expect(calls[2][0]).toBe("\u001b[34m"); // Blue }); it("should handle multiple arguments", () => { process.env.DEBUG_CACHE_HANDLER = "true"; const obj = { test: "object" }; const arr = [1, 2, 3]; debug("cyan", "Multiple args", obj, arr, "string", 42); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[36m", "DEBUG CACHE HANDLER: ", "Multiple args", obj, arr, "string", 42 ); }); it("should handle undefined and null arguments", () => { process.env.DEBUG_CACHE_HANDLER = "true"; debug("white", "Null and undefined", null, undefined); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[37m", "DEBUG CACHE HANDLER: ", "Null and undefined", null, undefined ); }); it("should work with empty message", () => { process.env.DEBUG_CACHE_HANDLER = "true"; debug("yellow", ""); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[33m", "DEBUG CACHE HANDLER: ", "" ); }); }); describe("debugVerbose function", () => { it("should log when DEBUG_CACHE_HANDLER_VERBOSE is set", () => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = "true"; debugVerbose("Verbose test message", "arg1", "arg2"); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[90m", "DEBUG CACHE HANDLER VERBOSE: ", "Verbose test message", "arg1", "arg2" ); }); it("should not log when DEBUG_CACHE_HANDLER_VERBOSE is not set", () => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = undefined; debugVerbose("Verbose test message", "arg1", "arg2"); expect(consoleSpy).not.toHaveBeenCalled(); }); it("should not log when DEBUG_CACHE_HANDLER_VERBOSE is set to false", () => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = "false"; debugVerbose("Verbose test message", "arg1", "arg2"); expect(consoleSpy).not.toHaveBeenCalled(); }); it("should use gray color for verbose messages", () => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = "true"; debugVerbose("Gray verbose message"); // Check that gray color code is used (ANSI escape sequence for gray) const call = consoleSpy.mock.calls[0]; expect(call[0]).toBe("\u001b[90m"); // Gray }); it("should handle complex objects in verbose mode", () => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = "true"; const complexObj = { nested: { array: [1, 2, { deep: "value" }], buffer: Buffer.from("test"), }, func: () => "test", }; debugVerbose("Complex object", complexObj); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[90m", "DEBUG CACHE HANDLER VERBOSE: ", "Complex object", complexObj ); }); it("should work independently of DEBUG_CACHE_HANDLER", () => { process.env.DEBUG_CACHE_HANDLER = undefined; process.env.DEBUG_CACHE_HANDLER_VERBOSE = "true"; debug("red", "Regular debug message"); debugVerbose("Verbose debug message"); expect(consoleSpy).toHaveBeenCalledTimes(1); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[90m", "DEBUG CACHE HANDLER VERBOSE: ", "Verbose debug message" ); }); it("should handle very long strings", () => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = "true"; const longString = "a".repeat(10_000); debugVerbose("Long string test", longString); expect(consoleSpy).toHaveBeenCalledWith( "\x1b[90m", "DEBUG CACHE HANDLER VERBOSE: ", "Long string test", longString ); }); }); describe("environment variable edge cases", () => { it("should handle DEBUG_CACHE_HANDLER with different truthy values", () => { const truthyValues = ["true", "1", "yes", "on", "TRUE", "True"]; truthyValues.forEach((value) => { process.env.DEBUG_CACHE_HANDLER = value; consoleSpy.mockClear(); debug("green", `Test with ${value}`); expect(consoleSpy).toHaveBeenCalled(); }); }); it("should handle DEBUG_CACHE_HANDLER_VERBOSE with different truthy values", () => { const truthyValues = ["true", "1", "yes", "on", "TRUE", "True"]; truthyValues.forEach((value) => { process.env.DEBUG_CACHE_HANDLER_VERBOSE = value; consoleSpy.mockClear(); debugVerbose(`Verbose test with ${value}`); expect(consoleSpy).toHaveBeenCalled(); }); }); it("should handle both debug flags enabled simultaneously", () => { process.env.DEBUG_CACHE_HANDLER = "true"; process.env.DEBUG_CACHE_HANDLER_VERBOSE = "true"; debug("red", "Regular message"); debugVerbose("Verbose message"); expect(consoleSpy).toHaveBeenCalledTimes(2); }); it("should handle empty string environment variables", () => { process.env.DEBUG_CACHE_HANDLER = ""; process.env.DEBUG_CACHE_HANDLER_VERBOSE = ""; debug("red", "Should not log"); debugVerbose("Should not log"); expect(consoleSpy).not.toHaveBeenCalled(); }); }); describe("performance considerations", () => { it("should not process arguments when debug is disabled", () => { process.env.DEBUG_CACHE_HANDLER = undefined; const expensiveOperation = vi.fn(() => "expensive result"); debug("red", "Test", expensiveOperation()); // The expensive operation should still be called because JavaScript // evaluates arguments before function calls expect(expensiveOperation).toHaveBeenCalled(); expect(consoleSpy).not.toHaveBeenCalled(); }); it("should handle errors in logged objects gracefully", () => { process.env.DEBUG_CACHE_HANDLER = "true"; const problematicObject = { get cyclical() { return this; }, toJSON() { throw new Error("JSON error"); }, }; // Should not throw an error expect(() => { debug("red", "Problematic object", problematicObject); }).not.toThrow(); expect(consoleSpy).toHaveBeenCalled(); }); }); });