2024-03-09 02:25:17 +01:00
|
|
|
import {
|
2024-04-07 07:30:49 +02:00
|
|
|
type Mock,
|
|
|
|
|
beforeEach,
|
|
|
|
|
describe,
|
|
|
|
|
expect,
|
|
|
|
|
it,
|
|
|
|
|
jest,
|
|
|
|
|
mock,
|
|
|
|
|
test,
|
2024-03-09 02:25:17 +01:00
|
|
|
} from "bun:test";
|
2024-04-07 07:30:49 +02:00
|
|
|
import type fs from "node:fs/promises";
|
2024-03-09 02:25:17 +01:00
|
|
|
import type { BunFile } from "bun";
|
2024-04-07 07:30:49 +02:00
|
|
|
// FILEPATH: /home/jessew/Dev/lysand/packages/log-manager/log-manager.test.ts
|
|
|
|
|
import { LogLevel, LogManager, MultiLogManager } from "../index";
|
2024-03-09 02:25:17 +01:00
|
|
|
|
|
|
|
|
describe("LogManager", () => {
|
2024-04-07 07:30:49 +02:00
|
|
|
let logManager: LogManager;
|
|
|
|
|
let mockOutput: BunFile;
|
|
|
|
|
let mockAppend: Mock<typeof fs.appendFile>;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
mockOutput = Bun.file("test.log");
|
|
|
|
|
mockAppend = jest.fn();
|
2024-04-07 12:18:21 +02:00
|
|
|
await mock.module("node:fs/promises", () => ({
|
2024-04-07 07:30:49 +02:00
|
|
|
appendFile: mockAppend,
|
|
|
|
|
}));
|
|
|
|
|
logManager = new LogManager(mockOutput);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should initialize and write init log", () => {
|
2024-04-07 12:18:21 +02:00
|
|
|
new LogManager(mockOutput);
|
2024-04-07 07:30:49 +02:00
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining("--- INIT LogManager at"),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log message with timestamp", async () => {
|
|
|
|
|
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
|
|
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining("[INFO] TestEntity: Test message"),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log message without timestamp", async () => {
|
|
|
|
|
await logManager.log(
|
|
|
|
|
LogLevel.INFO,
|
|
|
|
|
"TestEntity",
|
|
|
|
|
"Test message",
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
"[INFO] TestEntity: Test message\n",
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test.skip("should write to stdout", async () => {
|
|
|
|
|
logManager = new LogManager(Bun.stdout);
|
|
|
|
|
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
|
|
|
|
|
|
|
|
|
|
const writeMock = jest.fn();
|
|
|
|
|
|
|
|
|
|
await mock.module("Bun", () => ({
|
|
|
|
|
stdout: Bun.stdout,
|
|
|
|
|
write: writeMock,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
expect(writeMock).toHaveBeenCalledWith(
|
|
|
|
|
Bun.stdout,
|
|
|
|
|
expect.stringContaining("[INFO] TestEntity: Test message"),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log error message", async () => {
|
|
|
|
|
const error = new Error("Test error");
|
|
|
|
|
await logManager.logError(LogLevel.ERROR, "TestEntity", error);
|
|
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining("[ERROR] TestEntity: Test error"),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log basic request details", async () => {
|
|
|
|
|
const req = new Request("http://localhost/test", { method: "GET" });
|
|
|
|
|
await logManager.logRequest(req, "127.0.0.1");
|
|
|
|
|
|
|
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining("127.0.0.1: GET http://localhost/test"),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("Request logger", () => {
|
|
|
|
|
it("should log all request details for JSON content type", async () => {
|
|
|
|
|
const req = new Request("http://localhost/test", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
|
body: JSON.stringify({ test: "value" }),
|
|
|
|
|
});
|
|
|
|
|
await logManager.logRequest(req, "127.0.0.1", true);
|
|
|
|
|
|
|
|
|
|
const expectedLog = `127.0.0.1: POST http://localhost/test
|
2024-03-11 03:04:14 +01:00
|
|
|
[Headers]
|
|
|
|
|
content-type: application/json
|
|
|
|
|
[Body]
|
|
|
|
|
{
|
|
|
|
|
"test": "value"
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2024-04-07 07:30:49 +02:00
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining(expectedLog),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log all request details for text content type", async () => {
|
|
|
|
|
const req = new Request("http://localhost/test", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: { "Content-Type": "text/plain" },
|
|
|
|
|
body: "Test body",
|
|
|
|
|
});
|
|
|
|
|
await logManager.logRequest(req, "127.0.0.1", true);
|
|
|
|
|
|
|
|
|
|
const expectedLog = `127.0.0.1: POST http://localhost/test
|
2024-03-11 03:04:14 +01:00
|
|
|
[Headers]
|
|
|
|
|
content-type: text/plain
|
|
|
|
|
[Body]
|
|
|
|
|
Test body
|
|
|
|
|
`;
|
2024-04-07 07:30:49 +02:00
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining(expectedLog),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log all request details for FormData content-type", async () => {
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append("test", "value");
|
|
|
|
|
const req = new Request("http://localhost/test", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
body: formData,
|
|
|
|
|
});
|
|
|
|
|
await logManager.logRequest(req, "127.0.0.1", true);
|
|
|
|
|
|
|
|
|
|
const expectedLog = `127.0.0.1: POST http://localhost/test
|
2024-03-11 03:04:14 +01:00
|
|
|
[Headers]
|
|
|
|
|
content-type: multipart/form-data; boundary=${
|
2024-04-07 07:30:49 +02:00
|
|
|
req.headers.get("Content-Type")?.split("boundary=")[1] ?? ""
|
|
|
|
|
}
|
2024-03-11 03:04:14 +01:00
|
|
|
[Body]
|
|
|
|
|
test: value
|
|
|
|
|
`;
|
|
|
|
|
|
2024-04-07 07:30:49 +02:00
|
|
|
expect(mockAppend).toHaveBeenCalledWith(
|
|
|
|
|
mockOutput.name,
|
|
|
|
|
expect.stringContaining(
|
|
|
|
|
expectedLog.replace("----", expect.any(String)),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
2024-03-11 03:04:14 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("MultiLogManager", () => {
|
2024-04-07 07:30:49 +02:00
|
|
|
let multiLogManager: MultiLogManager;
|
|
|
|
|
let mockLogManagers: LogManager[];
|
|
|
|
|
let mockLog: jest.Mock;
|
|
|
|
|
let mockLogError: jest.Mock;
|
|
|
|
|
let mockLogRequest: jest.Mock;
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
mockLog = jest.fn();
|
|
|
|
|
mockLogError = jest.fn();
|
|
|
|
|
mockLogRequest = jest.fn();
|
|
|
|
|
mockLogManagers = [
|
|
|
|
|
{
|
|
|
|
|
log: mockLog,
|
|
|
|
|
logError: mockLogError,
|
|
|
|
|
logRequest: mockLogRequest,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
log: mockLog,
|
|
|
|
|
logError: mockLogError,
|
|
|
|
|
logRequest: mockLogRequest,
|
|
|
|
|
},
|
|
|
|
|
] as unknown as LogManager[];
|
|
|
|
|
multiLogManager = MultiLogManager.fromLogManagers(...mockLogManagers);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log message to all logManagers", async () => {
|
|
|
|
|
await multiLogManager.log(LogLevel.INFO, "TestEntity", "Test message");
|
|
|
|
|
expect(mockLog).toHaveBeenCalledTimes(2);
|
|
|
|
|
expect(mockLog).toHaveBeenCalledWith(
|
|
|
|
|
LogLevel.INFO,
|
|
|
|
|
"TestEntity",
|
|
|
|
|
"Test message",
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log error to all logManagers", async () => {
|
|
|
|
|
const error = new Error("Test error");
|
|
|
|
|
await multiLogManager.logError(LogLevel.ERROR, "TestEntity", error);
|
|
|
|
|
expect(mockLogError).toHaveBeenCalledTimes(2);
|
|
|
|
|
expect(mockLogError).toHaveBeenCalledWith(
|
|
|
|
|
LogLevel.ERROR,
|
|
|
|
|
"TestEntity",
|
|
|
|
|
error,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("should log request to all logManagers", async () => {
|
|
|
|
|
const req = new Request("http://localhost/test", { method: "GET" });
|
|
|
|
|
await multiLogManager.logRequest(req, "127.0.0.1", true);
|
|
|
|
|
expect(mockLogRequest).toHaveBeenCalledTimes(2);
|
|
|
|
|
expect(mockLogRequest).toHaveBeenCalledWith(req, "127.0.0.1", true);
|
|
|
|
|
});
|
2024-03-09 02:25:17 +01:00
|
|
|
});
|