// Image-level compatibility test only: no mining, wallets, peers, or live-chain writes. import assert from 'node:assert/strict'; import { spawn, execFileSync } from 'node:child_process'; import { once } from 'node:events'; import { readFile, access } from 'node:fs/promises'; assert.equal(process.env.WIKIDECK_DATA_DIR, '/app/data'); assert.equal(process.env.WIKIDECK_PEERS, ''); assert.ok(process.env.RANDOMX_BINARY, 'Use the updated production image'); execFileSync(process.env.RANDOMX_BINARY, ['--self-test'], { timeout: 120_000, stdio: 'inherit', }); async function runNode() { const child = spawn(process.execPath, ['--experimental-strip-types', 'server/index.ts'], { cwd: '/app', env: process.env, stdio: ['ignore', 'pipe', 'pipe'], }); const exited = once(child, 'exit'); let output = ''; const timeout = setTimeout(() => child.kill('SIGKILL'), 120_000); try { await new Promise((resolve, reject) => { child.on('error', reject); child.once('exit', (code) => reject(new Error(`Node exited before ready: ${code}\n${output}`))); child.stderr.on('data', (chunk) => { output = (output + chunk).slice(-8192); }); child.stdout.on('data', (chunk) => { output = (output + chunk).slice(-8192); if (output.includes('Wikideck RandomX testnet:')) resolve(); }); }); for (const path of ['/healthz', '/p2p/status', '/api/peers']) { const response = await fetch(`http://127.0.0.1:3001${path}`, { signal: AbortSignal.timeout(10_000), }); assert.equal(response.status, 200, path); const body = await response.json(); if (path === '/healthz') { assert.equal(body.status, 'ok'); assert.equal(body.network, 'wikideck-randomx-testnet-2'); } else if (path === '/p2p/status') { assert.equal(body.network, 'wikideck-randomx-testnet-2'); assert.equal(body.size, 0); assert.match(body.chainId, /^[a-f0-9]{64}$/); } else assert.deepEqual(body.peers, []); } await access('/app/data/testnet-node.lock'); const ledger = JSON.parse(await readFile('/app/data/testnet/ledger.json', 'utf8')); assert.equal(ledger.network, 'wikideck-randomx-testnet-2'); assert.deepEqual(ledger.blocks, []); child.kill('SIGTERM'); assert.deepEqual(await exited, [0, null], output); await assert.rejects(access('/app/data/testnet-node.lock'), { code: 'ENOENT' }); } finally { clearTimeout(timeout); if (child.exitCode === null && child.signalCode === null) { child.kill('SIGKILL'); await exited; } } } await runNode(); await runNode(); console.log('PASS: native RandomX, node API, graceful lock cleanup and empty-ledger restart');