#!/usr/bin/env bash
# tests/dc-util-hosts-import/run-tests.sh
#
# Harness for netbox/dc-util-hosts-import.py. OFFLINE -- touches no NetBox.
#
# Two layers (mirrors tests/dc-rack-mgmt-import):
# 1. STATIC greps that PIN the guards every sandbox-loop writer must have (dry-by-
# default, upstream-write gated IN CODE, WAF-safe UA, DERIVE-not-hardcode: no baked
# plane/host address literal) + the scope decision (.4 artifact is OUT).
# 2. test_logic.py drives the REAL main()/preflight against an in-memory fake NB
# (injected at get_nb()), sourcing lib-net/lib-hosts for real -- derivation, whole-
# plan preflight, range precondition, dns-collision, idempotency, refuse-upstream.
# ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
S="$(cd "$HERE/../../netbox" && pwd)/dc-util-hosts-import.py"
pass=0; fail=0
ok() { pass=$((pass+1)); }
bad() { fail=$((fail+1)); echo " FAIL: $1"; }
command -v python3 >/dev/null 2>&1 || { echo "FAIL: python3 required"; exit 1; }
python3 -c "import ast;ast.parse(open('$S').read())" 2>/dev/null && ok || bad "does not parse"
python3 "$S" --help >/dev/null 2>&1 && ok || bad "--help -> 0"
# no env -> fail loud, not guess a target
NETBOX_URL= NETBOX_TOKEN= python3 "$S" --site vr1-dc0 --host vr1-dc0-maas-01 \
>/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "missing NETBOX_URL/TOKEN must fail"
# both inputs REQUIRED (target DC + host never inferred)
NETBOX_URL=http://10.10.1.10:8000 NETBOX_TOKEN=x python3 "$S" --host vr1-dc0-maas-01 \
>/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "missing --site must fail (DC never inferred)"
NETBOX_URL=http://10.10.1.10:8000 NETBOX_TOKEN=x python3 "$S" --site vr1-dc0 \
>/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "missing --host must fail (host never inferred)"
grep -q 'default=os.environ.get("UTIL_SITE")' "$S" && ok || bad "lost the UTIL_SITE env fallback"
grep -q 'default=os.environ.get("UTIL_HOST")' "$S" && ok || bad "lost the UTIL_HOST env fallback"
# DERIVE, NOT HARDCODE -- no baked utility-host address literal (.4-.9 on any plane /22)
grep -qE '"?10\.12\.(4|8|64|68)\.[4-9]"?' "$S" && bad "a utility-host address literal is baked in -- must be DERIVED" || ok
grep -q 'lib-net.sh' "$S" && grep -q 'lib-hosts.sh' "$S" && ok || bad "lost the lib-net/lib-hosts derivation"
# the clean-zero guard: an empty/missing octet must die, never default
grep -q 'clean-zero' "$S" && ok || bad "lost the empty-octet (clean-zero) hard die"
# SCOPE: the .4 artifact host is explicitly OUT (docstring records the ruling boundary)
grep -q 'artifact' "$S" && ok || bad "lost the .4-artifact out-of-scope note"
# DRY BY DEFAULT
grep -q '"--commit", action="store_true"' "$S" && ok || bad "no --commit flag -- must be dry by default"
grep -q 'DRY RUN -- nothing will be written' "$S" && ok || bad "does not announce its dry run"
# UPSTREAM WRITE GATED IN CODE (not by discipline)
grep -q 'yes-write-upstream' "$S" && ok || bad "lost the --yes-write-upstream gate"
grep -q 'SANDBOX_HOSTS' "$S" && ok || bad "lost the SANDBOX_HOSTS gate"
out="$(NETBOX_URL=https://netbox.baldurkeep.com NETBOX_TOKEN=x python3 "$S" \
--site vr1-dc0 --host vr1-dc0-maas-01 --commit 2>&1)"
printf '%s' "$out" | grep -q "REFUSING to --commit" && ok \
|| bad "did NOT refuse a --commit to a non-sandbox host (would have written production)"
# WAF: UA-aware or every upstream call 403s (looks like auth failure)
grep -q 'User-Agent' "$S" && grep -q 'curl/8.5.0' "$S" && ok || bad "lost the WAF-safe User-Agent"
# RANGE precondition present (a host address must land in an existing D-134 range)
grep -q 'range_covering' "$S" && ok || bad "lost the utility-range precondition"
# ---- behavioral layer ----
python3 "$HERE/test_logic.py"
rc=$?
[ $rc -eq 0 ] && ok || bad "test_logic.py behavioral suite failed (rc=$rc)"
echo "run-tests(dc-util-hosts-import): $pass passed, $fail failed"
[ $fail -eq 0 ] || exit 1