#!/usr/bin/env bash
# tests/dc-selector/run-tests.sh -- unit tests for the $DC selector
# convention (2026-07-09, tooling gap register #1): lib_net_select_dc() in
# scripts/lib-net.sh and lib_hosts_select_dc() in scripts/lib-hosts.sh.
#
# Asserts:
# - sourcing either file with no further action is unaffected (backward
# compatible by construction -- the flat vars populate exactly as before).
# - lib_net_select_dc: dc0/dc1 no-op (D-101: DC1 inherits DC0's v4 layout
# unchanged), dc2 fails loud, unknown token fails loud.
# - lib_hosts_select_dc: dc0 no-op, dc1 AND dc2 both fail loud (no real
# per-DC host enrollment exists yet for either -- this is the documented
# asymmetry vs. lib-net.sh's dc0|dc1 no-op).
# - Neither function ever silently invents/reuses a value across DCs.
set -uo pipefail
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NET="$SD/../../scripts/lib-net.sh"
HOSTSLIB="$SD/../../scripts/lib-hosts.sh"
P=0; F=0
ok(){ echo "PASS: $1"; P=$((P+1)); }
no(){ echo "FAIL: $1"; F=$((F+1)); }
chk(){ [ "$2" = "$3" ] && ok "$1" || no "$1 (got '$2' want '$3')"; }
# guard: both libs still refuse direct execution (unchanged by this addition)
( bash "$NET" >/dev/null 2>&1 ); chk "lib-net direct-exec-refused" "$?" 2
( bash "$HOSTSLIB" >/dev/null 2>&1 ); chk "lib-hosts direct-exec-refused" "$?" 2
# shellcheck source=/dev/null
. "$NET"
# shellcheck source=/dev/null
. "$HOSTSLIB"
# --- backward compatibility: sourcing alone still populates the flat vars ---
chk "PLANE_CIDRS unaffected" "${PLANE_CIDRS[0]}" "10.12.4.0/22"
chk "KEYSTONE_VIP_DEFAULT unaffected" "$KEYSTONE_VIP_DEFAULT" "10.12.4.50"
chk "HOSTS array unaffected" "${HOSTS[0]}" "openstack0"
chk "HOST_OCTET unaffected" "${HOST_OCTET[openstack0]}" "40"
# --- lib_net_select_dc (D-119 region-qualified): vr0-dc0/vr1-dc0 no-op,
# vr1-dc1 fails loud, RETIRED bare dcN fails loud, unknown fails loud ---
( lib_net_select_dc vr0-dc0 ); chk "net vr0-dc0 no-op rc" "$?" 0
( lib_net_select_dc vr1-dc0 ); chk "net vr1-dc0 no-op rc" "$?" 0
( lib_net_select_dc vr1-dc1 2>/dev/null ); chk "net vr1-dc1 fails-loud rc" "$?" 1
NET_ERR="$(lib_net_select_dc vr1-dc1 2>&1 1>/dev/null || true)"
grep -q "NetBox" <<<"$NET_ERR" && ok "net vr1-dc1 error cites NetBox gap" || no "net vr1-dc1 error cites NetBox gap"
( lib_net_select_dc bogus 2>/dev/null ); chk "net unknown-token fails-loud rc" "$?" 1
( lib_net_select_dc 2>/dev/null ); chk "net missing-arg fails-loud rc" "$?" 1
# D-119 REGRESSION GUARD: the bare dcN tokens are RETIRED and must be REJECTED.
# Accepting them "for compatibility" would preserve the exact cross-region
# ambiguity D-119 deletes -- 'dc0' meant VR0's LIVE cloud in lib-net.sh but VR1's
# FIRST DC in the NetBox importer. Silence here is how the off-by-one comes back.
for retired in dc0 dc1 dc2; do
( lib_net_select_dc "$retired" 2>/dev/null ); chk "net RETIRED '$retired' rejected (D-119)" "$?" 1
R_ERR="$(lib_net_select_dc "$retired" 2>&1 1>/dev/null || true)"
grep -q "RETIRED" <<<"$R_ERR" && ok "net '$retired' error says RETIRED" || no "net '$retired' error says RETIRED"
done
# no-op really means no-op: values identical after selecting vr0-dc0 or vr1-dc0.
# They match by INHERITANCE (D-101: VR1's first DC inherits VR0 DC0's v4 layout),
# not by coincidence -- and they are separate case arms so they can diverge later.
VR0_VAL="$(lib_net_select_dc vr0-dc0 >/dev/null 2>&1; echo "${PLANE_CIDRS[0]}")"
VR1_VAL="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${PLANE_CIDRS[0]}")"
chk "net vr0-dc0/vr1-dc0 identical values (D-101 inheritance)" "$VR0_VAL" "$VR1_VAL"
# --- lib_hosts_select_dc: vr0-dc0 no-op, BOTH VR1 DCs fail loud ---
( lib_hosts_select_dc vr0-dc0 ); chk "hosts vr0-dc0 no-op rc" "$?" 0
( lib_hosts_select_dc vr1-dc0 2>/dev/null ); chk "hosts vr1-dc0 fails-loud rc" "$?" 1
( lib_hosts_select_dc vr1-dc1 2>/dev/null ); chk "hosts vr1-dc1 fails-loud rc" "$?" 1
HOSTS_ERR1="$(lib_hosts_select_dc vr1-dc0 2>&1 1>/dev/null || true)"
grep -q "no enrolled hosts" <<<"$HOSTS_ERR1" && ok "hosts vr1-dc0 error cites no-enrollment" || no "hosts vr1-dc0 error cites no-enrollment"
HOSTS_ERR2="$(lib_hosts_select_dc vr1-dc1 2>&1 1>/dev/null || true)"
grep -q "no enrolled hosts" <<<"$HOSTS_ERR2" && ok "hosts vr1-dc1 error cites no-enrollment" || no "hosts vr1-dc1 error cites no-enrollment"
( lib_hosts_select_dc bogus 2>/dev/null ); chk "hosts unknown-token fails-loud rc" "$?" 1
( lib_hosts_select_dc 2>/dev/null ); chk "hosts missing-arg fails-loud rc" "$?" 1
for retired in dc0 dc1 dc2; do
( lib_hosts_select_dc "$retired" 2>/dev/null ); chk "hosts RETIRED '$retired' rejected (D-119)" "$?" 1
done
# --- the documented asymmetry: vr1-dc0 differs between the two libs ---
# net no-ops (D-101 says it inherits VR0 DC0's v4), hosts FAILS (it has no
# enrolled hosts yet). Intentional; asserted so nobody "fixes" one to match.
( lib_net_select_dc vr1-dc0 ); NET_RC=$?
( lib_hosts_select_dc vr1-dc0 2>/dev/null ); HOSTS_RC=$?
[ "$NET_RC" = 0 ] && [ "$HOSTS_RC" = 1 ] && ok "vr1-dc0 asymmetry: net no-ops, hosts fails (documented, not a bug)" || no "vr1-dc0 asymmetry broken (net=$NET_RC hosts=$HOSTS_RC)"
echo; [ "$F" = 0 ] && { echo "ALL PASS ($P checks)"; exit 0; } || { echo "FAILURES: $F"; exit 1; }