Files
logger/test/logger.test.ts
T
mifi f13fd6d96b
ci/woodpecker/push/ci Pipeline failed
Initial commit
2026-08-05 01:09:30 -03:00

95 lines
3.7 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { createLogger, parseLoggingOverride } from "../src/index.js";
import type { LogEvent, LogSink } from "../src/index.js";
function sink(): { sink: LogSink; events: LogEvent[] } {
const events: LogEvent[] = [];
return { events, sink: { emit: (event) => events.push(event) } };
}
describe("parseLoggingOverride", () => {
it("accepts a global level and a namespace-filtered level", () => {
expect(parseLoggingOverride("debug")).toEqual({ level: "debug", namespaces: [] });
expect(parseLoggingOverride("debug:WIDGET, API")).toEqual({
level: "debug",
namespaces: ["WIDGET", "API"],
});
expect(parseLoggingOverride("verbose")).toBeUndefined();
});
});
describe("createLogger", () => {
it("uses environment defaults and never evaluates suppressed lazy data", () => {
const destination = sink();
const expensive = vi.fn(() => ({ huge: "snapshot" }));
const logger = createLogger({ environment: "production", sinks: [destination.sink] });
logger.debug("ignored", expensive);
logger.error("kept", expensive);
expect(expensive).toHaveBeenCalledTimes(1);
expect(destination.events).toHaveLength(1);
expect(destination.events[0]?.arguments).toEqual(["kept", { huge: "snapshot" }]);
});
it("applies session namespace filtering to namespace descendants", () => {
const destination = sink();
const storage = { getItem: () => "debug:WIDGET" };
createLogger({
environment: "production",
namespace: "WIDGET:Button",
sessionStorage: storage,
sinks: [destination.sink],
}).debug("shown");
createLogger({
environment: "production",
namespace: "API",
sessionStorage: storage,
sinks: [destination.sink],
}).debug("hidden");
expect(destination.events.map((event) => event.arguments[0])).toEqual(["shown"]);
});
it("builds immutable child namespaces and reports failed assertions as errors", () => {
const destination = sink();
const root = createLogger({
environment: "development",
namespace: "WIDGET",
sinks: [destination.sink],
});
root.child("Button").assert(false, "missing label");
expect(root.namespace).toBe("WIDGET");
expect(destination.events[0]).toMatchObject({
level: "error",
namespace: "WIDGET:Button",
arguments: ["Assertion failed", "missing label"],
});
});
it("sends production browser errors to Sentry without writing to the console", () => {
const destination = sink();
const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined);
const logger = createLogger({
environment: "production",
runtime: "browser",
sentry: destination.sink,
});
logger.error("captured");
expect(destination.events).toHaveLength(1);
expect(consoleError).not.toHaveBeenCalled();
consoleError.mockRestore();
});
it("keeps production Node errors on stderr as well as sending them to Sentry", () => {
const destination = sink();
const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined);
const logger = createLogger({
environment: "production",
runtime: "node",
sentry: destination.sink,
});
logger.error("captured");
expect(destination.events).toHaveLength(1);
expect(consoleError).toHaveBeenCalledTimes(1);
consoleError.mockRestore();
});
});