#!/usr/bin/env bash
# tests/d124-transit-seed/run-tests.sh
#
# Harness for netbox/d124-transit-seed.py. OFFLINE -- touches no NetBox.
# STATIC greps that PIN the D-124 transit values + the guards every sandbox-loop
# writer must have (dry-by-default, upstream-write gated IN CODE, WAF-safe UA,
# fail-loud on missing env). PLUS a cross-file consistency check: the role slug +
# container this SEEDS must equal what dc-rack-mgmt-import.py REQUIRES, or that
# importer's die-if-absent preconditions would still fail after a seed. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
S="$(cd "$HERE/../../netbox" && pwd)/d124-transit-seed.py"
R="$(cd "$HERE/../../netbox" && pwd)/dc-rack-mgmt-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 -> must fail loud, not guess a target
NETBOX_URL= NETBOX_TOKEN= python3 "$S" >/dev/null 2>&1; [ $? -ne 0 ] && ok || bad "missing NETBOX_URL/TOKEN must fail"
# 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" --commit 2>&1)"
printf '%s' "$out" | grep -q "REFUSING to --commit" && ok \
|| bad "did NOT refuse a --commit to a non-sandbox host (it would have written to 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"
# THE D-124 TRANSIT VALUES (operator-pinned)
grep -qF 'ROLE = {"slug": "transit"' "$S" && ok || bad "transit role slug changed"
grep -qF 'CONTAINER = "172.31.0.0/24"' "$S" && ok || bad "transit container is not 172.31.0.0/24 (D-124)"
grep -q '"status": "container"' "$S" && ok || bad "the supernet is not created as a container prefix"
# CROSS-FILE CONSISTENCY -- what we SEED must equal what dc-rack-mgmt-import REQUIRES,
# or the seed is useless (that importer would still die on role/container absent).
grep -qF 'ROLE_SLUG = "transit"' "$R" && ok || bad "dc-rack-mgmt ROLE_SLUG != 'transit' -- seed would not satisfy it"
grep -qF 'CONTAINER = "172.31.0.0/24"' "$R" && ok || bad "dc-rack-mgmt CONTAINER != 172.31.0.0/24 -- seed would not satisfy it"
# SCOPE: the container is UNSCOPED (shared across DCs) -- it must NOT bind a site.
# (The per-DC /30 is dc-rack-mgmt-import's job + IS site-scoped; this tool is not.)
grep -q 'scope_type' "$S" && bad "the container must be UNSCOPED (no scope_type) -- per-DC /30 scoping is dc-rack-mgmt's job" || ok
# This tool seeds ONLY the role + container -- NOT a transit /30, NOT the site.
grep -qE 'create\("dcim/sites"' "$S" && bad "this tool CREATES a site -- the site is a precondition" || ok
grep -qE '/30|/31|transit.*/3[01]|--transit-cidr' "$S" && bad "this tool references a transit /30 -- that is dc-rack-mgmt's job, not the seeder's" || ok
grep -q 'create("ipam/roles"' "$S" && ok || bad "does not create the transit role (its job)"
grep -q 'create("ipam/prefixes"' "$S" && ok || bad "does not create the container prefix (its job)"
# ASCII + LF
LC_ALL=C grep -qP '[^\x00-\x7F]' "$S" && bad "non-ASCII byte in script" || ok
grep -qU $'\r' "$S" && bad "CR byte (must be LF)" || ok
echo
total=$((pass+fail))
if [ "$fail" -eq 0 ]; then echo "d124-transit-seed: $pass/$total PASS"; exit 0; fi
echo "d124-transit-seed: $fail/$total FAIL"; exit 1