/**
* mysql-mcp - MCP Logging Unit Tests
*
* Tests for McpLogger functionality including log level filtering,
* configuration methods, and message sending.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
// Create a fresh McpLogger for each test by importing the class directly
// We need to test the class, not the singleton
describe("McpLogger", () => {
let McpLogger: typeof import("../McpLogging.js").mcpLogger;
let mockServer: {
sendLoggingMessage: ReturnType<typeof vi.fn>;
};
beforeEach(async () => {
vi.clearAllMocks();
// Import fresh module for each test
vi.resetModules();
const module = await import("../McpLogging.js");
McpLogger = module.mcpLogger;
mockServer = {
sendLoggingMessage: vi.fn(),
};
});
afterEach(() => {
vi.resetModules();
});
describe("Configuration Methods", () => {
it("should allow setting the server", () => {
expect(() => McpLogger.setServer(mockServer as never)).not.toThrow();
});
it("should allow setting the logger name", () => {
expect(() => McpLogger.setLoggerName("test-logger")).not.toThrow();
});
it("should allow enabling/disabling logging", () => {
expect(() => McpLogger.setEnabled(false)).not.toThrow();
expect(() => McpLogger.setEnabled(true)).not.toThrow();
});
it("should allow setting minimum log level", () => {
expect(() => McpLogger.setMinLevel("debug")).not.toThrow();
expect(() => McpLogger.setMinLevel("error")).not.toThrow();
});
});
describe("Log Level Filtering", () => {
beforeEach(() => {
McpLogger.setServer(mockServer as never);
McpLogger.setEnabled(true);
McpLogger.setConnected(true);
});
it("should log messages at or above minimum level", () => {
McpLogger.setMinLevel("info");
McpLogger.info("test info message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({
level: "info",
data: "test info message",
}),
);
});
it("should not log messages below minimum level", () => {
McpLogger.setMinLevel("warning");
McpLogger.debug("debug message");
McpLogger.info("info message");
expect(mockServer.sendLoggingMessage).not.toHaveBeenCalled();
});
it("should respect debug level filtering", () => {
McpLogger.setMinLevel("debug");
McpLogger.debug("debug message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "debug" }),
);
});
it("should respect error level filtering", () => {
McpLogger.setMinLevel("error");
McpLogger.warning("warning message");
McpLogger.error("error message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledTimes(1);
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "error" }),
);
});
});
describe("Convenience Methods", () => {
beforeEach(() => {
McpLogger.setServer(mockServer as never);
McpLogger.setEnabled(true);
McpLogger.setConnected(true);
McpLogger.setMinLevel("debug");
});
it("should call debug with correct level", () => {
McpLogger.debug("debug message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "debug" }),
);
});
it("should call info with correct level", () => {
McpLogger.info("info message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "info" }),
);
});
it("should call notice with correct level", () => {
McpLogger.notice("notice message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "notice" }),
);
});
it("should call warning with correct level", () => {
McpLogger.warning("warning message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "warning" }),
);
});
it("should call error with correct level", () => {
McpLogger.error("error message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "error" }),
);
});
it("should call critical with correct level", () => {
McpLogger.critical("critical message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "critical" }),
);
});
it("should call alert with correct level", () => {
McpLogger.alert("alert message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "alert" }),
);
});
it("should call emergency with correct level", () => {
McpLogger.emergency("emergency message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({ level: "emergency" }),
);
});
});
describe("Data Handling", () => {
beforeEach(() => {
McpLogger.setServer(mockServer as never);
McpLogger.setEnabled(true);
McpLogger.setConnected(true);
McpLogger.setMinLevel("debug");
});
it("should send message as string when no data provided", () => {
McpLogger.info("simple message");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({
data: "simple message",
}),
);
});
it("should include additional data in message", () => {
McpLogger.info("message with data", { key: "value", count: 42 });
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
message: "message with data",
key: "value",
count: 42,
}),
}),
);
});
});
describe("Disabled States", () => {
it("should not log when disabled", () => {
McpLogger.setServer(mockServer as never);
McpLogger.setEnabled(false);
McpLogger.info("should not be logged");
expect(mockServer.sendLoggingMessage).not.toHaveBeenCalled();
});
it("should not log when server not set", () => {
McpLogger.setEnabled(true);
// Don't set server
McpLogger.info("should not be logged");
expect(mockServer.sendLoggingMessage).not.toHaveBeenCalled();
});
it("should handle sendLoggingMessage errors gracefully", () => {
McpLogger.setServer(mockServer as never);
McpLogger.setEnabled(true);
McpLogger.setConnected(true);
mockServer.sendLoggingMessage.mockImplementation(() => {
throw new Error("Transport error");
});
// Should not throw
expect(() => McpLogger.info("test")).not.toThrow();
});
});
describe("Logger Name", () => {
beforeEach(() => {
McpLogger.setServer(mockServer as never);
McpLogger.setEnabled(true);
McpLogger.setConnected(true);
McpLogger.setMinLevel("debug");
});
it("should use default logger name", () => {
McpLogger.info("test");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({
logger: "mysql-mcp",
}),
);
});
it("should use custom logger name when set", () => {
McpLogger.setLoggerName("custom-logger");
McpLogger.info("test");
expect(mockServer.sendLoggingMessage).toHaveBeenCalledWith(
expect.objectContaining({
logger: "custom-logger",
}),
);
});
});
});