forked from syntaxbullet/aurorabot
119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
import { expect, test, describe, beforeAll, afterAll, spyOn } from "bun:test";
|
|
import { logger } from "./logger";
|
|
import { existsSync, unlinkSync, readFileSync, writeFileSync } from "fs";
|
|
import { join } from "path";
|
|
|
|
describe("Logger", () => {
|
|
const logDir = join(process.cwd(), "logs");
|
|
const logFile = join(logDir, "error.log");
|
|
|
|
beforeAll(() => {
|
|
// Cleanup if exists
|
|
try {
|
|
if (existsSync(logFile)) unlinkSync(logFile);
|
|
} catch (e) {}
|
|
});
|
|
|
|
test("should log info messages to console with correct format", () => {
|
|
const spy = spyOn(console, "log");
|
|
const message = "Formatting test";
|
|
logger.info("system", message);
|
|
|
|
expect(spy).toHaveBeenCalled();
|
|
const callArgs = spy.mock.calls[0]?.[0];
|
|
expect(callArgs).toBeDefined();
|
|
if (callArgs) {
|
|
// Strict regex check for ISO timestamp and format
|
|
const regex = /^\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\] \[INFO\] \[SYSTEM\] Formatting test$/;
|
|
expect(callArgs).toMatch(regex);
|
|
}
|
|
spy.mockRestore();
|
|
});
|
|
|
|
test("should write error logs to file with stack trace", async () => {
|
|
const errorMessage = "Test error message";
|
|
const testError = new Error("Source error");
|
|
|
|
logger.error("system", errorMessage, testError);
|
|
|
|
// Polling for file write instead of fixed timeout
|
|
let content = "";
|
|
for (let i = 0; i < 20; i++) {
|
|
if (existsSync(logFile)) {
|
|
content = readFileSync(logFile, "utf-8");
|
|
if (content.includes("Source error")) break;
|
|
}
|
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
}
|
|
|
|
expect(content).toMatch(/^\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\] \[ERROR\] \[SYSTEM\] Test error message: Source error/);
|
|
expect(content).toContain("Stack Trace:");
|
|
expect(content).toContain("Error: Source error");
|
|
expect(content).toContain("logger.test.ts");
|
|
});
|
|
|
|
test("should handle log directory creation failures gracefully", async () => {
|
|
const consoleSpy = spyOn(console, "error");
|
|
|
|
// We trigger an error by trying to use a path that is a file where a directory should be
|
|
const triggerFile = join(process.cwd(), "logs_fail_trigger");
|
|
|
|
try {
|
|
writeFileSync(triggerFile, "not a directory");
|
|
|
|
// Manually override paths for this test instance
|
|
const originalLogDir = (logger as any).logDir;
|
|
const originalLogPath = (logger as any).errorLogPath;
|
|
|
|
(logger as any).logDir = triggerFile;
|
|
(logger as any).errorLogPath = join(triggerFile, "error.log");
|
|
(logger as any).initialized = false;
|
|
|
|
logger.error("system", "This should fail directory creation");
|
|
|
|
// Wait for async initialization attempt
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
expect(consoleSpy.mock.calls.some(call =>
|
|
String(call[0]).includes("Failed to initialize logger directory")
|
|
)).toBe(true);
|
|
|
|
// Reset logger state
|
|
(logger as any).logDir = originalLogDir;
|
|
(logger as any).errorLogPath = originalLogPath;
|
|
(logger as any).initialized = false;
|
|
} finally {
|
|
if (existsSync(triggerFile)) unlinkSync(triggerFile);
|
|
consoleSpy.mockRestore();
|
|
}
|
|
});
|
|
|
|
test("should include complex data objects in logs", () => {
|
|
const spy = spyOn(console, "log");
|
|
const data = { userId: "123", tags: ["test"] };
|
|
logger.info("bot", "Message with data", data);
|
|
|
|
expect(spy).toHaveBeenCalled();
|
|
const callArgs = spy.mock.calls[0]?.[0];
|
|
expect(callArgs).toBeDefined();
|
|
if (callArgs) {
|
|
expect(callArgs).toContain(` | Data: ${JSON.stringify(data)}`);
|
|
}
|
|
spy.mockRestore();
|
|
});
|
|
|
|
test("should handle circular references in data objects", () => {
|
|
const spy = spyOn(console, "log");
|
|
const data: any = { name: "circular" };
|
|
data.self = data;
|
|
|
|
logger.info("bot", "Circular test", data);
|
|
|
|
expect(spy).toHaveBeenCalled();
|
|
const callArgs = spy.mock.calls[0]?.[0];
|
|
expect(callArgs).toContain("[Circular]");
|
|
spy.mockRestore();
|
|
});
|
|
});
|