#!/usr/bin/env bash
# tests/opnsense-render-config/run-tests.sh -- offline harness for
# scripts/opnsense-render-config.sh. Unlike the other opnsense-* scripts,
# this one needs no external tool (pure bash + template substitution), so
# this harness tests the REAL behavior end-to-end, not just guard clauses.
# Exit: 0 all pass | 1 any case failed. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT="$HERE/../../scripts/opnsense-render-config.sh"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
base_env() {
export OPNSENSE_HOSTNAME="opnsense" DOMAIN="omega.dc1.vr1.cloud.neumatrix.local"
export ROOT_PASSWORD_HASH='$2y$10$fakehashfakehashfakehashfakehashfakehas'
export WAN_IF="vtnet0" WAN_IPADDR="203.0.113.2" WAN_SUBNET_BITS="30" WAN_GATEWAY="203.0.113.1"
export LAN_IF="vtnet1" LAN_IPADDR="10.12.8.1" LAN_SUBNET_BITS="22"
export MIRROR_SYNC_PROTOCOL="TCP" MIRROR_UPSTREAM_NET="10.12.5.10/32" MIRROR_SYNC_PORT="443"
unset ROUTE1_NETWORK ROUTE1_GATEWAY ROUTE1_DESCR ROUTE2_NETWORK ROUTE2_GATEWAY ROUTE2_DESCR
unset NTP_UPSTREAM_SERVERS NTP_PREFER_SERVER
}
run_ok() { # run_ok <label> <out-file>
local label="$1" out="$2" rc
bash "$SCRIPT" "$out" >/dev/null 2>&1; rc=$?
if [[ "$rc" == 0 ]] && [ -s "$out" ]; then
echo " PASS $label"; PASS=$((PASS+1))
else
echo " FAIL $label (rc=$rc)"; FAIL=$((FAIL+1))
fi
}
run_fail() { # run_fail <want_rc> <label> <out-file>
local want="$1" label="$2" out="$3" rc
bash "$SCRIPT" "$out" >/dev/null 2>&1; rc=$?
if [[ "$rc" == "$want" ]]; then
echo " PASS $label"; PASS=$((PASS+1))
else
echo " FAIL $label (rc=$rc want=$want)"; FAIL=$((FAIL+1))
fi
}
# T1: happy path, no optional routes -- renders, no leftover {{TOKEN}}, well-formed XML
base_env
run_ok "T1 happy path renders" "$TMP/t1.xml"
if ! grep -qE '\{\{[A-Z0-9_]+\}\}' "$TMP/t1.xml" 2>/dev/null; then
echo " PASS T1b no leftover {{TOKEN}} markers"; PASS=$((PASS+1))
else
echo " FAIL T1b leftover tokens found: $(grep -oE '\{\{[A-Z0-9_]+\}\}' "$TMP/t1.xml" | tr '\n' ' ')"; FAIL=$((FAIL+1))
fi
if python3 -c "import xml.dom.minidom,sys; xml.dom.minidom.parse(sys.argv[1])" "$TMP/t1.xml" >/dev/null 2>&1; then
echo " PASS T1c well-formed XML"; PASS=$((PASS+1))
else
echo " FAIL T1c not well-formed XML"; FAIL=$((FAIL+1))
fi
# T2: missing required var FAILS
base_env
unset OPNSENSE_HOSTNAME
run_fail 1 "T2 missing required var FAILS (rc 1)" "$TMP/t2.xml"
# T3: NTP defaults apply when unset (real OPNsense default, not invented)
base_env
bash "$SCRIPT" "$TMP/t3.xml" >/dev/null 2>&1
if grep -q "0.opnsense.pool.ntp.org" "$TMP/t3.xml" 2>/dev/null; then
echo " PASS T3 NTP default pool applied when unset"; PASS=$((PASS+1))
else
echo " FAIL T3 NTP default pool missing"; FAIL=$((FAIL+1))
fi
# T4: one static route renders correctly, second slot stays empty
base_env
export ROUTE1_NETWORK="fd00:dc2::/64" ROUTE1_GATEWAY="fd00:mesh::2" ROUTE1_DESCR="to DC2"
bash "$SCRIPT" "$TMP/t4.xml" >/dev/null 2>&1
if grep -q "fd00:dc2::/64" "$TMP/t4.xml" 2>/dev/null && grep -q "<staticroutes>" "$TMP/t4.xml" 2>/dev/null; then
echo " PASS T4 static route 1 renders"; PASS=$((PASS+1))
else
echo " FAIL T4 static route 1 missing from output"; FAIL=$((FAIL+1))
fi
# T5: route network set but gateway missing FAILS
base_env
export ROUTE1_NETWORK="10.0.0.0/24"
unset ROUTE1_GATEWAY
run_fail 1 "T5 route network without gateway FAILS (rc 1)" "$TMP/t5.xml"
# T6: missing output arg FAILS
base_env
out="$(bash "$SCRIPT" 2>&1)"; rc=$?
if [[ "$rc" == 1 ]] && grep -q "usage: opnsense-render-config.sh" <<<"$out"; then
echo " PASS T6 missing output arg FAILS (rc 1)"; PASS=$((PASS+1))
else
echo " FAIL T6 missing output arg (rc=$rc)"; FAIL=$((FAIL+1))
fi
echo; echo "RESULT: PASS=$PASS FAIL=$FAIL"
[[ "$FAIL" -eq 0 ]] && { echo "ALL PASS"; exit 0; } || exit 1