import fs from "node:fs"; import path from "node:path"; import { afterAll, expect, type Mock, test as baseTest, vi } from "vitest"; import { createTmpDir } from "../../../test/helpers/tmp-dir"; import { loggerFactory } from "./logger.server"; const tmpDir = createTmpDir(); afterAll(async () => { try { await fs.promises.rm(tmpDir, { recursive: true, force: true }); } catch (error) { // Ignore error console.error(error); } }); const logsDir = path.join(tmpDir, "logs"); // Start up the logger lib const Logger = loggerFactory({ file: { path: path.join(logsDir, "nodecg.log") }, }); const test = baseTest .extend<{ logger: InstanceType; SentryMock: { captureException: Mock }; }>({ logger: async ({}, use) => { const logger = new Logger("testServer"); await use(logger); }, SentryMock: async ({}, use) => { const SentryMock = { captureException: vi.fn(), }; await use(SentryMock); }, }) .extend<{ sentryLogger: InstanceType }>({ sentryLogger: async ({ SentryMock }, use) => { const SentryLogger = loggerFactory( { file: { path: path.join(logsDir, "sentry.log") } }, SentryMock as any, ); await use(new SentryLogger("sentryServer")); }, }); test("console - should default to being silent", () => { expect(Logger._consoleLogger.transports[0]!.silent).toBe(true); }); test('console - should default to level "info"', () => { expect(Logger._consoleLogger.transports[0]!.level).toBe("info"); }); test("file - should default to being silent", () => { expect(Logger._fileLogger.transports[0]!.silent).toBe(true); }); test('file - should default to level "info"', () => { expect(Logger._fileLogger.transports[0]!.level).toBe("info"); }); test("file - should make the logs folder", () => { expect(fs.existsSync(logsDir)).toBe(true); }); test("replicant - should default to false", () => { expect(Logger._shouldConsoleLogReplicants).toBe(false); expect(Logger._shouldFileLogReplicants).toBe(false); }); test("replicant - should do nothing when Logger._shouldLogReplicants is false", ({ logger, }) => { const consoleInfo = vi.spyOn(Logger._consoleLogger, "info"); logger.replicants("replicants"); expect(consoleInfo).not.toBeCalled(); consoleInfo.mockRestore(); const fileInfo = vi.spyOn(Logger._fileLogger, "info"); logger.replicants("replicants"); expect(fileInfo).not.toBeCalled(); fileInfo.mockRestore(); }); test("Sentry - should log errors to Sentry when global.sentryEnabled is true", ({ sentryLogger, SentryMock, }) => { sentryLogger.error("error message"); expect(SentryMock.captureException).toBeCalledTimes(1); expect(SentryMock.captureException.mock.calls[0]).toMatchInlineSnapshot(` [ [Error: [sentryServer] error message], ] `); }); test("Sentry - should prettyprint objects", ({ sentryLogger, SentryMock }) => { sentryLogger.error("error message:", { foo: { bar: "baz" } }); expect(SentryMock.captureException.mock.calls[0]).toMatchInlineSnapshot(` [ [Error: [sentryServer] error message: { foo: { bar: 'baz' } }], ] `); });