import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("./config.js", () => ({ readLoggingConfig: () => undefined, })); vi.mock("./logger.js", () => ({ getLogger: () => ({ trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {}, fatal: () => {}, }), })); let loadConfigCalls = 0; vi.mock("node:module", async () => { const actual = await vi.importActual("node:module"); return Object.assign({}, actual, { createRequire: (url: string | URL) => { const realRequire = actual.createRequire(url); return (specifier: string) => { if (specifier.endsWith("config.js")) { return { loadConfig: () => { loadConfigCalls += 1; if (loadConfigCalls > 5) { return {}; } console.error("config load failed"); return {}; }, }; } return realRequire(specifier); }; }, }); }); type ConsoleSnapshot = { log: typeof console.log; info: typeof console.info; warn: typeof console.warn; error: typeof console.error; debug: typeof console.debug; trace: typeof console.trace; }; let originalIsTty: boolean | undefined; let snapshot: ConsoleSnapshot; beforeEach(() => { loadConfigCalls = 0; vi.resetModules(); snapshot = { log: console.log, info: console.info, warn: console.warn, error: console.error, debug: console.debug, trace: console.trace, }; originalIsTty = process.stdout.isTTY; Object.defineProperty(process.stdout, "isTTY", { value: false, configurable: true }); }); afterEach(() => { console.log = snapshot.log; console.info = snapshot.info; console.warn = snapshot.warn; console.error = snapshot.error; console.debug = snapshot.debug; console.trace = snapshot.trace; Object.defineProperty(process.stdout, "isTTY", { value: originalIsTty, configurable: true }); vi.restoreAllMocks(); }); async function loadLogging() { const logging = await import("../logging.js"); const state = await import("./state.js"); state.loggingState.cachedConsoleSettings = null; return { logging, state }; } describe("getConsoleSettings", () => { it("does not recurse when loadConfig logs during resolution", async () => { const { logging } = await loadLogging(); logging.setConsoleTimestampPrefix(true); logging.enableConsoleCapture(); const { getConsoleSettings } = logging; getConsoleSettings(); expect(loadConfigCalls).toBe(1); }); it("skips config fallback during re-entrant resolution", async () => { const { logging, state } = await loadLogging(); state.loggingState.resolvingConsoleSettings = true; logging.setConsoleTimestampPrefix(true); logging.enableConsoleCapture(); logging.getConsoleSettings(); expect(loadConfigCalls).toBe(0); state.loggingState.resolvingConsoleSettings = false; }); });