64 lines
5.3 KiB
JavaScript
64 lines
5.3 KiB
JavaScript
/** Local smoke preparation only: owns a new embedded PG and one compiled child.
|
|
* Does not represent frontend/portal/Docker/whole-installation acceptance. */
|
|
import assert from 'node:assert/strict'
|
|
import fs from 'node:fs/promises'
|
|
import { createRequire } from 'node:module'
|
|
import { tmpdir } from 'node:os'
|
|
import { join,resolve,dirname,basename } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { createServer } from 'node:net'
|
|
import { randomBytes,createHash } from 'node:crypto'
|
|
import { spawn } from 'node:child_process'
|
|
const root=resolve(dirname(fileURLToPath(import.meta.url)),'../../..')
|
|
assert(process.argv.length>=3&&process.argv.length<=5,'Supply a unique evidence name, optional compiled output and delayed-approval scenario')
|
|
const delayedApproval=process.argv[4]==='delayed-approval'
|
|
assert(process.argv[4]===undefined||delayedApproval,'Unknown isolated scenario')
|
|
const name=process.argv[2];assert(/^[a-z0-9][a-z0-9-]{3,99}$/.test(name),'Invalid evidence name')
|
|
const outputName=process.argv[3]??'dist-installation-memory'
|
|
assert(/^dist-installation-memory(?:-[a-z0-9-]{3,64})?$/.test(outputName),'Invalid compiled output name')
|
|
const prefix=resolve(root,'tmp',name), statusPath=prefix+'-status.json'
|
|
const exclusive=await fs.open(statusPath,'wx');await exclusive.close()
|
|
const state={scope:'local-backend-coload-only',wholeInstallation2GbAccepted:false,compiledOutputName:outputName,delayedHumanApprovalScenario:delayedApproval,status:'starting',nodeExitCode:null,sourcePinsUnchanged:false,cleanupConfirmed:false,startedAt:new Date().toISOString(),wrapperPid:process.pid}
|
|
const save=()=>fs.writeFile(statusPath,JSON.stringify(state,null,2)+'\n');await save()
|
|
const require=createRequire(new URL('../../package.json',import.meta.url)),EmbeddedPostgres=require('embedded-postgres').default
|
|
const directory=await fs.mkdtemp(join(tmpdir(),'mm-mcp-test-')),server=createServer()
|
|
await new Promise(done=>server.listen(0,'127.0.0.1',done));const port=server.address().port;await new Promise(done=>server.close(done))
|
|
const password=randomBytes(24).toString('hex')
|
|
const postgres=new EmbeddedPostgres({databaseDir:join(directory,'pg'),port,user:'mcp_test',password,persistent:true,createPostgresUser:false,
|
|
initdbFlags:['--encoding=UTF8'],postgresFlags:['-h','127.0.0.1'],onLog:()=>{},onError:()=>{}})
|
|
const output=resolve(root,'backend',outputName),entry=resolve(output,'services/installation-co-load.owned.test.js')
|
|
async function pins() {
|
|
const rows=[]
|
|
async function walk(dir){for(const item of await fs.readdir(dir,{withFileTypes:true})){const file=join(dir,item.name);if(item.isDirectory())await walk(file);else rows.push({path:file.slice(root.length+1).replaceAll('\\','/'),sha256:createHash('sha256').update(await fs.readFile(file)).digest('hex')})}}
|
|
await walk(output);return rows.sort((a,b)=>a.path.localeCompare(b.path))
|
|
}
|
|
const before=await pins();await fs.writeFile(prefix+'-pins.json',JSON.stringify({before},null,2)+'\n')
|
|
let log,timer
|
|
try {
|
|
await postgres.initialise();await postgres.start()
|
|
log=await fs.open(prefix+'.log','wx')
|
|
const retained=Object.fromEntries(['SystemRoot','WINDIR','TEMP','TMP','LANG','LC_ALL'].filter(key=>process.env[key]).map(key=>[key,process.env[key]]))
|
|
const child=spawn(process.execPath,['--max-old-space-size=192','--max-semi-space-size=16',entry],{cwd:directory,windowsHide:true,stdio:['ignore',log.fd,log.fd],env:{...retained,
|
|
NODE_OPTIONS:'',NODE_ENV:'test',PORT:'8000',AUTH_ISSUER:'https://auth.shnt.top',AUTH_PORTAL_URL:'http://localhost:5175',SQLITE_IMPORT_FULL_HISTORY:'false',
|
|
MM_FLOW_READ_RESOURCE_PROFILE:'constrained',DATABASE_URL:`postgresql://mcp_test:${password}@127.0.0.1:${port}/postgres`,DATABASE_PATH:join(directory,'no-legacy.sqlite'),
|
|
MCP_STATE_DIR:join(directory,'state'),AUTH_REQUIRED:'true',AUTH_JWT_SECRET:randomBytes(32).toString('hex'),MCP_PUBLIC_URL:'http://localhost:8000/mcp',MMAPP_OWN_INSTALLATION_COLOAD:'true',MMAPP_OWN_COLOAD_DELAYED_APPROVAL:String(delayedApproval)}})
|
|
state.status='running';state.childPid=child.pid;await save()
|
|
timer=setTimeout(()=>{state.outerTimedOut=true;child.kill()},300000)
|
|
state.nodeExitCode=await new Promise((done,reject)=>{child.once('error',reject);child.once('exit',code=>done(code??1))})
|
|
clearTimeout(timer);await log.close();log=undefined
|
|
const after=await pins();state.sourcePinsUnchanged=JSON.stringify(before)===JSON.stringify(after)
|
|
await fs.writeFile(prefix+'-pins.json',JSON.stringify({before,after,unchanged:state.sourcePinsUnchanged},null,2)+'\n')
|
|
process.exitCode=state.nodeExitCode===0&&state.sourcePinsUnchanged?0:1
|
|
}catch(error){state.failure=error instanceof Error?error.name:'UnknownError';process.exitCode=1}
|
|
finally {
|
|
if(timer)clearTimeout(timer);if(log)await log.close()
|
|
try{
|
|
if(postgres.process?.exitCode===null)await postgres.stop()
|
|
const actual=await fs.realpath(directory),parent=await fs.realpath(tmpdir())
|
|
assert.equal(dirname(actual),parent);assert.match(basename(actual),/^mm-mcp-test-[A-Za-z0-9-]+$/);assert.equal((await fs.lstat(directory)).isSymbolicLink(),false)
|
|
await fs.rm(actual,{recursive:true,force:true,maxRetries:20,retryDelay:100});state.cleanupConfirmed=true
|
|
}catch{state.cleanupConfirmed=false;process.exitCode=1}
|
|
state.status='terminal';state.finishedAt=new Date().toISOString();await save();console.log(JSON.stringify(state))
|
|
}
|
|
if(state.cleanupConfirmed)process.exit(process.exitCode??1)
|