UNPKG

consortium

Version:

Remote control and session sharing CLI for AI coding agents

228 lines (209 loc) 6.06 kB
#!/usr/bin/env node /** * Standalone test for verify-platform.cjs. * * Runs the script in a child process with a temp CONSORTIUM_HOME_DIR and * asserts the sentinel file is created, exits 0, and contains a schema * that matches what Phase 2 will consume. * * Not a vitest test on purpose — the postinstall script must be runnable * before devDependencies (including vitest) have been installed, so the * sanity check must be runnable with plain `node`. * * Usage: node scripts/verify-platform.test.cjs */ 'use strict'; const fs = require('fs'); const os = require('os'); const path = require('path'); const { spawnSync } = require('child_process'); const assert = require('assert'); const SCRIPT = path.resolve(__dirname, 'verify-platform.cjs'); function makeTmpDir() { return fs.mkdtempSync(path.join(os.tmpdir(), 'csm-preflight-test-')); } function rmrf(dir) { try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) { /* ignore */ } } function runScript(env) { return spawnSync(process.execPath, [SCRIPT], { env: { ...process.env, ...env }, encoding: 'utf8', }); } function assertSentinelSchema(sentinel) { const requiredKeys = [ 'schemaVersion', 'platform', 'supported', 'musl', 'glibcVersion', 'binaryGlibcRequired', 'platformPackageName', 'platformPackageVersion', 'platformPackagePresent', 'warnings', 'errors', 'cliVersion', 'cliPackageName', 'generatedAt', ]; for (const k of requiredKeys) { assert.ok( Object.prototype.hasOwnProperty.call(sentinel, k), `sentinel missing key: ${k}` ); } assert.strictEqual(sentinel.schemaVersion, 1, 'schemaVersion must be 1'); assert.strictEqual( typeof sentinel.platform, 'string', 'platform must be a string' ); assert.match( sentinel.platform, /^[a-z0-9]+-[a-z0-9]+$/, `platform must be <os>-<arch>, got ${sentinel.platform}` ); assert.strictEqual( typeof sentinel.supported, 'boolean', 'supported must be bool' ); assert.strictEqual(typeof sentinel.musl, 'boolean', 'musl must be bool'); assert.ok(Array.isArray(sentinel.warnings), 'warnings must be an array'); assert.ok(Array.isArray(sentinel.errors), 'errors must be an array'); for (const arr of [sentinel.warnings, sentinel.errors]) { for (const item of arr) { assert.strictEqual( typeof item.code, 'string', 'entry.code must be a string' ); assert.strictEqual( typeof item.message, 'string', 'entry.message must be a string' ); if (item.remediation !== undefined) { assert.strictEqual( typeof item.remediation, 'string', 'entry.remediation must be a string' ); } } } assert.ok( typeof sentinel.generatedAt === 'string' && !Number.isNaN(Date.parse(sentinel.generatedAt)), 'generatedAt must be an ISO timestamp' ); assert.strictEqual( typeof sentinel.cliPackageName, 'string', 'cliPackageName must be a string' ); assert.strictEqual( typeof sentinel.cliVersion, 'string', 'cliVersion must be a string' ); } const tests = []; let passed = 0; let failed = 0; function test(name, fn) { tests.push({ name, fn }); } function run() { for (const { name, fn } of tests) { try { fn(); console.log(` ok - ${name}`); passed++; } catch (e) { console.log(` FAIL - ${name}`); console.log(` ${e.stack || e.message}`); failed++; } } console.log(`\n${passed} passed, ${failed} failed`); process.exit(failed === 0 ? 0 : 1); } // --- test cases ------------------------------------------------------- test('writes preflight.json to CONSORTIUM_HOME_DIR and exits 0', () => { const tmp = makeTmpDir(); try { const res = runScript({ CONSORTIUM_HOME_DIR: tmp }); assert.strictEqual( res.status, 0, `expected exit 0, got ${res.status}. stderr:\n${res.stderr}` ); const sentinelPath = path.join(tmp, 'preflight.json'); assert.ok( fs.existsSync(sentinelPath), `sentinel not found at ${sentinelPath}` ); const body = fs.readFileSync(sentinelPath, 'utf8'); const parsed = JSON.parse(body); assertSentinelSchema(parsed); } finally { rmrf(tmp); } }); test('creates CONSORTIUM_HOME_DIR recursively if missing', () => { const parent = makeTmpDir(); const target = path.join(parent, 'deep', 'nested', 'dir'); try { assert.ok(!fs.existsSync(target), 'precondition: target must not exist'); const res = runScript({ CONSORTIUM_HOME_DIR: target }); assert.strictEqual( res.status, 0, `expected exit 0, got ${res.status}. stderr:\n${res.stderr}` ); assert.ok(fs.existsSync(path.join(target, 'preflight.json'))); } finally { rmrf(parent); } }); test('writes stdout summary with "Run `consortium doctor` for details"', () => { const tmp = makeTmpDir(); try { const res = runScript({ CONSORTIUM_HOME_DIR: tmp }); assert.ok( /consortium doctor/.test(res.stdout), `expected doctor hint in stdout, got:\n${res.stdout}` ); } finally { rmrf(tmp); } }); test('exported parseGlibc / compareGlibc work', () => { const mod = require(SCRIPT); assert.deepStrictEqual(mod.parseGlibc('2.31'), [2, 31, 0]); assert.deepStrictEqual(mod.parseGlibc('2.34.1'), [2, 34, 1]); assert.strictEqual(mod.parseGlibc('garbage'), null); assert.ok(mod.compareGlibc('2.31', '2.34') < 0); assert.ok(mod.compareGlibc('2.34', '2.31') > 0); assert.strictEqual(mod.compareGlibc('2.34', '2.34'), 0); }); test('platform field uses <process.platform>-<process.arch> format', () => { const tmp = makeTmpDir(); try { runScript({ CONSORTIUM_HOME_DIR: tmp }); const parsed = JSON.parse( fs.readFileSync(path.join(tmp, 'preflight.json'), 'utf8') ); assert.strictEqual(parsed.platform, `${process.platform}-${process.arch}`); } finally { rmrf(tmp); } }); run();