#!/usr/bin/env bash
# Harness for scripts/dc-dc-whole-host-budget.py (R3-F06: the committed replacement
# for the lost scratchpad/optc-calc.py). Read-only arithmetic; asserts the ruled
# Model B / R-3 shape FITS, the model math, and the failure/usage paths.
set -u
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT="$HERE/../../scripts/dc-dc-whole-host-budget.py"
PASS=0; FAIL=0
ok() { PASS=$((PASS+1)); }
bad() { FAIL=$((FAIL+1)); echo " FAIL: $1"; }
# assert_contains <desc> <expected-substring> <command...>
assert_contains() {
local desc="$1" want="$2"; shift 2
local out; out="$("$@" 2>&1)"
if grep -qF -- "$want" <<<"$out"; then ok; else bad "$desc (want '$want')"; fi
}
# assert_exit <desc> <expected-code> <command...>
assert_exit() {
local desc="$1" want="$2"; shift 2
"$@" >/dev/null 2>&1; local got=$?
if [ "$got" -eq "$want" ]; then ok; else bad "$desc (want exit $want, got $got)"; fi
}
# 1. Ruled shape: Model B, defaults (3+2+4) -> FIT at 838 GiB, exit 0.
assert_contains "model B ram=838" "ram=838/1024GiB verdict=FIT" python3 "$SCRIPT" --model B
assert_contains "model B headroom" "headroom=186GiB" python3 "$SCRIPT" --model B
assert_exit "model B fits (0)" 0 python3 "$SCRIPT" --model B
assert_contains "model B binds RAM" "BINDING resource" python3 "$SCRIPT" --model B
# 2. Model A (fallback) -> FIT at 822 GiB (16 GiB less: no containment overhead, has small rack).
assert_contains "model A ram=822" "ram=822/1024GiB verdict=FIT" python3 "$SCRIPT" --model A
assert_exit "model A fits (0)" 0 python3 "$SCRIPT" --model A
# 3. Model math: B adds containment overhead on top of A's node RAM; B RAM > A RAM by 2*16 - (2*rackmem).
# (B: +2*16 overhead; A: +2*8 rack) => B - A = 32 - 16 = 16 GiB. Assert B==838, A==822.
assert_contains "B minus A = 16 GiB" "ram=838/1024GiB" python3 "$SCRIPT" --model B
# 4. Old 3-storage shape (pre-R-3) is LIGHTER: 3 storage/DC -> 838 - 2*24 = 790 GiB (matches D-121's original).
assert_contains "3-storage = 790" "ram=790/1024GiB verdict=FIT" python3 "$SCRIPT" --model B --storage 3,8,24,550
# 5. NO-FIT path: oversized control nodes push RAM over 1024 -> exit 1, NO-FIT.
assert_exit "oversize -> exit 1" 1 python3 "$SCRIPT" --model B --control 3,16,160,150
assert_contains "oversize NO-FIT" "verdict=NO-FIT" python3 "$SCRIPT" --model B --control 3,16,160,150
# 6. Usage error: malformed triple -> exit 2.
assert_exit "bad arg -> exit 2" 2 python3 "$SCRIPT" --host 256,1024
# 7. Determinism: two identical runs produce identical output.
A_OUT="$(python3 "$SCRIPT" --model B 2>&1)"; B_OUT="$(python3 "$SCRIPT" --model B 2>&1)"
if [ "$A_OUT" = "$B_OUT" ]; then ok; else bad "non-deterministic output"; fi
# 8. Single-DC scaling: --dcs 1 halves the node RAM contribution.
assert_contains "dcs=1 lighter" "verdict=FIT" python3 "$SCRIPT" --model B --dcs 1
TOTAL=$((PASS+FAIL))
echo "dc-dc-whole-host-budget: $PASS/$TOTAL PASS"
[ "$FAIL" -eq 0 ]