// createFallbackLogger Unit-Tests (Phase 1, test-luecken-integration). // // Pinnt beide Pfade des Fallback-Loggers auf das identische // "[ns] msg"-Format — wrapped logger und console-Fallback dürfen // nicht divergieren (Log-Parser/Grep-Konsistenz). import { describe, expect, mock, spyOn, test } from "bun:test"; import { createFallbackLogger } from "../utils"; describe("createFallbackLogger", () => { describe("mit wrapped logger", () => { test("delegiert an logger.error mit [namespace]-Prefix (kein colon)", () => { const error = mock((_msg: string, _data?: Record) => {}); const fallback = createFallbackLogger("redis", { error }); fallback.error("connection lost", { attempt: 3 }); expect(error).toHaveBeenCalledTimes(1); expect(error).toHaveBeenCalledWith("[redis] connection lost", { attempt: 3 }); }); test("reicht fehlendes data-Argument als undefined durch", () => { const error = mock((_msg: string, _data?: Record) => {}); const fallback = createFallbackLogger("jobs", { error }); fallback.error("boom"); expect(error).toHaveBeenCalledWith("[jobs] boom", undefined); }); test("debug() delegiert an logger.debug wenn vorhanden (fw#1812: graceful-shutdown log-level downgrade)", () => { const error = mock((_msg: string, _data?: Record) => {}); const debug = mock((_msg: string, _data?: Record) => {}); const fallback = createFallbackLogger("job-runner", { error, debug }); fallback.debug("connection is closed", { reason: "graceful-shutdown" }); expect(debug).toHaveBeenCalledTimes(1); expect(debug).toHaveBeenCalledWith("[job-runner] connection is closed", { reason: "graceful-shutdown", }); expect(error).not.toHaveBeenCalled(); }); test("debug() fällt auf console.debug zurück wenn der wrapped logger keine debug-Methode hat", () => { const error = mock((_msg: string, _data?: Record) => {}); const spy = spyOn(console, "debug").mockImplementation(() => {}); try { const fallback = createFallbackLogger("job-runner", { error }); fallback.debug("connection is closed"); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith("[job-runner] connection is closed", undefined); } finally { spy.mockRestore(); } }); }); describe("ohne logger (console-Fallback)", () => { test("schreibt auf console.error mit [namespace]-Prefix im selben Format wie wrapped", () => { const spy = spyOn(console, "error").mockImplementation(() => {}); try { const fallback = createFallbackLogger("boot"); fallback.error("no logger wired", { phase: "init" }); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith("[boot] no logger wired", { phase: "init" }); } finally { spy.mockRestore(); } }); test("debug() schreibt auf console.debug mit [namespace]-Prefix", () => { const spy = spyOn(console, "debug").mockImplementation(() => {}); try { const fallback = createFallbackLogger("boot"); fallback.debug("no logger wired", { phase: "init" }); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith("[boot] no logger wired", { phase: "init" }); } finally { spy.mockRestore(); } }); }); });