import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { enforceElectronRuntimeGuard, SUPPORTED_ELECTRON_MAJOR } from './electron-runtime-guard'; function withTempDir(run: (directory: string) => void): void { const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-electron-guard-')); try { run(directory); } finally { fs.rmSync(directory, { recursive: true, force: true }); } } test('runtime guard major matches the pinned Electron dependency', () => { // A stale constant would make the guard reject the bundled runtime after an Electron bump. const packageJson = JSON.parse( fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'), ) as { devDependencies: { electron: string } }; const pinnedMajor = Number.parseInt(packageJson.devDependencies.electron.replace(/^\D*/, ''), 10); assert.equal(SUPPORTED_ELECTRON_MAJOR, pinnedMajor); }); test('runtime guard records the supported Electron major', () => { withTempDir((userDataPath) => { const result = enforceElectronRuntimeGuard({ electronVersion: '43.4.1', userDataPath, supportedElectronMajor: 43, }); assert.equal(result.ok, true); assert.deepEqual(JSON.parse(fs.readFileSync(result.statePath, 'utf8')), { highestElectronMajor: 43, lastElectronVersion: '43.4.1', }); }); }); test('runtime guard rejects a runtime outside the build major without writing state', () => { withTempDir((userDataPath) => { const result = enforceElectronRuntimeGuard({ electronVersion: '44.0.0', userDataPath, supportedElectronMajor: 43, }); assert.equal(result.ok, false); if (result.ok) return; assert.equal(result.title, 'Unsupported Electron runtime'); assert.match(result.details, /requires Electron 43/); assert.equal(fs.existsSync(result.statePath), false); }); }); test('runtime guard rejects prerelease Electron versions without writing state', () => { withTempDir((userDataPath) => { const result = enforceElectronRuntimeGuard({ electronVersion: '43.4.1-beta.1', userDataPath, supportedElectronMajor: 43, }); assert.equal(result.ok, false); if (result.ok) return; assert.equal(result.title, 'SubMiner could not verify Electron'); assert.equal(fs.existsSync(result.statePath), false); }); }); test('runtime guard blocks a profile downgrade before rewriting its safety record', () => { withTempDir((userDataPath) => { const statePath = path.join(userDataPath, 'electron-runtime.json'); fs.writeFileSync( statePath, JSON.stringify({ highestElectronMajor: 44, lastElectronVersion: '44.1.0' }), 'utf8', ); const result = enforceElectronRuntimeGuard({ electronVersion: '43.4.1', userDataPath, supportedElectronMajor: 43, }); assert.equal(result.ok, false); if (result.ok) return; assert.equal(result.title, 'Electron downgrade blocked'); assert.match(result.details, /destroy Yomitan dictionaries/); assert.equal(result.details.includes(statePath), true); assert.deepEqual(JSON.parse(fs.readFileSync(statePath, 'utf8')), { highestElectronMajor: 44, lastElectronVersion: '44.1.0', }); }); }); test('runtime guard blocks a minor downgrade even when the patch is higher', () => { withTempDir((userDataPath) => { const statePath = path.join(userDataPath, 'electron-runtime.json'); fs.writeFileSync( statePath, JSON.stringify({ highestElectronMajor: 43, lastElectronVersion: '43.4.1' }), 'utf8', ); const result = enforceElectronRuntimeGuard({ electronVersion: '43.3.99', userDataPath, supportedElectronMajor: 43, }); assert.equal(result.ok, false); if (result.ok) return; assert.equal(result.title, 'Electron downgrade blocked'); assert.equal(result.details.includes(statePath), true); assert.deepEqual(JSON.parse(fs.readFileSync(statePath, 'utf8')), { highestElectronMajor: 43, lastElectronVersion: '43.4.1', }); }); }); test('runtime guard allows a patch downgrade while preserving minor downgrade protection', () => { withTempDir((userDataPath) => { const statePath = path.join(userDataPath, 'electron-runtime.json'); fs.writeFileSync( statePath, JSON.stringify({ highestElectronMajor: 43, lastElectronVersion: '43.7.3' }), 'utf8', ); const result = enforceElectronRuntimeGuard({ electronVersion: '43.7.2', userDataPath, }); assert.equal(result.ok, true); const updatedState = fs.readFileSync(statePath, 'utf8'); assert.deepEqual(JSON.parse(updatedState), { highestElectronMajor: 43, lastElectronVersion: '43.7.2', }); const minorDowngrade = enforceElectronRuntimeGuard({ electronVersion: '43.6.99', userDataPath, }); assert.equal(minorDowngrade.ok, false); if (minorDowngrade.ok) return; assert.equal(minorDowngrade.title, 'Electron downgrade blocked'); assert.equal(fs.readFileSync(statePath, 'utf8'), updatedState); }); }); test('runtime guard fails closed when its safety record is malformed', () => { withTempDir((userDataPath) => { const statePath = path.join(userDataPath, 'electron-runtime.json'); fs.writeFileSync(statePath, '{}', 'utf8'); const result = enforceElectronRuntimeGuard({ electronVersion: '43.4.1', userDataPath, supportedElectronMajor: 43, }); assert.equal(result.ok, false); if (result.ok) return; assert.equal(result.title, 'SubMiner profile safety check failed'); assert.match(result.details, /invalid format/); }); });