77 lines
5.3 KiB
TypeScript
77 lines
5.3 KiB
TypeScript
import {test} from "node:test"
|
|
import assert from "node:assert/strict"
|
|
import {buildLiveOptimizerData,DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,readOptimizerObservedMetrics,type OptimizerApiServer,type RouteOptimizerSpeedProbe,calcRouteScore} from "./route-optimizer-data"
|
|
const rows:OptimizerApiServer[]=(["home-router","jump-host","exit-node"] as const).map((type,i)=>({id:i+1,name:"Router"+i,host:"192.0.2."+i,country:"RU",site:"TEST",model:"CHR",type,enabled:true,status:"online",latency:10,...(i===0?{wanUplinks:[{id:"w",name:"WAN",iface:"ether1",isp:"ISP",ip:"198.51.100.1",maxDl:100,maxUl:100}]}:{})}))
|
|
const probe:RouteOptimizerSpeedProbe={id:"p",srcServerId:"1",dstServerId:"2",srcInterface:"ether1",dstInterface:"ether2",enabled:true,lastPingRttMs:0,lastPingLossPct:0,lastTxAvgMbps:150.25,lastRxAvgMbps:null,lastPingAt:"2020-01-01T00:00:00Z"}
|
|
test("observed raw zero, decimal, null and old receipt stay separate from model clamp",()=>{
|
|
const result=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,[probe,{...probe,id:"e",srcServerId:"2",dstServerId:"3",lastTxAvgMbps:0}])
|
|
const leg=result.homes[0].wanJhLegs[0],exit=result.homes[0].fullRoutes[0].je
|
|
assert.equal(leg.pingMs,1);assert.equal(leg.dlMbps,100)
|
|
assert.equal(leg.observed?.pingMs,0);assert.equal(leg.observed?.ulMbps,150.25);assert.equal(leg.observed?.dlMbps,null)
|
|
assert.equal(leg.observed?.lossPct,0);assert.equal(leg.observed?.pingAt,"2020-01-01T00:00:00Z")
|
|
assert.equal(leg.score,calcRouteScore(leg.pingMs,leg.dlMbps,leg.ulMbps,60))
|
|
assert.equal(exit.pingMs,1);assert.equal(exit.observed?.pingMs,0);assert.equal(exit.observed?.ulMbps,0)
|
|
})
|
|
test("missing and disabled probe leave observed values unknown while model stays available",()=>{
|
|
for(const probes of [[],[{...probe,enabled:false}]]){
|
|
const result=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,probes)
|
|
const leg=result.homes[0].wanJhLegs[0]
|
|
assert.deepEqual(leg.observed,readOptimizerObservedMetrics())
|
|
assert.ok(leg.pingMs>0);assert.ok(leg.dlMbps>0)
|
|
assert.equal(result.homes[0].fullRoutes[0].je.observed?.probeId,null)
|
|
}
|
|
})
|
|
|
|
import {servers} from "./data"
|
|
test("live topology never imports mock WAN or creates a synthetic uplink",()=>{
|
|
const mock=servers.find(server=>server.type==="home-router")!
|
|
for(const wanUplinks of [undefined,[]]){
|
|
const inventory=rows.map((row,i)=>i===0?{...row,name:mock.name,host:mock.host,wanUplinks}:row)
|
|
const result=buildLiveOptimizerData(inventory,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS)
|
|
assert.equal(result.homes[0].home.wans.length,0);assert.equal(result.homes[0].fullRoutes.length,0)
|
|
assert.equal(result.homes[0].bestRoute,null)
|
|
}
|
|
})
|
|
|
|
test("Community recommendations belong to their source home only",()=>{
|
|
const inventory=[...rows,{...rows[0],id:4,name:"Second home"}]
|
|
const rule=(id:string,community:string)=>({id,community,communityName:community,gateway:"192.0.2.2",gatewayTunnelId:"",description:""})
|
|
const result=buildLiveOptimizerData(inventory,[{serverId:"1",rules:[rule("a","65001:1")]},{serverId:"4",rules:[rule("b","65001:4")]},{serverId:"3",rules:[rule("c","65001:3")]}],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS)
|
|
assert.deepEqual(result.homes[0].commRecs.map(r=>r.community),["65001:1"])
|
|
assert.deepEqual(result.homes[1].commRecs.map(r=>r.community),["65001:4"])
|
|
})
|
|
|
|
|
|
test("Community catalog retains all 1000 source rules without inventing indices",()=>{
|
|
const inventory=rows.filter(row=>row.type!=="exit-node")
|
|
const rules=Array.from({length:1000},(_,i)=>({id:String(i+1),community:'65001:'+String(i+1),communityName:'Service '+(i+1),gateway:'192.0.2.2',gatewayTunnelId:'',description:''}))
|
|
const result=buildLiveOptimizerData(inventory,[{serverId:'1',rules}],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS)
|
|
const recs=result.homes[0].commRecs
|
|
assert.equal(recs.length,1000);assert.equal(recs[999].community,'65001:1000')
|
|
assert.ok(recs.every(rec=>rec.current===null&&rec.recommended===null&&!rec.shouldSwitch))
|
|
})
|
|
|
|
|
|
test("forward and reversed peer tests keep DL toward Home and UL away, with asymmetric WAN limits",()=>{
|
|
const inventory=rows.map((row,i)=>i===0?{...row,wanUplinks:[{id:'w',name:'WAN',iface:'ether1',isp:'ISP',ip:'198.51.100.1',maxDl:100,maxUl:20}]}:row)
|
|
const home={...probe,lastPingRttMs:5,lastTxAvgMbps:30,lastRxAvgMbps:80}
|
|
const forward={...probe,id:'peer',srcServerId:'2',dstServerId:'3',srcInterface:'',lastPingRttMs:5,lastTxAvgMbps:40,lastRxAvgMbps:90}
|
|
const reverse={...forward,srcServerId:'3',dstServerId:'2',lastTxAvgMbps:90,lastRxAvgMbps:40}
|
|
for(const peer of [forward,reverse]){
|
|
const entry=buildLiveOptimizerData(inventory,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,[home,peer]).homes[0],route=entry.fullRoutes[0]
|
|
assert.equal(route.hw.observed?.dlMbps,80);assert.equal(route.hw.observed?.ulMbps,30)
|
|
assert.equal(route.hw.dlMbps,80);assert.equal(route.hw.ulMbps,20)
|
|
assert.equal(route.je.observed?.dlMbps,90);assert.equal(route.je.observed?.ulMbps,40)
|
|
assert.equal(route.je.dlMbps,90);assert.equal(route.je.ulMbps,40)
|
|
assert.equal(route.score,calcRouteScore(10,80,20,60))
|
|
}
|
|
})
|
|
|
|
test("reversed probe keeps measured zero and missing rate distinct",()=>{
|
|
const reversed={...probe,lastTxAvgMbps:0,lastRxAvgMbps:null}
|
|
assert.equal(readOptimizerObservedMetrics(reversed,true).dlMbps,0)
|
|
assert.equal(readOptimizerObservedMetrics(reversed,true).ulMbps,null)
|
|
assert.equal(readOptimizerObservedMetrics(reversed).dlMbps,null)
|
|
assert.equal(readOptimizerObservedMetrics(reversed).ulMbps,0)
|
|
})
|