import EmbeddedPostgres from 'embedded-postgres' import { build,stop } from 'esbuild' import { mkdtemp,readFile,rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { resolve,join,dirname } from 'node:path' import { pathToFileURL } from 'node:url' import { randomBytes } from 'node:crypto' import { createServer } from 'node:net' import { spawn } from 'node:child_process' import assert from 'node:assert/strict' if(process.argv.length!==2)throw Error('Profile settings tests accept no database or target arguments') const terminal=child=>child.exitCode!==null||child.signalCode!==null||child.pid===undefined async function stopOwnedChild(child,graceful=false){ if(!child||terminal(child))return await new Promise((done,reject)=>{ let timer,forceTimer const finish=()=>{clearTimeout(timer);clearTimeout(forceTimer);child.removeListener('exit',finish);done()} child.once('exit',finish) const force=()=>{ if(terminal(child)){finish();return} child.kill('SIGKILL') forceTimer=setTimeout(()=>{if(terminal(child))finish();else{child.removeListener('exit',finish);reject(Error('Owned child failed to reach confirmed terminal state: '+child.pid))}},10000) } timer=setTimeout(force,graceful?5000:10000) if(graceful&&child.connected)child.send({type:'shutdown'},error=>{if(error&&!terminal(child))child.kill()}) else child.kill() if(terminal(child))finish() }) } const repo=resolve(import.meta.dirname,'../..'),bundle=await mkdtemp(join(repo,'tmp','mm-profile-cold-bundle-')) try{ const compiled=join(bundle,'case.mjs'),driverFile=resolve(repo,'backend/scripts/flow-read-profile-http-client.mjs') await build({entryPoints:[resolve(repo,'backend/src/services/traffic-flow-settings-read-profile.owned.test.ts')],outfile:compiled,platform:'node',format:'esm',bundle:true,packages:'external',banner:{js:"import{createRequire as __req}from'node:module';const require=__req(import.meta.url);"},plugins:[{name:'current-source',setup(b){b.onResolve({filter:/^@mmapp\/contracts(?:\/.*)?$/},a=>({path:resolve(repo,'packages/contracts/src',a.path.replace(/^@mmapp\/contracts\/?/,'')||'index')+'.ts'}));b.onLoad({filter:/\.ts$/},async a=>({contents:(await readFile(a.path,'utf8')).replaceAll('import.meta.url',JSON.stringify(pathToFileURL(a.path).href)),loader:'ts'}))}}]}) for(const profile of ['standard','constrained']){ const directory=await mkdtemp(join(tmpdir(),'mm-flow-profile-cold-owned-')),listener=createServer() await new Promise(resolve=>listener.listen(0,'127.0.0.1',resolve));const port=listener.address().port;await new Promise(resolve=>listener.close(resolve)) const password=randomBytes(24).toString('hex'),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','-c','shared_buffers=64MB','-c','work_mem=2MB'],onLog:()=>{},onError:()=>{}}) let started=false,client,app try{ await postgres.initialise();await postgres.start();started=true await new Promise((done,reject)=>{client=spawn(process.execPath,[driverFile],{cwd:directory,windowsHide:true,stdio:['ignore','inherit','inherit','ipc'],env:{...process.env,NODE_OPTIONS:'--max-old-space-size=192 --max-semi-space-size=16'}});client.on('message',message=>{if(message.type==='client-ready')done();if((message.type==='client-result'||message.type==='client-progress')&&app?.connected)app.send(message)});client.once('error',reject);client.once('exit',code=>{if(!app)reject(Error('Client startup exit '+code))})}) const outcome=await new Promise((done,reject)=>{ app=spawn(process.execPath,[compiled],{cwd:directory,windowsHide:true,stdio:['ignore','inherit','inherit','ipc'],env:{...process.env,NODE_OPTIONS:'--max-old-space-size=192 --max-semi-space-size=16',MM_FLOW_READ_RESOURCE_PROFILE:profile,MMAPP_OWN_FLOW_PROFILE_TEST:'true',DATABASE_URL:`postgresql://mcp_test:${password}@127.0.0.1:${port}/postgres`,DATABASE_PATH:join(directory,'unused.sqlite'),MCP_STATE_DIR:join(directory,'state'),AUTH_REQUIRED:'true',AUTH_ISSUER:'https://auth.test.local',AUTH_JWT_SECRET:'owned-profile-only',MCP_PUBLIC_URL:'http://localhost:8000/mcp',NODE_ENV:'test'}}) const timeout=setTimeout(()=>reject(Error('Profile cold test timed out')),120000) app.on('message',message=>{if(message.type==='client-command')client.send(message)}) app.once('error',error=>{clearTimeout(timeout);reject(error)});app.once('exit',code=>{clearTimeout(timeout);done(code??1)}) }) assert.equal(outcome,0,profile+' public/settings profile failed') }finally{ const stopped=await Promise.allSettled([stopOwnedChild(app),stopOwnedChild(client,true)]) for(const result of stopped)if(result.status==='rejected')throw result.reason if(started)await postgres.stop() assert.equal(dirname(resolve(directory)),resolve(tmpdir()));assert.match(directory,/mm-flow-profile-cold-owned-/);await rm(directory,{recursive:true,force:true,maxRetries:20,retryDelay:100}) } } }finally{stop();assert.equal(dirname(resolve(bundle)),resolve(repo,'tmp'));await rm(bundle,{recursive:true,force:true,maxRetries:20,retryDelay:100})} // All owned children and files are confirmed closed above; avoid library ambient handles keeping the CLI alive. console.log('PASS both owned profile fixtures and confirmed child cleanup') process.exit(0)