#!/usr/bin/env bash
# Behavior regression for carve-host-interfaces.sh -- Pattern A (D-060).
# Fake `maas` (read-only fixtures) + real jq. Drives the carve in DRY-RUN for
# openstack0 (octet .40) and asserts the emitted WOULD-actions and absences.
# Rewritten 2026-07-03 from the retired D-057 provider-vip expectations
# (harness must travel with its script's scheme).
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CARVE="$(cd "$HERE/../../scripts" && pwd)/carve-host-interfaces.sh"
BIN="$HERE/fakebin"; FIX="$HERE/fix"
chmod +x "$BIN"/* 2>/dev/null || true # GitHub Desktop lands files mode 100644
command -v jq >/dev/null || { echo "FAIL: jq required"; exit 1; }
python3 "$HERE/make_fixtures.py" >/dev/null
rc_all=0; OUT="$(mktemp)"
run() { # <want_exit> <label> <subnets> <ifaces> [dc_value]
local want="$1" label="$2" subs="$3" ifs="$4" dcval="${5:-}"
DC="$dcval" PATH="$BIN:$PATH" FIX_MACHINES="$FIX/machines.json" FIX_MACHINE="$FIX/machine.json" \
FIX_SUBNETS="$subs" FIX_IFACES="$ifs" \
bash "$CARVE" openstack0 >"$OUT" 2>&1
local rc=$?
if [ "$rc" -ne "$want" ]; then
printf ' [XX] %-40s exit %s (want %s)\n' "$label" "$rc" "$want"; sed 's/^/ /' "$OUT"; rc_all=1; return
fi
printf ' [ok] %-40s exit %s\n' "$label" "$rc"
}
# run_dc: like run(), but no fixtures needed -- used for $DC values expected to
# fail loud at the selector layer, BEFORE the script ever touches need_jq/MAAS.
run_dc() { # <want_exit> <label> <dc_value>
local want="$1" label="$2" dcval="$3"
DC="$dcval" bash "$CARVE" openstack0 >"$OUT" 2>&1
local rc=$?
if [ "$rc" -ne "$want" ]; then
printf ' [XX] %-40s exit %s (want %s)\n' "$label" "$rc" "$want"; sed 's/^/ /' "$OUT"; rc_all=1; return
fi
printf ' [ok] %-40s exit %s\n' "$label" "$rc"
}
has() { grep -qE "$1" "$OUT" || { printf ' MISS expected /%s/\n' "$1"; rc_all=1; }; }
absent(){ grep -qE "$1" "$OUT" && { printf ' LEAK forbidden /%s/\n' "$1"; rc_all=1; } || true; }
count() { local n; n=$(grep -cE "$1" "$OUT"); [ "$n" -eq "$2" ] || { printf ' COUNT /%s/ = %s (want %s)\n' "$1" "$n" "$2"; rc_all=1; }; }
echo "=== carve-host-interfaces.sh -- Pattern A carve (fake maas + real jq) ==="
run 0 "fresh host: full Pattern-A plan" "$FIX/sub_ok.json" "$FIX/if_fresh.json"
has 'WOULD: unlink enp1s0\(id=100\) commissioning link id=900' # L3 leaves the uplink (D-057 root cause)
has 'WOULD: create br-ex \(OVS\) parent=enp1s0\(id=100\)'
has 'STATIC 10\.12\.4\.40 on subnet 1' # br-ex carries the provider static
has 'WOULD: create br-metal \(standard\) parent=enp7s0\(id=101\)'
has 'STATIC 10\.12\.8\.40 on subnet 2'
has 'create br-metal\.103 \(VID 103, vlan obj 5003\)'
has 'WOULD: create br-internal \(standard\)'
has 'STATIC 10\.12\.12\.40 on subnet 3'
has 'enp8s0\(id=102\) -> VLAN 5004 \(10\.12\.16\.0/22\)' # vlan resolved by CIDR, not hardcoded
has 'STATIC 10\.12\.16\.40 on subnet 4'
has 'STATIC 10\.12\.32\.40 on subnet 5'
has 'STATIC 10\.12\.36\.40 on subnet 6'
has 'no action on enp11s0'
has 'Summary: 0 fatal'
absent 'br-prov-api' # the D-057 world must never reappear
absent 'enp1s0\.104'
absent 'provider-vip'
run 1 "missing data-tenant subnet -> hard gate" "$FIX/sub_missing.json" "$FIX/if_fresh.json"
has 'no MAAS subnet for 10\.12\.16\.0/22'
run 1 "metal-internal on wrong VID -> hard gate" "$FIX/sub_wrongvid.json" "$FIX/if_fresh.json"
has "metal-internal 10\.12\.12\.0/22 is VID '99', expected 103"
run 0 "idempotent: Pattern A complete -> all SKIP" "$FIX/sub_ok.json" "$FIX/if_done.json"
count 'WOULD:' 0
has 'br-ex exists -- SKIP create'
has 'br-ex already on 10\.12\.4\.0/22 -- SKIP'
has 'br-metal exists -- SKIP create'
has 'br-internal already on 10\.12\.12\.0/22 -- SKIP'
has 'enp8s0 already STATIC on 10\.12\.16\.0/22 -- SKIP'
has 'Summary: 0 fatal'
echo
echo "=== \$DC selector wiring (DOCFIX-166; tooling gap register #15 sub-item 2) ==="
run 0 "DC=dc0 explicit: identical no-op full carve plan" "$FIX/sub_ok.json" "$FIX/if_fresh.json" dc0
has 'WOULD: create br-ex \(OVS\) parent=enp1s0\(id=100\)'
has 'Summary: 0 fatal'
run_dc 1 "DC=dc1: hosts selector fails loud (no per-DC host data)" dc1
has 'no enrolled hosts yet'
absent 'DO:|WOULD:' # must exit before any MAAS interaction
run_dc 1 "DC=dc2: net selector fails loud first (NetBox gap)" dc2
has 'no assigned network literals yet'
run_dc 1 "DC=bogus: unknown token fails loud" bogus
has "unknown DC 'bogus'"
echo
[ "$rc_all" -eq 0 ] && echo "ALL PASS" || echo "SOME FAILED"
rm -f "$OUT"; exit "$rc_all"