32 lines
2.7 KiB
JavaScript
32 lines
2.7 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { test } from 'node:test'
|
|
import { evaluateObservedInstallationBudget as evaluate } from './installation-memory-budget.mjs'
|
|
const MiB=1024*1024
|
|
function fixture() {
|
|
const containers=['backend','postgres','frontend','portal'].map((role,index)=>({id:String(index+1).repeat(64),role,running:true,imageId:'sha256:'+'a'.repeat(64),startedAt:'2026-10-28T12:00:00Z',memoryLimitBytes:0,reportedLimitBytes:2*1024*MiB}))
|
|
const roleById=Object.fromEntries(containers.map(row=>[row.id,row.role]))
|
|
return {inventory:{readOnly:true,containerSetStable:true,environmentRead:false,serviceChanges:false,newFixtureStarted:false,hostMemTotalBytes:2021212*1024,containers},roleById}
|
|
}
|
|
test('observed zero configured caps stay unbounded despite positive Docker reported host limits',()=>{
|
|
const f=fixture(), result=evaluate(f.inventory,f.roleById)
|
|
assert.equal(result.budgetFits,false);assert(result.reasons.includes('UNBOUNDED_CONTAINER'));assert.equal(result.configuredContainerBytes,0)
|
|
assert.equal(result.wholeInstallation2GbAccepted,false);assert.equal(result.roleLabelsAuthenticated,false);assert.equal(result.inventoryFreshnessProven,false)
|
|
})
|
|
test('complete explicit configured caps can fit arithmetic without becoming runtime acceptance',()=>{
|
|
const f=fixture();[512,384,160,192].forEach((limit,index)=>{f.inventory.containers[index].memoryLimitBytes=limit*MiB})
|
|
const result=evaluate(f.inventory,f.roleById);assert(result.budgetFits);assert.equal(result.plannedBytes,1760*MiB);assert.equal(result.changesAuthorized,false)
|
|
})
|
|
test('uncertain snapshots, started fixtures and missing limit/identity fields refuse',()=>{
|
|
for(const mutate of [f=>{f.inventory.containerSetStable=false},f=>{f.inventory.serviceChanges=true},f=>{f.inventory.newFixtureStarted=true},f=>{delete f.inventory.containers[0].memoryLimitBytes},f=>{f.inventory.containers[0].running=false},f=>{f.inventory.containers[0].imageId='unknown'},f=>{f.inventory.containers[0].startedAt='invalid'}]){const f=fixture();mutate(f);assert.throws(()=>evaluate(f.inventory,f.roleById))}
|
|
})
|
|
test('missing, foreign, inherited and accessor role labels cannot silently classify a service',()=>{
|
|
for(const mode of ['missing','foreign','inherited','accessor']){
|
|
const f=fixture(),key=f.inventory.containers[0].id
|
|
if(mode==='missing')delete f.roleById[key]
|
|
if(mode==='foreign')f.roleById['f'.repeat(64)]='other'
|
|
if(mode==='inherited'){const labels=Object.create({[key]:'backend'});for(const [id,role]of Object.entries(f.roleById))if(id!==key)labels[id]=role;f.roleById=labels}
|
|
if(mode==='accessor')Object.defineProperty(f.roleById,key,{enumerable:true,get(){assert.fail('Role getter must not execute')}})
|
|
assert.throws(()=>evaluate(f.inventory,f.roleById))
|
|
}
|
|
})
|