47 lines
3.9 KiB
JavaScript
47 lines
3.9 KiB
JavaScript
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)) }
|
|
})
|