MeshChatX/tests/electron/safeConsole.test.js

131 lines
4.9 KiB
JavaScript

// SPDX-License-Identifier: 0BSD
import { createRequire } from "module";
import { beforeEach, describe, expect, it, vi } from "vitest";
const require = createRequire(import.meta.url);
const {
isBrokenPipeError,
installBrokenPipeGuards,
safeConsoleLog,
shouldMirrorStdout,
createMainProcessLogger,
ELECTRON_LOG_PREFIX,
} = require("../../electron/safeConsole");
describe("safeConsole", () => {
beforeEach(() => {
vi.restoreAllMocks();
});
it("detects EPIPE by code and message", () => {
expect(isBrokenPipeError({ code: "EPIPE" })).toBe(true);
expect(isBrokenPipeError({ errno: "EPIPE" })).toBe(true);
expect(isBrokenPipeError(new Error("write EPIPE"))).toBe(true);
expect(isBrokenPipeError(new Error("other"))).toBe(false);
expect(isBrokenPipeError(null)).toBe(false);
});
it("installBrokenPipeGuards ignores EPIPE stream errors", () => {
const handlers = [];
const stream = {
on(event, handler) {
handlers.push([event, handler]);
},
};
installBrokenPipeGuards({ stdout: stream, stderr: null });
expect(handlers).toHaveLength(1);
expect(handlers[0][0]).toBe("error");
expect(() => handlers[0][1]({ code: "EPIPE" })).not.toThrow();
installBrokenPipeGuards({ stdout: stream, stderr: null });
expect(handlers).toHaveLength(1);
});
it("safeConsoleLog swallows EPIPE from console.log", () => {
const spy = vi.spyOn(console, "log").mockImplementation(() => {
const err = new Error("write EPIPE");
err.code = "EPIPE";
throw err;
});
expect(() => safeConsoleLog("backend line")).not.toThrow();
expect(spy).toHaveBeenCalledWith("backend line");
});
it("safeConsoleLog rethrows non-EPIPE errors", () => {
vi.spyOn(console, "log").mockImplementation(() => {
throw new Error("boom");
});
expect(() => safeConsoleLog("x")).toThrow("boom");
});
it("shouldMirrorStdout is false without a TTY unless forced", () => {
expect(shouldMirrorStdout({ stdout: { isTTY: false } }, {})).toBe(false);
expect(shouldMirrorStdout({ stdout: { isTTY: true } }, {})).toBe(true);
expect(shouldMirrorStdout({ stdout: { isTTY: false } }, { MESHCHAT_FORCE_STDOUT_LOG: "1" })).toBe(true);
expect(shouldMirrorStdout({ stdout: { isTTY: true } }, { MESHCHAT_DISABLE_STDOUT_LOG: "1" })).toBe(false);
});
it("createMainProcessLogger appends durable lines and skips non-TTY stdout", () => {
const writes = [];
const mkdirCalls = [];
const logger = createMainProcessLogger({
getLogsDir: () => "/tmp/meshchat-logs",
mkdirSync: (dir, opts) => {
mkdirCalls.push([dir, opts]);
},
appendFileSync: (file, data) => {
writes.push([file, data]);
},
});
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
logger.write("backend ready", {
proc: { stdout: { isTTY: false } },
env: {},
});
expect(consoleSpy).not.toHaveBeenCalled();
expect(mkdirCalls.length).toBeGreaterThan(0);
expect(writes).toHaveLength(1);
expect(writes[0][0]).toBe("/tmp/meshchat-logs/meshchatx.log");
expect(writes[0][1]).toContain(ELECTRON_LOG_PREFIX);
expect(writes[0][1]).toContain("backend ready");
});
it("createMainProcessLogger mirrors to stdout when TTY is present", () => {
const logger = createMainProcessLogger({
getLogsDir: () => "/tmp/meshchat-logs",
mkdirSync: () => {},
appendFileSync: () => {},
});
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
logger.write("tty line", {
proc: { stdout: { isTTY: true } },
env: {},
});
expect(consoleSpy).toHaveBeenCalledWith("tty line");
});
it("createMainProcessLogger buffers until logs dir is available", () => {
let logsDir = null;
const writes = [];
const logger = createMainProcessLogger({
getLogsDir: () => logsDir,
mkdirSync: () => {},
appendFileSync: (file, data) => {
writes.push([file, data]);
},
});
logger.write("early", { mirrorStdout: false });
expect(writes).toHaveLength(0);
expect(logger._pendingForTests.length).toBe(1);
logsDir = "/tmp/meshchat-logs";
logger.write("later", { mirrorStdout: false });
expect(writes.length).toBeGreaterThanOrEqual(1);
const joined = writes.map((entry) => entry[1]).join("");
expect(joined).toContain("early");
expect(joined).toContain("later");
expect(logger._pendingForTests.length).toBe(0);
});
});