diff --git a/tests/nostr/countDiagnostics.test.mjs b/tests/nostr/countDiagnostics.test.mjs new file mode 100644 index 00000000..644ea02f --- /dev/null +++ b/tests/nostr/countDiagnostics.test.mjs @@ -0,0 +1,182 @@ +import { describe, it, before, after, afterEach, beforeEach } from "node:test"; +import assert from "node:assert/strict"; +import "../test-setup.mjs"; + +describe("countDiagnostics", () => { + let countDiagnostics; + let warnCalls = []; + let originalConsoleWarn; + + before(async () => { + // Setup environment before importing modules + globalThis.__BITVID_DEV_MODE_OVERRIDE__ = true; + globalThis.__BITVID_VERBOSE_DEV_MODE_OVERRIDE__ = true; + + // Mock console.warn + originalConsoleWarn = console.warn; + console.warn = (...args) => { + warnCalls.push(args); + // originalConsoleWarn(...args); // Uncomment to see logs + }; + + // Dynamic import to pick up the overrides + countDiagnostics = await import("../../js/nostr/countDiagnostics.js"); + }); + + after(() => { + if (originalConsoleWarn) { + console.warn = originalConsoleWarn; + } + }); + + afterEach(() => { + warnCalls = []; + // Reset window flags between tests if needed, though most tests set them explicitly + if (typeof window !== "undefined") { + delete window.__BITVID_VERBOSE_DEV_MODE__; + } + }); + + describe("isVerboseDiagnosticsEnabled", () => { + it("should return true by default (due to override)", () => { + assert.equal(countDiagnostics.isVerboseDiagnosticsEnabled(), true); + }); + + it("should return false if window.__BITVID_VERBOSE_DEV_MODE__ is false", () => { + window.__BITVID_VERBOSE_DEV_MODE__ = false; + assert.equal(countDiagnostics.isVerboseDiagnosticsEnabled(), false); + }); + + it("should return true if window.__BITVID_VERBOSE_DEV_MODE__ is true", () => { + window.__BITVID_VERBOSE_DEV_MODE__ = true; + assert.equal(countDiagnostics.isVerboseDiagnosticsEnabled(), true); + }); + + it("should fall back to isVerboseDevMode if window flag is not boolean", () => { + window.__BITVID_VERBOSE_DEV_MODE__ = "invalid"; + assert.equal(countDiagnostics.isVerboseDiagnosticsEnabled(), true); + + window.__BITVID_VERBOSE_DEV_MODE__ = 123; + assert.equal(countDiagnostics.isVerboseDiagnosticsEnabled(), true); + }); + }); + + describe("logRelayCountFailure", () => { + it("should log warning for new relay URL", () => { + const relayUrl = "wss://relay.example.com"; + const error = new Error("Connection failed"); + + countDiagnostics.logRelayCountFailure(relayUrl, error); + + assert.equal(warnCalls.length, 1); + assert.match(warnCalls[0][0], /COUNT request failed on wss:\/\/relay.example.com/); + assert.deepEqual(warnCalls[0][1], error); + }); + + it("should suppress 'Failed to connect to relay' errors", () => { + const relayUrl = "wss://relay.bad.com"; + const error = new Error("Failed to connect to relay wss://relay.bad.com"); + + countDiagnostics.logRelayCountFailure(relayUrl, error); + + assert.equal(warnCalls.length, 0); + }); + + it("should throttle duplicate warnings for the same relay", () => { + const relayUrl = "wss://relay.throttled.com"; + const error = new Error("Timeout"); + + // First call logs + countDiagnostics.logRelayCountFailure(relayUrl, error); + assert.equal(warnCalls.length, 1); + + // Second call suppresses + countDiagnostics.logRelayCountFailure(relayUrl, error); + assert.equal(warnCalls.length, 1); + }); + + it("should handle empty or non-string relay URLs", () => { + const error = new Error("Unknown error"); + + countDiagnostics.logRelayCountFailure(null, error); + assert.equal(warnCalls.length, 1); + assert.match(warnCalls[0][0], /COUNT request failed on \(unknown relay\)/); + + warnCalls = []; + // This call should be throttled because both null and "" map to "(unknown relay)" + countDiagnostics.logRelayCountFailure("", error); + assert.equal(warnCalls.length, 0); + }); + }); + + describe("Other Loggers", () => { + // These use fixed keys so we can only test them once for "logging" behavior per process/suite run + + it("should log timeout cleanup failure once", () => { + const error = new Error("Cleanup failed"); + countDiagnostics.logCountTimeoutCleanupFailure(error); + + assert.equal(warnCalls.length, 1); + assert.match(warnCalls[0][0], /COUNT timeout cleanup failed/); + + // Verify throttling immediately + countDiagnostics.logCountTimeoutCleanupFailure(error); + assert.equal(warnCalls.length, 1); + }); + + it("should log rebroadcast failure once", () => { + const error = new Error("Rebroadcast failed"); + countDiagnostics.logRebroadcastCountFailure(error); + + assert.equal(warnCalls.length, 1); + assert.match(warnCalls[0][0], /COUNT request for rebroadcast failed/); + + // Verify throttling + countDiagnostics.logRebroadcastCountFailure(error); + assert.equal(warnCalls.length, 1); + }); + + it("should log view count failure once", () => { + const error = new Error("View count failed"); + countDiagnostics.logViewCountFailure(error); + + assert.equal(warnCalls.length, 1); + assert.match(warnCalls[0][0], /COUNT view request failed/); + + // Verify throttling + countDiagnostics.logViewCountFailure(error); + assert.equal(warnCalls.length, 1); + }); + }); + + describe("Verbose Mode Disabled", () => { + beforeEach(() => { + window.__BITVID_VERBOSE_DEV_MODE__ = false; + }); + + it("should not log even for new keys when disabled", () => { + const relayUrl = "wss://relay.silent.com"; + const error = new Error("Silent error"); + + countDiagnostics.logRelayCountFailure(relayUrl, error); + assert.equal(warnCalls.length, 0); + }); + + it("should not consume throttle key when disabled", () => { + const relayUrl = "wss://relay.delayed.com"; + const error = new Error("Delayed error"); + + // Disabled: no log, key not added + countDiagnostics.logRelayCountFailure(relayUrl, error); + assert.equal(warnCalls.length, 0); + + // Enable verbose mode + window.__BITVID_VERBOSE_DEV_MODE__ = true; + + // Should log now because previous call didn't register key + countDiagnostics.logRelayCountFailure(relayUrl, error); + assert.equal(warnCalls.length, 1); + assert.match(warnCalls[0][0], /COUNT request failed on wss:\/\/relay.delayed.com/); + }); + }); +});