import assert from 'node:assert/strict' import { test } from 'node:test' import { evaluateInstallationMemoryBudget as evaluate } from './installation-memory-budget.mjs' const MiB = 1024 * 1024 function fixture() { const containers = [['backend',512],['postgres',384],['frontend',160],['portal',192]].map(([role, limit], i) => ({ id: String(i+1).repeat(64), role, configuredLimitBytes: limit * MiB })) return { hostMemTotalBytes: 2021212 * 1024, osReserveBytes: 512 * MiB, observedContainerIds: containers.map(row => row.id), containers } } test('complete four-service candidate fits arithmetic, never runtime acceptance or change permission', () => { const input = fixture(), before = structuredClone(input), result = evaluate(input) assert.equal(result.budgetFits,true); assert.equal(result.plannedBytes,1760 * MiB) assert.equal(result.wholeInstallation2GbAccepted,false); assert.equal(result.runtimeAcceptanceClaimed,false); assert.equal(result.changesAuthorized,false) assert(Object.isFrozen(result)); assert(Object.isFrozen(result.reasons)); assert.deepEqual(input,before) }) test('coexisting baseline and owned app/PG cannot represent one installation', () => { const input = fixture() for (const [i,role,limit] of [[5,'backend',320],[6,'postgres',192]]) { input.containers.push({id:String(i).repeat(64),role,configuredLimitBytes:limit*MiB}); input.observedContainerIds.push(String(i).repeat(64)) } const result = evaluate(input) assert.equal(result.budgetFits,false); assert(result.reasons.includes('DUPLICATE_APPLICATION_STACK')); assert(result.reasons.includes('INSTALLATION_BUDGET_EXCEEDS_HOST')) }) test('unknown host containers, missing and foreign inventory entries refuse', () => { for (const mode of ['omitted','foreign','missing-service']) { const input = fixture() if (mode === 'omitted') input.observedContainerIds.push('a'.repeat(64)) if (mode === 'foreign') input.containers[0].id='b'.repeat(64) if (mode === 'missing-service') { input.containers.splice(0,1); input.observedContainerIds.splice(0,1) } const result = evaluate(input); assert.equal(result.budgetFits,false) assert(result.reasons.includes(mode === 'missing-service'?'REQUIRED_SERVICE_MISSING':'INCOMPLETE_HOST_CONTAINER_INVENTORY')) } }) test('unlimited cap is distinct from observed idle RSS or Docker host limit display', () => { const input = fixture(); input.containers[0].configuredLimitBytes=0 assert(evaluate(input).reasons.includes('UNBOUNDED_CONTAINER')) input.containers[0].configuredLimitBytes=3*1024*MiB assert(evaluate(input).reasons.includes('CONTAINER_LIMIT_EXCEEDS_HOST')) }) test('extra service, reserve and physical host all count toward the same budget', () => { const input = fixture(); input.containers.push({id:'a'.repeat(64),role:'other',configuredLimitBytes:256*MiB}); input.observedContainerIds.push('a'.repeat(64)) assert(evaluate(input).reasons.includes('INSTALLATION_BUDGET_EXCEEDS_HOST')) const low = fixture(); low.osReserveBytes=128*MiB; assert(evaluate(low).reasons.includes('OS_RESERVE_BELOW_CANDIDATE_FLOOR')) const large = fixture(); large.hostMemTotalBytes=4*1024*MiB; assert(evaluate(large).reasons.includes('HOST_OUTSIDE_2GIB_TARGET')) }) test('malformed, duplicate, fractional, oversized or unsafe inventory rejects without I/O', () => { const mutations = [input=>{input.inject='unknown'},input=>{input.containers[0].role='invented'},input=>{input.containers[0].configuredLimitBytes=-1},input=>{input.containers[0].configuredLimitBytes=0.5},input=>{input.containers[0].id='../foreign'},input=>{input.containers[1].id=input.containers[0].id},input=>{input.observedContainerIds.push(input.observedContainerIds[0])},input=>{input.containers[0].configuredLimitBytes=Number.MAX_SAFE_INTEGER},input=>{input.observedContainerIds=Array(129).fill('a'.repeat(64))}] for (const mutate of mutations) { const input=fixture();mutate(input);assert.throws(()=>evaluate(input)) } })