Files
MikrotikManager/backend/scripts/vrf-production-profile.mjs
T

147 lines
13 KiB
JavaScript

import assert from 'node:assert/strict'
import { mkdir, writeFile, realpath, lstat, mkdtemp, open } from 'node:fs/promises'
import { basename, join, resolve, relative, isAbsolute } from 'node:path'
import { createRequire } from 'node:module'
import { pathToFileURL, fileURLToPath } from 'node:url'
import { randomBytes } from 'node:crypto'
import { spawn } from 'node:child_process'
const diagnosticLogCap = 1024 * 1024
const diagnosticNames = new Set(['Error', 'AssertionError', 'McpError', 'TypeError', 'RangeError'])
const diagnosticCodes = new Set(['ERR_ASSERTION', 'VERIFICATION_FAILED', 'DEVICE_UNAVAILABLE', 'PLAN_EXPIRED', 'RATE_LIMITED', 'VRF_ACK_JOURNAL_UNCONFIRMED', 'VRF_VERIFIED_CHECKPOINT_UNCONFIRMED', '42883', '22P02'])
/** Pure projection only: no arbitrary log text, SQL, native rows or credentials. */
export function projectVrfDiagnostics(log, privateValues = [], expectedSlice = null) {
assert.equal(typeof log, 'string'); assert(Array.isArray(privateValues))
assert(Buffer.byteLength(log) <= diagnosticLogCap, 'Diagnostic source log exceeded existing cap')
let redacted = log
for (const value of [...privateValues, 'owned-vrf-loopback-password'].filter(value => typeof value === 'string' && value).sort((a,b) => b.length-a.length)) redacted = redacted.split(value).join('[REDACTED]')
redacted = redacted.replace(/postgres(?:ql)?:\/\/[^\s'"]+/gi, '[REDACTED_DATABASE_URL]')
.replace(/mmcp_[a-z]+_[A-Za-z0-9_-]+/g, '[REDACTED_MCP_TOKEN]')
.replace(/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, '[REDACTED_JWT]')
const records = [], acceptedCaseSequence = []; let malformedLines = 0, omittedLines = 0, incrementalRecords = 0, truncatedRecords = 0, droppedCaseLabels = 0, lastAcceptedCase = null, finalComponentPresent = false
const labelPattern = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,159}$/
const retainLabel = label => { if (typeof label !== 'string' || !labelPattern.test(label)) { droppedCaseLabels++; return }; if (acceptedCaseSequence.length === 128) { acceptedCaseSequence.shift(); droppedCaseLabels++ }; acceptedCaseSequence.push(label); lastAcceptedCase = label }
const retain = value => { if (records.length === 128) { records.shift(); truncatedRecords++ } records.push(value) }
for (const line of redacted.split(/\r?\n/).filter(Boolean)) {
let value; try { value = JSON.parse(line) } catch {
malformedLines++
const name = /^(Error|AssertionError|McpError|TypeError|RangeError)(?:[ :\[]|$)/.exec(line)?.[1]
if (name) retain({ diagnosticError: true, safeName: name, safeCode: line.includes('[ERR_ASSERTION]') ? 'ERR_ASSERTION' : 'other' })
const normalized = line.replaceAll('\\', '/'), position = /vrf-production-execution-owned\.(?:ts|mts):(\d{1,7}):(\d{1,7})\)?$/.exec(normalized)
if (position && (normalized.includes('/backend/src/test/vrf-production-execution-owned.ts:') || normalized.includes('/tmp/mcp-vrf-all68-staged-20261101/vrf-production-execution-owned.mts:'))) retain({ source: 'owned-vrf-fixture', line: Number(position[1]), column: Number(position[2]) })
continue
}
if (!value || typeof value !== 'object' || Array.isArray(value)) { omittedLines++; continue }
const row = {}
if (value.acceptedCase) { incrementalRecords++; row.acceptedCaseRecord = true; const labels = Array.isArray(value.acceptedCase) ? value.acceptedCase : [value.acceptedCase]; if (labels.length <= 68) for (const label of labels) retainLabel(label); else droppedCaseLabels += labels.length }
if (Array.isArray(value.accepted)) { row.finalCaseCount = value.accepted.length; if (expectedSlice !== null && value.slice === expectedSlice) finalComponentPresent = true }
if (['planned','queued','running','applied','failed','cancelled','unknown','expired','rejected','unconfirmed'].includes(value.operationState)) row.operationState = value.operationState
for (const key of ['phase', 'elapsedMs', 'nativeReads', 'nativeWrites', 'roundTrips', 'lastSqlMs', 'totalSqlMs', 'maxSqlMs']) if (Number.isSafeInteger(value[key]) && value[key] >= 0) row[key] = value[key]
if (value.diagnosticWaitSql === true) row.diagnosticWaitSql = true
if (value.diagnosticError === true) { row.diagnosticError = true; row.safeName = diagnosticNames.has(value.safeName) ? value.safeName : 'other'; row.safeCode = diagnosticCodes.has(value.safeCode) ? value.safeCode : 'other' }
if (!Object.keys(row).length) { omittedLines++; continue }
retain(row)
}
return { version: 1, rawLogRetained: false, sourceCapBytes: diagnosticLogCap, capturedLogBytes: Buffer.byteLength(log), malformedLines, omittedLines, incrementalRecords, truncatedRecords, finalComponentPresent, acceptedCaseSequence, lastAcceptedCase, droppedCaseLabels, records }
}
export function vrfSliceDiagnostic(result, log, childBudgetMs, privateValues = []) {
assert(result && typeof result === 'object')
assert(['baseline','physical','vlan','disabled','mixed','reconcile','sql-journal','guards','coexistence','alias'].includes(result.slice))
assert(Number.isSafeInteger(result.code) && typeof result.childExitConfirmed === 'boolean')
assert(Number.isSafeInteger(result.caseCount) && result.caseCount >= 0)
assert.equal(childBudgetMs, result.slice === 'baseline' ? 240000 : 120000)
assert(result.failure === null || ['child-deadline','child-log-limit','child-signal','child-error','child-error-after-pid','child-exit-unconfirmed','missing-component-receipt'].includes(result.failure))
assert(result.accepted === (result.code === 0 && result.failure === null && result.childExitConfirmed))
return { ...result, capMs: childBudgetMs, diagnostics: projectVrfDiagnostics(log, privateValues, result.slice), scope: 'same-owned-PG-test-only', actualRouter: false, forwardingVerified: false }
}
async function writeDiagnostic(path, value, maxBytes = 65536) {
const bytes = Buffer.from(JSON.stringify(value, null, 2) + '\n'); assert(bytes.length <= maxBytes)
const stream = await open(path, 'wx', 0o600)
try { await stream.writeFile(bytes); await stream.sync() } finally { await stream.close() }
}
/** Existing test runner owns the PG lifecycle. This component creates only
* bounded Node children on that same synthetic DB, with an empty private cwd.
* It never reads backend/.env, creates a PG, or accepts an application URL. */
export async function runOwnedVrfProductionProfile({ directory, database, databaseUrl }) {
const target = new URL(databaseUrl)
assert.equal(target.hostname, '127.0.0.1'); assert.equal(target.username, 'mcp_test')
assert(/^mm_accept_166_\d+$/.test(database)); assert.equal(target.pathname, '/' + database)
const owned = resolve(directory)
assert.equal(await realpath(owned), owned); assert(basename(owned).startsWith('mm-mcp-test-'))
assert(!(await lstat(owned)).isSymbolicLink())
// Fixed destination derived from this source module, never caller/env controlled.
const repository = fileURLToPath(new URL('../../', import.meta.url)), reportRoot = join(repository, 'tmp')
assert.equal(await realpath(reportRoot), reportRoot); assert(!(await lstat(reportRoot)).isSymbolicLink())
const diagnosticDirectory = await mkdtemp(join(reportRoot, 'mcp-vrf-profile-'))
assert.equal(await realpath(diagnosticDirectory), diagnosticDirectory); assert(!(await lstat(diagnosticDirectory)).isSymbolicLink())
assert.equal(relative(reportRoot, diagnosticDirectory), basename(diagnosticDirectory)); assert(basename(diagnosticDirectory).startsWith('mcp-vrf-profile-'))
const persist = (name, value) => writeDiagnostic(join(diagnosticDirectory, name), value)
const diagnosticPath = relative(repository, diagnosticDirectory)
await persist('started.json', { version: 1, status: 'running', accepted: false, childExitConfirmed: false, scope: 'same-owned-PG-test-only' })
console.log(JSON.stringify({ profile: 'test:vrf:production', diagnosticDirectory: diagnosticPath, status: 'running' }))
const results = []
let currentSlice = null, currentChildExitConfirmed = false, lastSliceResult = null
try {
const entry = fileURLToPath(new URL('../src/test/vrf-production-execution-owned.ts', import.meta.url))
const report = fileURLToPath(new URL('../../docs/mcp-chr-vrf-companion-native-v5.json', import.meta.url))
const loader = pathToFileURL(createRequire(import.meta.url).resolve('tsx')).href
const safe = Object.fromEntries(['SystemRoot', 'WINDIR', 'TEMP', 'TMP', 'LANG', 'LC_ALL'].filter(key => typeof process.env[key] === 'string').map(key => [key, process.env[key]]))
for (const slice of ['baseline', 'physical', 'vlan', 'disabled', 'mixed', 'reconcile', 'sql-journal', 'guards', 'coexistence', 'alias']) {
currentSlice = slice; currentChildExitConfirmed = false; lastSliceResult = null
await persist(slice + '-started.json', { version: 1, slice, status: 'running', accepted: false, childExitConfirmed: false })
const childBudgetMs = slice === 'baseline' ? 240000 : 120000
const cwd = join(owned, 'vrf-production-' + slice), rel = relative(owned, cwd)
assert(rel && !rel.startsWith('..') && !isAbsolute(rel)); await mkdir(cwd)
assert.equal(await realpath(cwd), cwd); assert(!(await lstat(cwd)).isSymbolicLink())
let log = '', failure = null, exited = false, stopTimer, unknownTimer
const authSecret = randomBytes(32).toString('hex')
const child = spawn(process.execPath, ['--import', loader, entry], { cwd, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'], env: {
...safe, NODE_OPTIONS: '', NODE_ENV: 'test', DATABASE_URL: databaseUrl,
DATABASE_PATH: join(cwd, 'no-legacy.sqlite'), MCP_STATE_DIR: join(cwd, 'state'),
AUTH_REQUIRED: 'true', AUTH_JWT_SECRET: authSecret, MCP_PUBLIC_URL: 'http://localhost:8000/mcp',
MMAPP_OWN_VRF_EXECUTION: 'true', MMAPP_VRF_EXECUTION_SLICE: slice, MMAPP_VRF_NATIVE_REPORT: report,
MMAPP_VRF_SHARED_OWNED_PG: 'true', MMAPP_OWN_SQL_DB: database,
} })
const stop = reason => {
failure ??= reason
if (exited) return
child.kill('SIGTERM')
stopTimer ??= setTimeout(() => { if (!exited) child.kill('SIGKILL') }, 5000)
}
const deadline = setTimeout(() => stop('child-deadline'), childBudgetMs)
for (const stream of [child.stdout, child.stderr]) stream.on('data', bytes => {
if (Buffer.byteLength(log) + bytes.length > 1024 * 1024) { stop('child-log-limit'); return }
log += bytes.toString()
})
const code = await new Promise(resolveExit => {
child.once('exit', (status, signal) => { exited = true; if (signal) failure ??= 'child-signal'; resolveExit(status ?? 1) })
child.once('error', () => { failure ??= 'child-error'; if (!child.pid) { exited = true; resolveExit(1) } else stop('child-error-after-pid') })
unknownTimer = setTimeout(() => { if (!exited) { stop('child-exit-unconfirmed'); resolveExit(1) } }, childBudgetMs + 15000)
})
clearTimeout(deadline); clearTimeout(stopTimer); clearTimeout(unknownTimer)
const expectedCases = { baseline: 11, physical: 7, vlan: 9, disabled: 9, mixed: 7, reconcile: 6, 'sql-journal': 1, guards: 11, coexistence: 3, alias: 4 }
const component = log.trim().split(/\r?\n/).flatMap(line => { try { return [JSON.parse(line)] } catch { return [] } }).findLast(value => value.slice === slice && Array.isArray(value.accepted))
if (!component || component.accepted.length !== expectedCases[slice] || component.actualSql !== true || component.actualCrypto !== true || component.actualAuthPolicyApprovalRuntime !== true || component.fakeVerifier !== false || component.actualRouter !== false || component.forwardingVerified !== false) failure ??= 'missing-component-receipt'
const result = { slice, accepted: code === 0 && !failure && exited, caseCount: component?.accepted?.length ?? 0, code, failure, childPid: child.pid ?? null, childExitConfirmed: exited }
lastSliceResult = result; currentChildExitConfirmed = exited
const diagnostic = vrfSliceDiagnostic(result, log, childBudgetMs, [databaseUrl, target.password, decodeURIComponent(target.password), authSecret])
await persist(slice + '-result.json', diagnostic)
// Persist outside the owner's cleanup boundary BEFORE any next slice/return.
await writeFile(join(cwd, 'fixture.log'), JSON.stringify(diagnostic.diagnostics, null, 2))
await writeFile(join(cwd, 'acceptance.json'), JSON.stringify(result, null, 2))
results.push(result)
console.log(JSON.stringify({ profile: 'test:vrf:production', ...result }))
if (!result.accepted) break
}
const receipt = { accepted: results.length === 10 && results.every(result => result.accepted), childExitConfirmed: results.every(result => result.childExitConfirmed), sameOwnedPg: true, actualRouter: false, forwardingVerified: false, results }
await persist('terminal.json', { version: 1, status: 'terminal', ...receipt, requiredCases: 68, completedCases: results.reduce((sum, value) => sum + value.caseCount, 0), diagnosticDirectory: diagnosticPath })
await writeFile(join(owned, 'vrf-production-acceptance.json'), JSON.stringify(receipt, null, 2))
return receipt
} catch {
await persist('wrapper-failure.json', { version: 1, status: 'refused', accepted: false, phase: currentSlice, failure: 'wrapper-exception', childExitConfirmed: currentChildExitConfirmed && results.every(value => value.childExitConfirmed), completedSlices: results.length, lastSliceResult, results })
throw Error('VRF profile refused; bounded diagnostics retained at ' + diagnosticPath)
}
}