import assert from 'node:assert/strict' import ts from 'typescript' import { readFileSync } from 'node:fs' import { createRequire } from 'node:module' import { resolve } from 'node:path' const require=createRequire(import.meta.url),zod=require('zod'),here=import.meta.dirname function evaluate(source,mocks={}){ const exports={},module={exports} const code=ts.transpileModule(source,{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2022}}).outputText new Function('require','exports','module',code)(id=>{if(id==='zod')return zod;if(Object.hasOwn(mocks,id))return mocks[id];throw Error('Unexpected dependency '+id)},exports,module) return module.exports } const contracts=evaluate(readFileSync(resolve(here,'../../packages/contracts/src/traffic-flow.ts'),'utf8')) let quality=null,slotReleases=0 const qualityHelper=evaluate(readFileSync(resolve(here,'../src/modules/mcp/flow-read-quality.ts'),'utf8'),{ '@mmapp/contracts/traffic-flow':contracts,'../../services/traffic-flow-ingest.js':{getFlowRuntimeCounters:async()=>({dataQuality:quality})},'./traffic-history-actions.js':{assertTrafficHistoryAccess:async()=>{}}, }) const server={id:1,name:'selected',type:'jump-host'},dto={bpsNow:0,bpsWire:0,bpsOverlay:0,bytesWire:0,live:false,rxSeries:[],txSeries:[],services:[]} const window={rows:[],topRowsPerBucketLimit:20,topConversationsLimit:20,since:'from',until:'to'} class McpError extends Error{constructor(code,message,statusCode){super(message);this.code=code;this.statusCode=statusCode}} const mocks={ './flow-read-quality.js':qualityHelper, '@mmapp/contracts/traffic-flow':{...contracts,flowAnalyticsDtoSchema:{parse:x=>x},flowMapHopsDtoSchema:{parse:x=>x}}, '../../services/traffic-flow-analytics.js':{buildFlowAnalytics:async()=>dto,getFlowMonthly:async()=>({month:'2026-10',bytes:0,countries:[],services:[],asns:[]}),safeBuildLiveFlowSample:async()=>({event:'sample',data:dto}),LIVE_ANALYTICS_MINUTES:5}, '../../services/traffic-flow-map-hops.js':{buildFlowMapHops:async()=>({live:false,hops:[]})}, '../../services/traffic-flow-source-limits.js':{readFlowSourceLimits:async()=>({topN:20}),readFlowMapSourceLimits:async()=>({topN:20,mapServiceMinSharePct:5})}, '../servers/repository/servers-repository.js':{getServerRowById:async()=>server}, '../../services/traffic-flow-server-source.js':{getBoundedFlowServerRowById:async()=>server}, './flow-source-scope.js':{assertBoundedFlowSource:async()=>{},flowCatalogDevices:async()=>({selected:[server],incomplete:false})}, './traffic-history-actions.js':{assertTrafficHistoryAccess:async()=>{},assertTrafficHistorySource:async()=>{}}, './security.js':{McpError},'node:util':require('node:util'),'node:timers/promises':require('node:timers/promises'), '../../services/traffic-flow-live-slots.js':{FLOW_LIVE_TICK_MS:2000,tryAcquireFlowLiveSlot:()=>true,releaseFlowLiveSlot:()=>slotReleases++}, '../../services/traffic-flow-settings.js':{getTrafficFlowSettingsRow:async()=>({topN:20})}, '../../services/traffic-flow-ifindex.js':{resolveIfaceName:()=>''}, '../../services/traffic-flow-talkers-model.js':{projectFlowTalkers:()=>({talkers:[],totalConversations:0})}, '../../services/traffic-flow-catalog-read.js':{readFlowCatalogWindow:async()=>window,readFlowExporterInputs:async()=>({window,catalog:{list:[server]},configuredIds:new Set([1]),configurationMembershipVerified:true}),readFlowClientInputs:async()=>({users:[],total:0,window}),observeFlowExporter:()=>({id:'1'}),observeFlowClient:()=>({})}, './catalog-scope.js':{catalogPaging:zod.z.object({offset:zod.z.number().default(0),limit:zod.z.number().default(50)}),catalogDevices:async()=>({selected:[server],incomplete:false})}, } const actions=[] for(const [file,method] of [['flow-catalog-actions.ts','registerFlowCatalogActions'],['flow-analytics-actions.ts','registerFlowAnalyticsActions'],['flow-map-actions.ts','registerFlowMapActions'],['flow-live-actions.ts','registerFlowLiveActions']]){ const actionModule=evaluate(readFileSync(resolve(here,'../src/modules/mcp',file),'utf8'),mocks) actionModule[method]((...args)=>actions.push({id:args[0],schema:args[7],read:args[8].read})) } assert.equal(actions.length,7) const base={version:1,collectorEpoch:'00000000-0000-4000-8000-000000000001',scope:'collector-global',exportCoverage:'unverified',localAdmission:'partial',records:'1',bytes:'10',packets:'1',knownLowerBounds:{records:'1',bytes:'10',packets:'1'},firstAt:'2026-10-29T00:00:00.000Z',lastAt:'2026-10-29T00:00:00.000Z',intervalUnknown:false,reasons:{'aggregate-capacity':true,'ring-capacity':false,'key-width':false,'unsafe-numeric':false},counterOverflow:false,rawRowsPolicy:'topN-and-pending-cap-not-complete',lifetimeScope:'current-collector-isolate'} const clean={...base,localAdmission:'no-local-loss-observed',records:'0',bytes:'0',packets:'0',knownLowerBounds:{records:'0',bytes:'0',packets:'0'},firstAt:null,lastAt:null,reasons:{'aggregate-capacity':false,'ring-capacity':false,'key-width':false,'unsafe-numeric':false}} const cases=[null,base,{...base,localAdmission:'loss-quantity-unknown',bytes:null},{...base,localAdmission:'no-local-loss-observed'},clean] let count=0 for(const globalAllowed of [false,true])for(const state of cases){ quality=state for(const action of actions){ const input=action.schema.parse(action.id==='traffic.flow.monthly.read'?{month:'2026-10'}:{serverId:1}),context={connection:{policy:{scope:{serverIds:[1],allowGlobal:globalAllowed,operationIds:globalAllowed?['netflow.settings.read']:[]}}}} const output=await action.read(input,context),expected=contracts.flowIngestQualitySchema.safeParse(globalAllowed?state:null) assert.deepEqual(output.dataQuality,expected.success?expected.data:null,action.id) assert.equal(output.collectorQualityAvailable,expected.success,action.id) assert.equal(output.collectorQualityScope,'current-collector-isolate',action.id) assert.equal(output.incomplete,['traffic.flow.talkers.read','traffic.flow.analytics.read','traffic.flow.map.read','traffic.flow.live.read'].includes(action.id),action.id+' preserves original result completeness') assert.equal(output.collectorObservationIncomplete,!expected.success||expected.data.localAdmission!=='no-local-loss-observed',action.id) assert.equal(output.coverageVerified,false,action.id);assert.equal(output.exportCoverage,'unverified',action.id) count++ } } assert.equal(slotReleases,10) console.log('PASS '+count+' actual registered MCP read adapter invocations: unavailable, partial, unknown quantities and invalid healthy claim; inert source/auth adapters; real semantic quality schema')