import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import { EventEmitter } from 'node:events'; import path from 'node:path'; import vm from 'node:vm'; import { transform } from 'esbuild'; // Compile the real producer; Electron, disk, timers and renderer are isolated mocks. // No GUI, application profile or repository runtime files are touched. const source = await readFile(new URL('../../src/main/dev-ops-health.ts', import.meta.url), 'utf8'); const compiled = (await transform(source, { loader: 'ts', format: 'cjs', target: 'node20' })).code; const runId = '12345678-1234-1234-1234-123456789abc'; const flush = async () => { for (let i = 0; i < 8; i++) await new Promise(resolve => setImmediate(resolve)); }; function fixture(options = {}) { const files = new Map(), writes = [], renames = [], intervals = new Set(); const directory = path.join(path.resolve('/fixture/ycode'), '.tmp', 'ops'); const destination = path.join(directory, 'renderer-health.json'); const window = new EventEmitter(), contents = new EventEmitter(); let sequence = 0, releaseWrite, hold = !!options.holdFirstWrite; const dom = { complete: true, shell: true, button: true, width: 800, height: 600, display: 'block', visibility: 'visible' }; const fs = { realpath: async value => options.linkedDirectory ? `${value}-elsewhere` : value, writeFile: async (file, data, flags) => { writes.push({ file, data: JSON.parse(data), flags }); if (hold) { hold = false; await new Promise(resolve => { releaseWrite = resolve; }); } assert.equal(flags.flag, 'wx'); assert.equal(flags.mode, 0o600); assert.equal(files.has(file), false); files.set(file, data); }, rename: async (from, to) => { assert.equal(files.has(from), true); files.set(to, files.get(from)); files.delete(from); renames.push(JSON.parse(files.get(to))); }, rm: async file => { files.delete(file); } }; Object.assign(contents, { isDestroyed: () => false, getOSProcessId: () => 5678, isLoadingMainFrame: () => !!options.loading, executeJavaScript: async code => { if (options.rejectProbe) throw new Error('renderer unavailable'); const shell = { querySelector: () => dom.button ? {} : null, getBoundingClientRect: () => ({ width: dom.width, height: dom.height }) }; return vm.runInNewContext(code, { document: { readyState: dom.complete ? 'complete' : 'loading', querySelector: () => dom.shell ? shell : null }, getComputedStyle: () => ({ display: dom.display, visibility: dom.visibility }) }); } }); Object.assign(window, { webContents: contents, isDestroyed: () => false }); const module = { exports: {} }; vm.runInNewContext(compiled, { module, exports: module.exports, require: name => { if (name === 'electron') return { app: { isPackaged: !!options.packaged } }; if (name === 'node:fs') return { promises: fs }; if (name === 'node:path') return path; if (name === 'node:crypto') return { randomUUID: () => `mock-${++sequence}` }; throw new Error(`Unexpected dependency: ${name}`); }, process: { env: { YCODE_OPS_RUN_ID: options.id === undefined ? runId : options.id }, cwd: () => '/fixture/ycode', pid: 1234 }, setInterval: callback => { const timer = { callback, unref() {} }; intervals.add(timer); return timer; }, clearInterval: timer => intervals.delete(timer), Date }); return { window, contents, dom, files, writes, renames, intervals, destination, attach: () => module.exports.attachDevOpsHealth(window), tick: () => { for (const timer of intervals) timer.callback(); }, release: () => { assert.ok(releaseWrite, 'expected delayed write'); releaseWrite(); }, proof: () => files.has(destination) ? JSON.parse(files.get(destination)) : null }; } test('producer opt-in gates packaged, missing and invalid run IDs without timers or writes', async () => { for (const options of [{ packaged: true }, { id: '' }, { id: 'not-a-uuid' }]) { const f = fixture(options); f.attach(); await flush(); assert.equal(f.writes.length, 0); assert.equal(f.intervals.size, 0); } }); test('producer writes fresh run and native PID proof only for a complete visible shell with button', async () => { const f = fixture(); f.attach(); await flush(); const proof = f.proof(); assert.equal(proof.ready, true); assert.equal(proof.runId, runId); assert.equal(proof.pid, 1234); assert.equal(proof.rendererPid, 5678); assert.ok(Math.abs(Date.now() - proof.checkedAt) < 5000); assert.equal(f.files.size, 1); assert.equal(f.files.has(f.destination), true); for (const [key, value] of [['complete', false], ['shell', false], ['button', false], ['width', 0], ['height', 0], ['display', 'none'], ['visibility', 'hidden']]) { const prior = f.dom[key]; f.dom[key] = value; f.tick(); await flush(); assert.equal(f.proof().ready, false, `${key} must fail readiness`); f.dom[key] = prior; } f.tick(); await flush(); assert.equal(f.proof().ready, true); }); test('loading, rejected renderer and linked output directory cannot produce ready proof', async () => { for (const options of [{ loading: true }, { rejectProbe: true }, { linkedDirectory: true }]) { const f = fixture(options); f.attach(); await flush(); assert.notEqual(f.proof()?.ready, true); if (options.linkedDirectory) assert.equal(f.writes.length, 0); } }); test('invalidation while a true proof write is pending cannot leave a stale true result', async () => { const f = fixture({ holdFirstWrite: true }); f.attach(); await flush(); assert.equal(f.writes[0].data.ready, true); f.contents.emit('did-start-loading'); await flush(); f.release(); await flush(); assert.equal(f.proof().ready, false); // Once invalidation is published, no older queued true may replace it. const firstFalse = f.renames.findIndex(proof => proof.ready === false); assert.ok(firstFalse >= 0); assert.ok(f.renames.slice(firstFalse).every(proof => proof.ready === false)); }); test('unresponsive and closed windows invalidate proof; close cancels periodic probes', async () => { const f = fixture(); f.attach(); await flush(); f.window.emit('unresponsive'); await flush(); assert.equal(f.proof().ready, false); f.tick(); await flush(); assert.equal(f.proof().ready, false); f.window.emit('responsive'); await flush(); assert.equal(f.proof().ready, true); f.window.emit('closed'); await flush(); assert.equal(f.proof().ready, false); assert.equal(f.intervals.size, 0); });