#!/usr/bin/env bash
# tests/prereqs/run-tests.sh
# Harness for scripts/prereqs/* -- exercises the mode-parse / guard / --check /
# --dry-run paths only. It NEVER runs a real apt install (that needs root and
# would mutate the machine; same posture as the opnsense-* harnesses). ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PREQ="$(cd "$HERE/../../scripts/prereqs" && pwd)"
pass=0; fail=0
ok()  { pass=$((pass+1)); }
bad() { fail=$((fail+1)); echo "  FAIL: $1"; }
# t <desc> <expected-rc> <cmd...>
t() { local d="$1" exp="$2"; shift 2; "$@" >/dev/null 2>&1; local rc=$?; if [ "$rc" = "$exp" ]; then ok; else bad "$d (rc=$rc want=$exp)"; fi; }

INSTALLERS="install-opentofu install-virtualization install-jq install-image-tools install-apparmor-libvirt"

# 1. lib-prereq sourcing + mode parsing
. "$PREQ/lib-prereq.sh"
type pq_have >/dev/null 2>&1 && ok || bad "lib-prereq did not define pq_have"
pq_parse_mode --check   >/dev/null 2>&1; [ "${PQ_CHECK:-x}" = "1" ]  && ok || bad "parse --check set PQ_CHECK"
pq_parse_mode --dry-run >/dev/null 2>&1; [ "${PQ_DRYRUN:-x}" = "1" ] && ok || bad "parse --dry-run set PQ_DRYRUN"
pq_parse_mode --bogus   >/dev/null 2>&1; [ "$?" = "2" ]  && ok || bad "parse bad arg -> 2"
pq_parse_mode --help    >/dev/null 2>&1; [ "$?" = "10" ] && ok || bad "parse --help -> 10"

# 2. pq_run: dry-run must NOT execute; real mode must
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
PQ_DRYRUN=1 pq_run "sentinel" -- touch "$tmp/nope" >/dev/null 2>&1
[ ! -e "$tmp/nope" ] && ok || bad "pq_run dry-run executed the command"
PQ_DRYRUN=0 pq_run "sentinel" -- touch "$tmp/yes" >/dev/null 2>&1
[ -e "$tmp/yes" ] && ok || bad "pq_run real-mode did not execute"

# 3. each installer: --help=0, bad-arg=2, --dry-run=0 (never a real install)
for s in $INSTALLERS; do
  t "$s --help"    0 bash "$PREQ/$s.sh" --help
  t "$s bad-arg"   2 bash "$PREQ/$s.sh" --bogus
  t "$s --dry-run" 0 bash "$PREQ/$s.sh" --dry-run
done

# 4. orchestrator
t "install-all --help"    0 bash "$PREQ/install-all.sh" --help
t "install-all bad-arg"   2 bash "$PREQ/install-all.sh" --bogus
t "install-all --dry-run" 0 bash "$PREQ/install-all.sh" --dry-run

# 5. check-prereqs is read-only and exits 0 (all present) or 1 (something missing)
bash "$PREQ/check-prereqs.sh" >/dev/null 2>&1; rc=$?
{ [ "$rc" = "0" ] || [ "$rc" = "1" ]; } && ok || bad "check-prereqs rc=$rc (want 0|1)"

# 5b. each tool row appears at most once -- guards the line() return-value bug
#     (a helper whose last cmd is a failing test makes `A && line OK || line WARN`
#     print BOTH). Found on first dogfood run; fixed via line() return 0.
crep="$(bash "$PREQ/check-prereqs.sh" 2>&1)"
dupbad=0
for tool in tofu virsh jq qemu-img curl python3 sudo apparmor-libvirt; do
  n="$(printf '%s\n' "$crep" | grep -cE "\][ ]+${tool}[ ]+")"
  [ "$n" -le 1 ] || dupbad=1
done
[ "$dupbad" = "0" ] && ok || bad "check-prereqs printed a tool row more than once"

# 6. an absent tool's --dry-run must reach + print the dry-run plan (marker)
if pq_have jq; then
  ok   # jq present -> install-jq short-circuits at OK before the plan; marker not expected
else
  bash "$PREQ/install-jq.sh" --dry-run 2>&1 | grep -q '\[dry-run\]' && ok || bad "install-jq --dry-run lacked [dry-run] marker"
fi

# 7. install-apparmor-libvirt: the DETECTION is what has to be right -- a false OK
#    here means every VR1 VM defines-then-fails-to-start with a bare qemu
#    "Permission denied" and nothing naming AppArmor. Drive it off a pool parent
#    that is guaranteed absent from the profile, so the negative path is
#    deterministic regardless of what this host happens to have applied.
AAF="/etc/apparmor.d/local/abstractions/libvirt-qemu"
BOGUS="/var/lib/libvirt/zz-not-granted-$$"
if [ -d /etc/apparmor.d ]; then
  # 7a. --check must FAIL (rc 1) for a parent that was never granted
  VR1_POOL_PARENT="$BOGUS" bash "$PREQ/install-apparmor-libvirt.sh" --check >/dev/null 2>&1
  [ "$?" = "1" ] && ok || bad "apparmor --check returned OK for an ungranted pool parent (false OK)"

  # 7b. --dry-run must plan the fix but mutate NOTHING (the profile must not gain the rule)
  out="$(VR1_POOL_PARENT="$BOGUS" bash "$PREQ/install-apparmor-libvirt.sh" --dry-run 2>&1)"
  printf '%s\n' "$out" | grep -q '\[dry-run\]' && ok || bad "apparmor --dry-run printed no [dry-run] plan"
  if [ -r "$AAF" ] && grep -qF "$BOGUS" "$AAF"; then
    bad "apparmor --dry-run MUTATED $AAF (wrote the rule)"
  else
    ok
  fi

  # 7c. the rule it writes must be exactly the grant qemu needs
  printf '%s\n' "$out" | grep -qF "$BOGUS/** rwk," && ok || bad "apparmor plan lacks the '<parent>/** rwk,' grant"
else
  # no AppArmor on this host: the installer must report N/A and SUCCEED, not fail
  VR1_POOL_PARENT="$BOGUS" bash "$PREQ/install-apparmor-libvirt.sh" --check >/dev/null 2>&1
  [ "$?" = "0" ] && ok || bad "apparmor --check must exit 0 (N/A) on a non-AppArmor host"
  ok; ok; ok   # the three AppArmor-host assertions do not apply here
fi

echo
total=$((pass+fail))
if [ "$fail" -eq 0 ]; then echo "prereqs: $pass/$total PASS"; exit 0; fi
echo "prereqs: $fail/$total FAIL"; exit 1
