/* eslint-disable @typescript-eslint/no-empty-function */ import { describe, expect, mock, test } from "bun:test"; import type { Sink } from "../../../src/web/utils/logger"; import { AntdMessageSink, ConsoleSink, createConsoleLogger, createDefaultLogger, createMemoryLogger, createNoopLogger, } from "../../../src/web/utils/logger"; describe("ConsoleSink", () => { test("调试环境输出 debug 级别", () => { const sink = new ConsoleSink(false); const spy = mock((..._args: unknown[]) => {}); const orig = console.log; console.log = spy; sink.write("debug", "测试消息", undefined, {}); console.log = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:DEBUG\] 测试消息/); }); test("生产环境屏蔽 debug 级别", () => { const sink = new ConsoleSink(true); const spy = mock((..._args: unknown[]) => {}); const orig = console.log; console.log = spy; sink.write("debug", "测试消息", undefined, {}); console.log = orig; expect(spy).toHaveBeenCalledTimes(0); }); test("生产环境屏蔽 info 级别", () => { const sink = new ConsoleSink(true); const spy = mock((..._args: unknown[]) => {}); const orig = console.log; console.log = spy; sink.write("info", "测试消息", undefined, {}); console.log = orig; expect(spy).toHaveBeenCalledTimes(0); }); test("生产环境保留 warn 级别", () => { const sink = new ConsoleSink(true); const spy = mock((..._args: unknown[]) => {}); const orig = console.warn; console.warn = spy; sink.write("warn", "测试消息", undefined, {}); console.warn = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:WARN\] 测试消息/); }); test("生产环境保留 error 级别", () => { const sink = new ConsoleSink(true); const spy = mock((..._args: unknown[]) => {}); const orig = console.error; console.error = spy; sink.write("error", "测试消息", undefined, {}); console.error = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:ERROR\] 测试消息/); }); test("绑定信息追加到消息后缀", () => { const sink = new ConsoleSink(false); const spy = mock((..._args: unknown[]) => {}); const orig = console.log; console.log = spy; sink.write("info", "测试消息", undefined, { id: "123", page: "projects" }); console.log = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:INFO\] 测试消息 \[id=123\]\[page=projects\]/); }); test("data 透传不序列化", () => { const sink = new ConsoleSink(false); const spy = mock((..._args: unknown[]) => {}); const orig = console.error; console.error = spy; const err = new Error("测试错误"); sink.write("error", "失败", err, {}); console.error = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[1]).toBe(err); }); test("error 级别映射到 console.error", () => { const sink = new ConsoleSink(false); const logSpy = mock((..._args: unknown[]) => {}); const errorSpy = mock((..._args: unknown[]) => {}); const origLog = console.log; const origError = console.error; console.log = logSpy; console.error = errorSpy; sink.write("error", "错误", undefined, {}); console.log = origLog; console.error = origError; expect(errorSpy).toHaveBeenCalledTimes(1); expect(logSpy).toHaveBeenCalledTimes(0); }); test("warn 级别映射到 console.warn", () => { const sink = new ConsoleSink(false); const logSpy = mock((..._args: unknown[]) => {}); const warnSpy = mock((..._args: unknown[]) => {}); const origLog = console.log; const origWarn = console.warn; console.log = logSpy; console.warn = warnSpy; sink.write("warn", "警告", undefined, {}); console.log = origLog; console.warn = origWarn; expect(warnSpy).toHaveBeenCalledTimes(1); expect(logSpy).toHaveBeenCalledTimes(0); }); }); describe("AntdMessageSink", () => { test("warn 级别调用 message.warning", () => { const warningSpy = mock(() => {}); const messageApi = { error: mock(() => {}), info: mock(() => {}), loading: mock(() => {}), success: mock(() => {}), warning: warningSpy, }; const sink = new AntdMessageSink(messageApi as never); sink.write("warn", "操作警告", undefined, {}); expect(warningSpy).toHaveBeenCalledWith("操作警告"); }); test("error 级别调用 message.error", () => { const errorSpy = mock(() => {}); const messageApi = { error: errorSpy, info: mock(() => {}), loading: mock(() => {}), success: mock(() => {}), warning: mock(() => {}), }; const sink = new AntdMessageSink(messageApi as never); sink.write("error", "操作失败", undefined, {}); expect(errorSpy).toHaveBeenCalledWith("操作失败"); }); test("debug 级别不触发 notification", () => { const messageApi = { error: mock(() => {}), info: mock(() => {}), loading: mock(() => {}), success: mock(() => {}), warning: mock(() => {}), }; const sink = new AntdMessageSink(messageApi as never); sink.write("debug", "调试消息", undefined, {}); expect(messageApi.info).toHaveBeenCalledTimes(0); expect(messageApi.warning).toHaveBeenCalledTimes(0); expect(messageApi.error).toHaveBeenCalledTimes(0); }); test("info 级别不触发 notification", () => { const messageApi = { error: mock(() => {}), info: mock(() => {}), loading: mock(() => {}), success: mock(() => {}), warning: mock(() => {}), }; const sink = new AntdMessageSink(messageApi as never); sink.write("info", "信息消息", undefined, {}); expect(messageApi.info).toHaveBeenCalledTimes(0); expect(messageApi.warning).toHaveBeenCalledTimes(0); expect(messageApi.error).toHaveBeenCalledTimes(0); }); }); describe("NoopLogger", () => { test("所有方法静默不抛异常", () => { const logger = createNoopLogger(); logger.debug("调试"); logger.info("信息"); logger.warn("警告"); logger.error("错误"); expect(logger.child({ page: "test" })).toBe(logger); }); }); describe("MemoryLogger", () => { test("记录所有级别的日志", () => { const logger = createMemoryLogger(); logger.debug("调试"); logger.info("信息"); logger.warn("警告"); logger.error("错误"); expect(logger.entries).toEqual([ { data: undefined, level: "debug", message: "调试" }, { data: undefined, level: "info", message: "信息" }, { data: undefined, level: "warn", message: "警告" }, { data: undefined, level: "error", message: "错误" }, ]); }); test("记录附带 data 的日志", () => { const logger = createMemoryLogger(); const err = new Error("测试"); logger.error("失败", err); expect(logger.entries[0]).toEqual({ data: err, level: "error", message: "失败" }); }); }); describe("DefaultLogger isProduction", () => { function createSpySink(): { entries: Array<{ data: unknown; level: string; message: string }>; sink: Sink } { const entries: Array<{ data: unknown; level: string; message: string }> = []; return { entries, sink: { write(level, message, data) { entries.push({ data, level, message }); }, }, }; } test("isProduction=true 时 debug/info 不记录", () => { const spy = createSpySink(); const logger = createDefaultLogger([spy.sink], true); logger.debug("调试"); logger.info("信息"); expect(spy.entries).toHaveLength(0); }); test("isProduction=true 时 warn/error 正常记录", () => { const spy = createSpySink(); const logger = createDefaultLogger([spy.sink], true); logger.warn("警告"); logger.error("错误"); expect(spy.entries).toHaveLength(2); expect(spy.entries[0]!.level).toBe("warn"); expect(spy.entries[1]!.level).toBe("error"); }); test("isProduction=false 时 debug/info 正常记录", () => { const spy = createSpySink(); const logger = createDefaultLogger([spy.sink], false); logger.debug("调试"); logger.info("信息"); expect(spy.entries).toHaveLength(2); expect(spy.entries[0]!.level).toBe("debug"); expect(spy.entries[1]!.level).toBe("info"); }); }); describe("child() 作用域", () => { test("child 绑定信息输出到日志前缀", () => { const spy = mock((..._args: unknown[]) => {}); const orig = console.warn; console.warn = spy; const sink = new ConsoleSink(false); const logger = createDefaultLogger([sink], false).child({ page: "projects" }); logger.warn("测试"); console.warn = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:WARN\] 测试 \[page=projects\]/); }); test("嵌套 child 追加绑定", () => { const spy = mock((..._args: unknown[]) => {}); const orig = console.warn; console.warn = spy; const sink = new ConsoleSink(false); const logger = createDefaultLogger([sink], false).child({ page: "projects" }).child({ action: "delete" }); logger.warn("测试"); console.warn = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:WARN\] 测试 \[page=projects\]\[action=delete\]/); }); test("嵌套 child 同 key 覆盖", () => { const spy = mock((..._args: unknown[]) => {}); const orig = console.warn; console.warn = spy; const sink = new ConsoleSink(false); const logger = createDefaultLogger([sink], false).child({ page: "projects" }).child({ page: "models" }); logger.warn("测试"); console.warn = orig; expect(spy).toHaveBeenCalledTimes(1); const call = spy.mock.calls[0]!; expect(call[0]).toMatch(/\[Alfred:WARN\] 测试 \[page=models\]/); }); }); describe("setLevel 运行时调整", () => { test("setLevel 可提高最小输出级别", () => { const spy = (() => { const entries: Array<{ level: string; message: string }> = []; return { entries, sink: { write(level: string, message: string) { entries.push({ level, message }); }, }, }; })(); const logger = createDefaultLogger([spy.sink], false); logger.debug("调试"); expect(spy.entries).toHaveLength(1); logger.setLevel("error"); logger.debug("调试2"); logger.warn("警告"); logger.error("错误"); expect(spy.entries).toHaveLength(2); expect(spy.entries[1]!.level).toBe("error"); expect(spy.entries[1]!.message).toBe("错误"); }); }); describe("createConsoleLogger", () => { test("返回非 null Logger 实例", () => { const logger = createConsoleLogger(); expect(logger).not.toBeNull(); expect(typeof logger.debug).toBe("function"); expect(typeof logger.info).toBe("function"); expect(typeof logger.warn).toBe("function"); expect(typeof logger.error).toBe("function"); expect(typeof logger.child).toBe("function"); expect(typeof logger.setLevel).toBe("function"); }); });