Files
kandev/apps/web/lib/debug/log.test.ts
T

117 lines
3.7 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { createDebugLogger } from "./log";
describe("createDebugLogger", () => {
beforeEach(() => {
vi.spyOn(console, "debug").mockImplementation(() => {});
});
afterEach(() => {
vi.restoreAllMocks();
});
it("returns a function", () => {
expect(typeof createDebugLogger("test")).toBe("function");
});
it("prefixes output with [namespace]", () => {
const log = createDebugLogger("my-ns");
log("hello");
expect(console.debug).toHaveBeenCalledWith("[my-ns] hello");
});
it("logs plain strings as-is", () => {
const log = createDebugLogger("ns");
log("simple message");
expect(console.debug).toHaveBeenCalledWith("[ns] simple message");
});
it("flattens plain objects to key=value pairs", () => {
const log = createDebugLogger("ns");
log("msg", { a: 1, b: "hello world" });
expect(console.debug).toHaveBeenCalledWith('[ns] msg a=1 b="hello world"');
});
it("quotes values containing spaces", () => {
const log = createDebugLogger("ns");
log({ key: "value with space" });
expect(console.debug).toHaveBeenCalledWith('[ns] key="value with space"');
});
it("leaves bare values unquoted", () => {
const log = createDebugLogger("ns");
log({ key: "simple_value" });
expect(console.debug).toHaveBeenCalledWith("[ns] key=simple_value");
});
it("formats null and undefined", () => {
const log = createDebugLogger("ns");
log({ a: null, b: undefined });
expect(console.debug).toHaveBeenCalledWith("[ns] a=null b=undefined");
});
it("formats numbers and booleans", () => {
const log = createDebugLogger("ns");
log({ count: 42, flag: true });
expect(console.debug).toHaveBeenCalledWith("[ns] count=42 flag=true");
});
it("formats Error objects with name and message", () => {
const log = createDebugLogger("ns");
log({ err: new Error("boom") });
expect(console.debug).toHaveBeenCalledWith('[ns] err={"name":"Error","message":"boom"}');
});
it("serializes nested objects as JSON", () => {
const log = createDebugLogger("ns");
log({ nested: { x: 1 } });
expect(console.debug).toHaveBeenCalledWith('[ns] nested={"x":1}');
});
it("mixes strings and objects in a single call", () => {
const log = createDebugLogger("ns");
log("prefix", { a: 1 }, "suffix");
expect(console.debug).toHaveBeenCalledWith("[ns] prefix a=1 suffix");
});
});
describe("IS_DEBUG", () => {
// Module re-imports are required because IS_DEBUG is a module-level constant
// evaluated at import time. resetModules forces re-evaluation with the
// current env / globals each time.
beforeEach(() => {
vi.resetModules();
});
afterEach(() => {
vi.unstubAllEnvs();
vi.unstubAllGlobals();
});
it("is true in a production build when window.__KANDEV_DEBUG is set", async () => {
// Simulates `make start-debug`: production bundle, runtime window flag.
vi.stubEnv("NODE_ENV", "production");
vi.stubEnv("NEXT_PUBLIC_KANDEV_DEBUG", "");
vi.stubGlobal("window", { __KANDEV_DEBUG: true });
const { IS_DEBUG } = await import("./log");
expect(IS_DEBUG).toBe(true);
});
it("is false in a production build with no flag set", async () => {
vi.stubEnv("NODE_ENV", "production");
vi.stubEnv("NEXT_PUBLIC_KANDEV_DEBUG", "");
vi.stubGlobal("window", {});
const { IS_DEBUG } = await import("./log");
expect(IS_DEBUG).toBe(false);
});
it("is true when NEXT_PUBLIC_KANDEV_DEBUG=true at build time", async () => {
vi.stubEnv("NODE_ENV", "production");
vi.stubEnv("NEXT_PUBLIC_KANDEV_DEBUG", "true");
vi.stubGlobal("window", {});
const { IS_DEBUG } = await import("./log");
expect(IS_DEBUG).toBe(true);
});
});