#!/usr/bin/env bash
# tests/maas-node-power/run-tests.sh
#
# Harness for scripts/maas-node-power.sh. OFFLINE -- stubs `maas` and `virsh` on
# PATH; contacts no MAAS and no libvirt.
#
# Pinned properties, each load-bearing:
# 1. DRY BY DEFAULT (--commit required to write).
# 2. Matching is BY MAC, never by name -- MAAS renames machines at enlistment
# ("ace-oyster"), so a name-based match would silently map nothing (or worse,
# the wrong node) on a real fleet.
# 3. Ground truth is a power QUERY, not a saved parameter: a stored power_id
# proves nothing about whether MAAS can actually reach the hypervisor.
# 4. Preconditions fail loud (bad URI, missing tools, no matches).
# 5. The pod-vs-per-machine reasoning stays documented -- it is a measured
# refutation of D-103/D-123's stated mechanism and must not be lost.
# ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
S="$(cd "$HERE/../../scripts" && pwd)/maas-node-power.sh"
pass=0; fail=0
ok() { pass=$((pass+1)); }
bad() { fail=$((fail+1)); echo " FAIL: $1"; }
[ -f "$S" ] && ok || bad "maas-node-power.sh missing"
bash -n "$S" 2>/dev/null && ok || bad "does not parse"
# --- stub environment -------------------------------------------------------
STUB="$(mktemp -d)"; trap 'rm -rf "$STUB"' EXIT
cat > "$STUB/virsh" <<'EOF'
#!/usr/bin/env bash
# stub libvirt: two domains, distinct MACs; one extra domain outside the prefix.
case "$*" in
*"list --all --name"*) printf 'vr1-dc0-control-01\nvr1-dc0-compute-01\nother-vm\n' ;;
*"domiflist vr1-dc0-control-01"*) printf 'Interface Type Source Model MAC\n---\nvnet0 network metal-admin virtio 52:54:00:aa:aa:aa\n' ;;
*"domiflist vr1-dc0-compute-01"*) printf 'Interface Type Source Model MAC\n---\nvnet1 network metal-admin virtio 52:54:00:BB:BB:BB\n' ;;
*) printf '' ;;
esac
EOF
cat > "$STUB/maas" <<'EOF'
#!/usr/bin/env bash
# stub MAAS: machines with MAAS-assigned random hostnames (NOT domain names).
case "$*" in
*"machines read"*)
cat <<'JSON'
[{"system_id":"aaa111","hostname":"ace-oyster","power_type":"","interface_set":[{"mac_address":"52:54:00:aa:aa:aa"}]},
{"system_id":"bbb222","hostname":"cuddly-bengal","power_type":"","interface_set":[{"mac_address":"52:54:00:bb:bb:bb"}]},
{"system_id":"zzz999","hostname":"office1-netbox","power_type":"lxd","interface_set":[{"mac_address":"00:16:3e:00:00:01"}]}]
JSON
;;
*"machine update"*) echo '{"ok":true}'; echo "$*" >> "$TESTLOG" ;;
*"query-power-state"*) echo '{"state":"off"}' ;;
*) printf '' ;;
esac
EOF
chmod +x "$STUB/virsh" "$STUB/maas"
export TESTLOG="$STUB/updates.log"; : > "$TESTLOG"
PATH="$STUB:$PATH"; export PATH
URI="qemu+ssh://jessea123@172.31.0.2/system"
# --- 1. usage / precondition failures ---
"$S" >/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "no args must fail"
"$S" not-a-uri >/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "a non-libvirt URI must be rejected"
out="$("$S" not-a-uri 2>&1)"
printf '%s' "$out" | grep -q "does not look like a libvirt URI" && ok || bad "bad URI message missing"
# --- 2. DRY BY DEFAULT ---
out="$("$S" "$URI" vr1-dc0 2>&1)"
printf '%s' "$out" | grep -q "DRY RUN" && ok || bad "no dry-run announcement"
[ -s "$TESTLOG" ] && bad "DRY RUN wrote to MAAS (machine update called)" || ok
# --- 3. MATCHED BY MAC, not by name. MAAS hostnames are random at enlistment,
# so the mapping must pair ace-oyster <-> vr1-dc0-control-01.
printf '%s' "$out" | grep -q "ace-oyster.*vr1-dc0-control-01" && ok || bad "MAC match ace-oyster -> control-01 missing"
printf '%s' "$out" | grep -q "cuddly-bengal.*vr1-dc0-compute-01" && ok || bad "MAC match is case-sensitive (stub MAC was upper-case) or missing"
# a non-fleet machine must NOT be touched
printf '%s' "$out" | grep -q "office1-netbox" && bad "an unrelated MAAS machine was included" || ok
# --- 4. --commit writes, and every write is power-QUERY verified ---
: > "$TESTLOG"
out="$("$S" --commit "$URI" vr1-dc0 2>&1)"
grep -q "machine update aaa111" "$TESTLOG" && ok || bad "--commit did not update the first machine"
grep -q "machine update bbb222" "$TESTLOG" && ok || bad "--commit did not update the second machine"
grep -q "power_parameters_power_id=vr1-dc0-control-01" "$TESTLOG" && ok || bad "power_id not set to the DOMAIN name"
grep -q "power_parameters_power_address=$URI" "$TESTLOG" && ok || bad "power_address not passed through"
grep -q "power_type=virsh" "$TESTLOG" && ok || bad "power_type=virsh not set"
printf '%s' "$out" | grep -q "power state: off" && ok || bad "no ground-truth power query after the update"
grep -q "office1-netbox" "$TESTLOG" && bad "an unrelated machine was updated" || ok
# --- 5. no domains matching the prefix -> fail loud, do not silently no-op ---
out="$("$S" --commit "$URI" no-such-prefix 2>&1)"; rc=$?
[ "$rc" -ne 0 ] && ok || bad "an empty domain match must be nonzero"
printf '%s' "$out" | grep -q "no domains matching" && ok || bad "empty-match message missing"
# --- 6. the measured reasoning must stay documented ---
grep -q "domblkinfo" "$S" && ok || bad "the pod-incompatibility evidence (domblkinfo) is no longer cited"
grep -qi "missing storage backend" "$S" && ok || bad "the verbatim libvirt error is gone"
grep -qi "roosevelt" "$S" && ok || bad "the Roosevelt per-node-power rationale is gone"
grep -qi "region" "$S" && ok || bad "the credential-lives-where-MAAS-dials-from note is gone"
grep -q "D-103" "$S" && ok || bad "no reference to the decision this amends"
echo
total=$((pass+fail))
if [ "$fail" -eq 0 ]; then echo "maas-node-power: $pass/$total PASS"; exit 0; fi
echo "maas-node-power: $fail/$total FAIL"; exit 1