#!/usr/bin/env bash
# tests/opnsense-plugins/run-tests.sh
#
# Harness for scripts/opnsense-plugins.sh (D-129 edge plugin profile installer).
# OFFLINE -- no network, no creds, no edge. Drives --dry-run (which prints the
# intended opnsense-api.sh calls without touching the wire) + greps the source,
# PINNING the two lessons the script exists to encode: (1) install ONE plugin at
# a time and POLL between (the firmware-lock trap), (2) os-qemu-guest-agent is a
# package, NOT a working agent (the channel/enable/restart caveat). A careless
# edit reddens HERE, not on a live edge. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
S="$(cd "$HERE/../../scripts" && pwd)/opnsense-plugins.sh"
pass=0; fail=0
ok() { pass=$((pass+1)); }
bad() { fail=$((fail+1)); echo " FAIL: $1"; }
# dry-run still needs a host set (opnsense-api.sh checks it before the dry-run
# branch) -- use an invalid one; nothing must reach it.
export OPNSENSE_API_HOST="test.invalid"
export OPNSENSE_API_CREDS="/nonexistent/should-never-be-read"
# --- parse / usage ---
bash -n "$S" 2>/dev/null && ok || bad "does not parse (bash -n)"
bash "$S" >/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "no-arg must be nonzero (usage)"
bash "$S" bogus-cmd >/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "bad subcommand must be nonzero"
# --- list ---
L="$(bash "$S" list 2>&1)"
printf '%s' "$L" | grep -q 'vr1-edge' && ok || bad "list lost vr1-edge"
printf '%s' "$L" | grep -q 'os-qemu-guest-agent' && ok || bad "vr1-edge lost os-qemu-guest-agent"
printf '%s' "$L" | grep -q 'os-iperf' && ok || bad "vr1-edge lost os-iperf"
printf '%s' "$L" | grep -q 'metal-edge' && ok || bad "list lost metal-edge"
printf '%s' "$L" | grep -qE 'microcode|os-nut|os-apcupsd' && ok || bad "metal-edge lost its site-specific note"
# --- dry-run apply vr1-edge (stdout=calls, stderr=warnings) ---
OUT="$(bash "$S" --dry-run apply vr1-edge 2>/tmp/opl_err.$$)"; rc=$?
ERR="$(cat /tmp/opl_err.$$ 2>/dev/null)"; rm -f /tmp/opl_err.$$
[ "$rc" -eq 0 ] && ok || bad "--dry-run apply vr1-edge -> nonzero ($rc)"
printf '%s' "$OUT" | grep -q 'core/firmware/install/os-qemu-guest-agent' && ok || bad "dry-run missing qga install call"
printf '%s' "$OUT" | grep -q 'core/firmware/install/os-iperf' && ok || bad "dry-run missing iperf install call"
# --- DISCIPLINE 1: one-at-a-time + poll. qga install BEFORE iperf, poll BETWEEN. ---
nq="$(printf '%s\n' "$OUT" | grep -n 'install/os-qemu-guest-agent' | head -1 | cut -d: -f1)"
np="$(printf '%s\n' "$OUT" | grep -n 'poll' | head -1 | cut -d: -f1)"
ni="$(printf '%s\n' "$OUT" | grep -n 'install/os-iperf' | head -1 | cut -d: -f1)"
if [ -n "$nq" ] && [ -n "$np" ] && [ -n "$ni" ] && [ "$nq" -lt "$np" ] && [ "$np" -lt "$ni" ]; then ok
else bad "not one-at-a-time: expected qga($nq) < poll($np) < iperf($ni)"; fi
grep -q 'upgradestatus' "$S" && ok || bad "source lost the upgradestatus poll (the firmware-lock fix)"
# --- DISCIPLINE 2: qemu-guest-agent honesty (channel/enable/restart caveat) ---
printf '%s' "$ERR" | grep -qiE 'channel|guest_agent' && ok || bad "no qga channel/enable warning emitted"
# --- unknown profile fails loud ---
bash "$S" --dry-run apply no-such-profile >/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "unknown profile must be nonzero"
# --- no baked secret; delegates auth to opnsense-api.sh ---
grep -qiE '^[^#]*secret[[:space:]]*=' "$S" && bad "script bakes a secret (must delegate to opnsense-api.sh)" || ok
grep -q 'opnsense-api.sh' "$S" && ok || bad "does not delegate HTTP to opnsense-api.sh"
# --- THE LIVE PATH (regression, 2026-07-20). The original `${DRY_RUN:+--dry-run}`
# expanded on DRY_RUN=0 (set-and-non-empty), so a REAL `apply` silently dry-ran
# while printing "OK: profile applied" -- a false success that survived because
# every test above drives --dry-run. These two exercise the LIVE branch with a
# stub api client, and assert the flag is passed if and only if it was asked for.
STUB="$(mktemp -d)"
cp "$S" "$STUB/opnsense-plugins.sh"
cat > "$STUB/opnsense-api.sh" <<'STUBEOF'
#!/usr/bin/env bash
# stub: records its own argv so the harness can see what the caller passed.
printf '%s\n' "ARGS:$*"
STUBEOF
chmod +x "$STUB/opnsense-api.sh"
LIVE="$(OPNSENSE_API_HOST=10.0.0.1 PLUGIN_POLL_MAX=1 PLUGIN_POLL_SLEEP=0 \
bash "$STUB/opnsense-plugins.sh" apply vr1-edge 2>/dev/null)"
printf '%s' "$LIVE" | grep -q 'ARGS:.*--dry-run' \
&& bad "LIVE apply passed --dry-run to the api client (the DRY_RUN:+ false-success bug is back)" || ok
printf '%s' "$LIVE" | grep -q 'ARGS:POST core/firmware/install/os-qemu-guest-agent' \
&& ok || bad "LIVE apply did not issue a real install POST"
DRYRUN="$(OPNSENSE_API_HOST=10.0.0.1 bash "$STUB/opnsense-plugins.sh" --dry-run apply vr1-edge 2>/dev/null)"
printf '%s' "$DRYRUN" | grep -q -- '--dry-run' \
&& ok || bad "--dry-run apply did NOT pass --dry-run through to the api client"
rm -rf "$STUB"
echo "opnsense-plugins: $pass passed, $fail failed"
[ "$fail" -eq 0 ]