diff --git a/js/nostr/maxListenerDiagnostics.js b/js/nostr/maxListenerDiagnostics.js index 365fa34f..76793fcf 100644 --- a/js/nostr/maxListenerDiagnostics.js +++ b/js/nostr/maxListenerDiagnostics.js @@ -15,7 +15,7 @@ const MAX_LISTENER_SNIPPETS = [ const MAX_LISTENER_CODES = new Set(["MaxListenersExceededWarning"]); -function collectCandidateStrings(value) { +export function collectCandidateStrings(value) { const candidates = []; if (!value) { @@ -40,7 +40,7 @@ function collectCandidateStrings(value) { return candidates; } -function shouldSuppressWarning(...args) { +export function shouldSuppressWarning(...args) { if (isVerboseDiagnosticsEnabled()) { return false; } diff --git a/tests/nostr/maxListenerDiagnostics.test.mjs b/tests/nostr/maxListenerDiagnostics.test.mjs new file mode 100644 index 00000000..83688fb6 --- /dev/null +++ b/tests/nostr/maxListenerDiagnostics.test.mjs @@ -0,0 +1,114 @@ +import assert from "node:assert/strict"; +import { test, describe, it, before, after, beforeEach } from "node:test"; +import { shouldSuppressWarning, collectCandidateStrings } from "../../js/nostr/maxListenerDiagnostics.js"; + +// Helper to control verbose mode mock +function setVerboseMode(enabled) { + if (!globalThis.window) { + globalThis.window = {}; + } + globalThis.window.__BITVID_VERBOSE_DEV_MODE__ = enabled; +} + +describe("maxListenerDiagnostics", () => { + let originalWindow; + + before(() => { + originalWindow = globalThis.window; + }); + + after(() => { + globalThis.window = originalWindow; + }); + + beforeEach(() => { + // Default to non-verbose for most tests + setVerboseMode(false); + }); + + describe("collectCandidateStrings", () => { + it("should return empty array for null/undefined/falsy", () => { + assert.deepEqual(collectCandidateStrings(null), []); + assert.deepEqual(collectCandidateStrings(undefined), []); + assert.deepEqual(collectCandidateStrings(false), []); + assert.deepEqual(collectCandidateStrings(0), []); + }); + + it("should return array with string for string input", () => { + assert.deepEqual(collectCandidateStrings("test warning"), ["test warning"]); + }); + + it("should extract relevant fields from object", () => { + const errorObj = { + name: "ErrorName", + message: "ErrorMessage", + code: "ErrorCode", + type: "ErrorType", + other: "OtherField" + }; + const result = collectCandidateStrings(errorObj); + assert.ok(result.includes("ErrorName")); + assert.ok(result.includes("ErrorMessage")); + assert.ok(result.includes("ErrorCode")); + assert.ok(result.includes("ErrorType")); + assert.ok(!result.includes("OtherField")); + assert.equal(result.length, 4); + }); + + it("should ignore non-string fields in object", () => { + const errorObj = { + message: 123, + code: null + }; + assert.deepEqual(collectCandidateStrings(errorObj), []); + }); + }); + + describe("shouldSuppressWarning", () => { + it("should NOT suppress anything if verbose mode is enabled", () => { + setVerboseMode(true); + assert.equal(shouldSuppressWarning("MaxListenersExceededWarning"), false); + assert.equal(shouldSuppressWarning({ code: "MaxListenersExceededWarning" }), false); + }); + + it("should suppress warning by code string", () => { + setVerboseMode(false); + assert.equal(shouldSuppressWarning("MaxListenersExceededWarning"), true); + }); + + it("should suppress warning by object code property", () => { + setVerboseMode(false); + assert.equal(shouldSuppressWarning({ code: "MaxListenersExceededWarning" }), true); + }); + + it("should suppress warning by message snippet", () => { + setVerboseMode(false); + assert.equal(shouldSuppressWarning("Possible EventEmitter memory leak detected. 11 listeners added."), true); + }); + + it("should suppress warning by object message property snippet", () => { + setVerboseMode(false); + assert.equal(shouldSuppressWarning({ message: "Possible EventEmitter memory leak detected" }), true); + }); + + it("should NOT suppress unrelated warnings", () => { + setVerboseMode(false); + assert.equal(shouldSuppressWarning("Some other warning"), false); + assert.equal(shouldSuppressWarning({ message: "Some other warning" }), false); + }); + + it("should handle multiple arguments", () => { + setVerboseMode(false); + // First arg unrelated, second arg matching + assert.equal(shouldSuppressWarning("unrelated", { code: "MaxListenersExceededWarning" }), true); + // Both unrelated + assert.equal(shouldSuppressWarning("unrelated", "also unrelated"), false); + }); + }); + + describe("process.emitWarning patch", () => { + it("should have patched process.emitWarning", () => { + assert.ok(process.emitWarning.__BITVID_MAX_LISTENER_PATCHED__, "process.emitWarning should be patched"); + }); + }); +});