const assert = require('node:assert/strict'); const { EventEmitter } = require('node:events'); const test = require('node:test'); const { WATCHDOG_CHANNEL, WATCHDOG_PATH, isAuthorizedWatchdogEvent, registerWatchdogHandler, sendWatchdogReset } = require('../js/watchdog.js'); function createHttpClient({ statusCode = 204, hang = false } = {}) { const calls = []; const httpClient = { request(options, onResponse) { const request = new EventEmitter(); request.destroy = (error) => request.emit('error', error); request.end = (body) => { calls.push({ options, body }); if (!hang) { queueMicrotask(() => onResponse({ statusCode, resume() {} })); } }; return request; } }; return { httpClient, calls }; } test('sends healthy true as a fixed local POST', async () => { const { httpClient, calls } = createHttpClient(); await sendWatchdogReset({ healthy: true }, httpClient); assert.deepEqual(calls, [{ options: { hostname: 'localhost', path: WATCHDOG_PATH, method: 'POST', headers: { 'Content-Type': 'application/json' } }, body: '{"healthy":true}' }]); }); test('sends healthy false in the request body', async () => { const { httpClient, calls } = createHttpClient(); await sendWatchdogReset({ healthy: false }, httpClient); assert.equal(calls[0].body, '{"healthy":false}'); }); test('rejects malformed payloads without making a request', async () => { const { httpClient, calls } = createHttpClient(); for (const payload of [null, [], {}, { healthy: 1 }, { healthy: true, url: 'http://example.test' }]) { await assert.rejects(sendWatchdogReset(payload, httpClient), /boolean healthy field/); } assert.equal(calls.length, 0); }); test('rejects non-success HTTP responses without parsing their body', async () => { const { httpClient } = createHttpClient({ statusCode: 503 }); await assert.rejects(sendWatchdogReset({ healthy: true }, httpClient), /HTTP 503/); }); test('rejects responses without a numeric HTTP status', async () => { const { httpClient } = createHttpClient({ statusCode: null }); await assert.rejects(sendWatchdogReset({ healthy: true }, httpClient), /HTTP unknown/); }); test('rejects requests that exceed the timeout', async () => { const { httpClient } = createHttpClient({ hang: true }); await assert.rejects(sendWatchdogReset({ healthy: true }, httpClient, 5), /timed out after 5 ms/); }); test('rejects unauthorized renderers without making a request', async () => { let registeredChannel; let handler; const { httpClient, calls } = createHttpClient(); const mainFrame = { url: 'https://allowed.example/' }; const mainWindow = { isDestroyed: () => false, webContents: { mainFrame } }; const ipcMain = { handle(channel, registeredHandler) { registeredChannel = channel; handler = registeredHandler; } }; registerWatchdogHandler(ipcMain, { isAuthorized: (event) => isAuthorizedWatchdogEvent(event, mainWindow, (url) => url === mainFrame.url), httpClient }); assert.equal(registeredChannel, WATCHDOG_CHANNEL); const otherRenderer = { sender: { mainFrame }, senderFrame: mainFrame }; await assert.rejects(handler(otherRenderer, { healthy: true }), /not authorized/); assert.equal(calls.length, 0); }); test('rejects subframes and renderers on disallowed URLs', () => { const mainFrame = { url: 'https://allowed.example/' }; const mainWindow = { isDestroyed: () => false, webContents: { mainFrame } }; const isUrlAllowed = (url) => url === mainFrame.url; const blockedFrame = { url: 'https://blocked.example/' }; const blockedWindow = { isDestroyed: () => false, webContents: { mainFrame: blockedFrame } }; assert.equal(isAuthorizedWatchdogEvent({ sender: mainWindow.webContents, senderFrame: { url: mainFrame.url } }, mainWindow, isUrlAllowed), false); assert.equal(isAuthorizedWatchdogEvent({ sender: blockedWindow.webContents, senderFrame: blockedFrame }, blockedWindow, isUrlAllowed), false); });