226 lines
7.4 KiB
JavaScript
226 lines
7.4 KiB
JavaScript
import { assert, assertEquals } from "@std/assert";
|
|
import Logger from "../lib/logger.ts";
|
|
import {
|
|
clearCapturedLogs,
|
|
getCapturedLogs,
|
|
getFirstLogAsJSON,
|
|
setupMocks,
|
|
} from "./helpers/logger-test-helpers.js";
|
|
|
|
// Setup and teardown for all tests
|
|
setupMocks();
|
|
|
|
Deno.test("Logger JSON Formatter - Basic JSON Output - should produce valid JSON output", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.info("test message");
|
|
|
|
assertEquals(getCapturedLogs().length, 1);
|
|
const logOutput = getCapturedLogs()[0];
|
|
|
|
// Should be valid JSON
|
|
JSON.parse(logOutput);
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Basic JSON Output - should include all required fields in JSON output", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json", callerLevel: "info" });
|
|
logger.info("test message");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
|
|
assertEquals(parsed.level, "info");
|
|
assertEquals(parsed.levelNumber, 2);
|
|
assertEquals(parsed.msg, "test message");
|
|
assertEquals(typeof parsed.time, "string");
|
|
assertEquals(typeof parsed.pid, "number");
|
|
assertEquals(typeof parsed.hostname, "string");
|
|
assert(parsed.callerFile);
|
|
assertEquals(typeof parsed.callerLine, "number");
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Basic JSON Output - should format timestamp correctly based on time option", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json", time: "short" });
|
|
logger.info("test message");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
// Should be short format, not ISO
|
|
assert(parsed.time.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/));
|
|
assert(!parsed.time.includes("T"));
|
|
assert(!parsed.time.includes("Z"));
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - JSON Error Handling - should handle circular references in log entry", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
|
|
// Create a circular reference by modifying the logger's formatters
|
|
const originalJsonFormatter = logger.formatters.json;
|
|
logger.formatters.json = function (logEntry) {
|
|
// Add a circular reference to the logEntry
|
|
const circular = { self: null };
|
|
circular.self = circular;
|
|
logEntry.circular = circular;
|
|
|
|
// Call the original formatter which should handle the error
|
|
return originalJsonFormatter.call(this, logEntry);
|
|
};
|
|
|
|
logger.info("test with circular reference");
|
|
|
|
const logOutput = getCapturedLogs()[0];
|
|
// Should be valid JSON despite circular reference
|
|
const parsed = JSON.parse(logOutput);
|
|
// Should contain error information
|
|
assert(parsed.jsonError.includes("JSON stringify failed"));
|
|
assertEquals(parsed.msg, "test with circular reference");
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - JSON Error Handling - should handle JSON stringify errors with fallback", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
|
|
// Create a problematic object that will cause JSON.stringify to fail
|
|
const problematic = {};
|
|
Object.defineProperty(problematic, "badProp", {
|
|
get() {
|
|
throw new Error("Property access error");
|
|
},
|
|
enumerable: true,
|
|
});
|
|
|
|
// Test the formatter directly with a problematic object
|
|
const problematicLogEntry = {
|
|
level: "info",
|
|
msg: "test message",
|
|
problematic: problematic,
|
|
};
|
|
|
|
const result = logger.formatters.json(problematicLogEntry);
|
|
|
|
// Should produce valid JSON with error info
|
|
const parsed = JSON.parse(result);
|
|
assert(parsed.jsonError.includes("JSON stringify failed"));
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - JSON Error Handling - should handle extreme JSON stringify failures", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
|
|
// Create an object that will fail even the safe fallback
|
|
// by mocking JSON.stringify to always throw
|
|
const originalStringify = JSON.stringify;
|
|
let callCount = 0;
|
|
|
|
JSON.stringify = function (...args) {
|
|
callCount++;
|
|
if (callCount <= 2) {
|
|
throw new Error("Mock JSON error");
|
|
}
|
|
return originalStringify.apply(this, args);
|
|
};
|
|
|
|
try {
|
|
const result = logger.formatters.json({
|
|
level: "error",
|
|
msg: "test message",
|
|
});
|
|
|
|
// Should still produce valid JSON string even after multiple failures
|
|
const parsed = JSON.parse(result);
|
|
assertEquals(parsed.level, "error");
|
|
assertEquals(parsed.msg, "test message");
|
|
assert(parsed.jsonError.includes("Multiple JSON errors occurred"));
|
|
} finally {
|
|
JSON.stringify = originalStringify;
|
|
}
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Special Characters and Edge Cases - should handle special characters", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.info('Special chars: "quotes", \\backslash, \nnewline');
|
|
|
|
// Should produce valid JSON despite special characters
|
|
JSON.parse(getCapturedLogs()[0]);
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Special Characters and Edge Cases - should handle empty messages", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.info("");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.msg, "");
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Special Characters and Edge Cases - should handle null and undefined arguments", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.info("Value: %s", null);
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.msg, "Value: null");
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Special Characters and Edge Cases - should handle very long messages", () => {
|
|
clearCapturedLogs();
|
|
const longMessage = "x".repeat(10000);
|
|
const logger = new Logger({ format: "json" });
|
|
logger.info(longMessage);
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.msg, longMessage);
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - Special Characters and Edge Cases - should handle objects in messages", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
const obj = { key: "value", nested: { prop: 123 } };
|
|
logger.info("Object: %j", obj);
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assert(parsed.msg.includes('{"key":"value","nested":{"prop":123}}'));
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - All Log Levels in JSON - should log error messages with correct level", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.error("error message");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.level, "error");
|
|
assertEquals(parsed.levelNumber, 0);
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - All Log Levels in JSON - should log warn messages with correct level", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.warn("warn message");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.level, "warn");
|
|
assertEquals(parsed.levelNumber, 1);
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - All Log Levels in JSON - should log info messages with correct level", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ format: "json" });
|
|
logger.info("info message");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.level, "info");
|
|
assertEquals(parsed.levelNumber, 2);
|
|
});
|
|
|
|
Deno.test("Logger JSON Formatter - All Log Levels in JSON - should log debug messages with correct level", () => {
|
|
clearCapturedLogs();
|
|
const logger = new Logger({ level: "debug", format: "json" });
|
|
logger.debug("debug message");
|
|
|
|
const parsed = getFirstLogAsJSON();
|
|
assertEquals(parsed.level, "debug");
|
|
assertEquals(parsed.levelNumber, 3);
|
|
});
|