#!/usr/bin/env bash
# tests/cloud-assert/run-tests.sh -- offline fakebin harness for cloud-assert.sh
# (DOCFIX-075). Fake juju/openstack/jq-free: real jq stays real; fake juju and
# openstack replay fixture outputs keyed by argument patterns, selected per test
# via FIXDIR. Asserts the behavioral checks fire on the exact incident
# signatures they were written for (D-045/D-046/D-051/D-062/vault-sealed).
# Mutates nothing outside $TMP. Exit: 0 all pass | 1 any failed. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
BIN="$TMP/bin"; mkdir -p "$BIN"
cat > "$BIN/juju" <<'FB'
#!/usr/bin/env bash
# fake juju: replay fixtures from $FIXDIR keyed by argv patterns
A="$*"
case "$A" in
*"status -m"*"keystone --format=line"*) cat "$FIXDIR/ks-line.txt" ;;
*"status -m"*"--format=json"*) cat "$FIXDIR/status.json" ;;
*"vault/0"*"vault status"*) cat "$FIXDIR/vault.txt" ;;
*"cluster/status OVN_Northbound"*) cat "$FIXDIR/ovn-nb.txt" ;;
*"cluster/status OVN_Southbound"*) cat "$FIXDIR/ovn-sb.txt" ;;
*"connection-status"*) cat "$FIXDIR/chassis.txt" ;;
*"ps -ww -C magnum-conductor"*) cat "$FIXDIR/conductor.txt" ;;
*) echo "fake-juju: unmatched: $A" >&2; exit 1 ;;
esac
FB
cat > "$BIN/openstack" <<'FB'
#!/usr/bin/env bash
A="$*"
case "$A" in
*"hypervisor list"*) cat "$FIXDIR/hypervisors.json" ;;
*"compute service list"*) cat "$FIXDIR/services.json" ;;
*"loadbalancer list"*) cat "$FIXDIR/lbs.json" ;;
*"domain show magnum"*) cat "$FIXDIR/domain.txt" ;;
*"user show magnum_domain_admin"*) cat "$FIXDIR/user.txt" ;;
*"coe service list"*) cat "$FIXDIR/coe.txt" ;;
*) echo "fake-openstack: unmatched: $A" >&2; exit 1 ;;
esac
FB
chmod +x "$BIN/juju" "$BIN/openstack"
mkgood() { # healthy-cloud fixture set
local d="$1"; mkdir -p "$d"
cat > "$d/status.json" <<'J'
{"applications":{
"mysql-innodb-cluster":{"units":{
"mysql-innodb-cluster/0":{"workload-status":{"current":"active","message":"Unit is ready: Mode: R/W, Cluster is ONLINE"}},
"mysql-innodb-cluster/1":{"workload-status":{"current":"active","message":"Unit is ready: Mode: R/O, Cluster is ONLINE"}},
"mysql-innodb-cluster/2":{"workload-status":{"current":"active","message":"Unit is ready: Mode: R/O, Cluster is ONLINE"}}}},
"ovn-central":{"units":{"ovn-central/0":{"workload-status":{"current":"active"}},
"ovn-central/1":{"workload-status":{"current":"active"}},"ovn-central/2":{"workload-status":{"current":"active"}}}},
"nova-compute":{"units":{"nova-compute/0":{"workload-status":{"current":"active"}}}},
"octavia":{"units":{"octavia/0":{"workload-status":{"current":"active"}}}},
"glance-simplestreams-sync":{"units":{"glance-simplestreams-sync/0":{"workload-status":{"current":"unknown"}}}}
}}
J
printf 'Initialized true\nSealed false\n' > "$d/vault.txt"
printf 'Cluster ID: abcd (uuid)\nServers: 3\n' > "$d/ovn-nb.txt"
cp "$d/ovn-nb.txt" "$d/ovn-sb.txt"
printf 'connected\n' > "$d/chassis.txt"
printf '/usr/bin/python3 /usr/bin/magnum-conductor --config-file /etc/magnum/magnum.conf --config-dir /etc/magnum/magnum.conf.d\n' > "$d/conductor.txt"
printf '[{"Hypervisor Hostname":"openstack0","State":"up"}]\n' > "$d/hypervisors.json"
printf '[{"Binary":"nova-compute","Host":"openstack0","Status":"enabled","State":"up"}]\n' > "$d/services.json"
printf '[]\n' > "$d/lbs.json"
printf 'True\n' > "$d/domain.txt"
printf 'magnum_domain_admin\n' > "$d/user.txt"
printf 'magnum-conductor\n' > "$d/coe.txt"
printf 'keystone: active/idle 2024.1 PO: 10.12.4.50\n' > "$d/ks-line.txt"
}
run() { # run <want_rc> <regex> <label> <fixdir> [env...]
local want="$1" rx="$2" label="$3" fd="$4"; shift 4
local out rc
out="$(cd "$TMP" && env "$@" FIXDIR="$fd" PATH="$BIN:$PATH" OS_AUTH_URL="https://fake:5000/v3" \
bash "$REPO/scripts/cloud-assert.sh" 2>&1)"; rc=$?
if [[ "$rc" == "$want" ]] && grep -qE "$rx" <<<"$out"; then
echo " PASS $label"; PASS=$((PASS+1))
else
echo " FAIL $label (rc=$rc want=$want)"; grep -E '\[(FAIL|WARN)\]|CLOUD-ASSERT' <<<"$out" | head -6 | sed 's/^/ /'; FAIL=$((FAIL+1))
fi
}
F="$TMP/good"; mkgood "$F"; run 0 'CLOUD-ASSERT: PASS' "T1 healthy cloud -> PASS" "$F"
F="$TMP/sealed"; mkgood "$F"; printf 'Initialized true\nSealed true\n' > "$F/vault.txt"
run 1 'vault SEALED' "T2 sealed vault FAILS (with unseal pointer)" "$F"
F="$TMP/rw2"; mkgood "$F"; python3 - "$F/status.json" <<'PY'
import json,sys; p=sys.argv[1]; d=json.load(open(p))
u=d["applications"]["mysql-innodb-cluster"]["units"]
u["mysql-innodb-cluster/1"]["workload-status"]["message"]="Unit is ready: Mode: R/W, Cluster is ONLINE"
json.dump(d,open(p,"w"))
PY
run 1 'R/W count=2' "T3 mysql split-brain (2x R/W) FAILS" "$F"
F="$TMP/chassis"; mkgood "$F"; printf 'not connected\n' > "$F/chassis.txt"
run 1 'chassis NOT connected' "T4 dead chassis FAILS (D-045 class)" "$F"
F="$TMP/pobroken"; mkgood "$F"; printf 'keystone: active/idle 2024.1 PO (broken) 10.12.4.50\n' > "$F/ks-line.txt"
run 1 'PO \(broken\)' "T5 PO(broken) FAILS (DOCFIX-071)" "$F"
F="$TMP/nodomain"; mkgood "$F"; printf 'No domain with a name or ID\n' > "$F/domain.txt"
run 1 "domain-setup" "T6 missing trustee domain FAILS (D-046)" "$F"
F="$TMP/graft"; mkgood "$F"; printf '/usr/bin/python3 /usr/bin/magnum-conductor --config-file /etc/magnum/magnum.conf\n' > "$F/conductor.txt"
run 1 'lack --config-dir' "T7 dead conductor graft FAILS (D-037)" "$F"
F="$TMP/lberr"; mkgood "$F"; printf '[{"name":"lb1","provisioning_status":"ERROR","operating_status":"OFFLINE"}]\n' > "$F/lbs.json"
run 1 'LBs unhealthy' "T8 LB in ERROR FAILS (with failover pointer)" "$F"
# T9: no admin scope -> A5-A7 HELD, exit 2 (never a silent pass)
F="$TMP/held"; mkgood "$F"
out="$(cd "$TMP" && env -u OS_AUTH_URL FIXDIR="$F" PATH="$BIN:$PATH" bash "$REPO/scripts/cloud-assert.sh" 2>&1)"; rc=$?
if [[ "$rc" == "2" ]] && grep -q 'HELD: no admin scope' <<<"$out"; then echo " PASS T9 missing admin scope -> HELD (rc 2)"; PASS=$((PASS+1))
else echo " FAIL T9 (rc=$rc)"; FAIL=$((FAIL+1)); fi
echo; echo "RESULT: PASS=$PASS FAIL=$FAIL"
[[ "$FAIL" -eq 0 ]] && { echo "ALL PASS"; exit 0; } || exit 1