#!/usr/bin/env bash
# tests/phase-05/run-tests.sh -- offline regression for phase-05-octavia-verify.sh.
# Behavior-tests the REAL script against fake juju/openstack shims + real jq.
# No live infra. Needs python3, bash, jq.
set -euo pipefail
IFS=$'\n\t'
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPTS="$(cd "$HERE/../../scripts" && pwd)"
TARGET="$SCRIPTS/phase-05-octavia-verify.sh"
BIN="$HERE/fakebin"

command -v python3 >/dev/null 2>&1 || { echo "FAIL: python3 required" >&2; exit 1; }
command -v jq      >/dev/null 2>&1 || { echo "FAIL: jq required" >&2; exit 1; }
[ -f "$TARGET" ] || { echo "FAIL: target missing: $TARGET" >&2; exit 1; }
chmod +x "$BIN/juju" "$BIN/openstack" 2>/dev/null || true

WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
python3 "$HERE/make_fixtures.py" "$WORK" >/dev/null
UP_OHM0='o-hm0            UP             fc00:3f8c:7162:d105:f816:3eff:feea:7e45/64'
# R8a (LP#2018998): the verify compares o-hm0's MTU against lb-mgmt-net's. These are the
# AGREEING pair used by every case that is not specifically testing the mismatch.
LINK_OK='7: o-hm0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8942 qdisc noqueue state UNKNOWN'
MTU_OK='8942'
UNK_OHM0='o-hm0            UNKNOWN        fc00:7bab:3c0b:ae43:f816:3eff:fe2a:f741/64 fe80::d860:d1ff:fe6f:1822/64'  # the real-world value (OVS internal port)
rc_all=0

# run <want_rc> <summary_regex> <label> KEY=VAL ...
run() {
  local want="$1" wantsum="$2" label="$3"; shift 3
  local rc v
  set +e
  PATH="$BIN:$PATH" OS_AUTH_URL="http://fake:5000/v3" env "$@" bash "$TARGET" openstack >"$WORK/out" 2>&1
  rc=$?
  set -e
  v="$(grep -E '^Summary:' "$WORK/out" | head -1 || true)"
  if [ "$rc" -eq "$want" ] && printf '%s' "$v" | grep -qE "$wantsum"; then
    printf '  [OK]  %-44s exit %s | %s\n' "$label" "$rc" "$v"
  else
    printf '  [XX]  %-44s exit %s (want %s) | %s\n' "$label" "$rc" "$want" "$v"
    sed 's/^/        /' "$WORK/out"; rc_all=1
  fi
}

echo "=== phase-05-octavia-verify.sh (fake juju/openstack + real jq) ==="

# PRE: octavia blocked, resources empty -> PROCEED
run 0 'PROCEED' "pre: blocked + empty -> run 5.1" \
  FIX_JUJU_STATUS="$WORK/status-blocked.json" \
  FIX_NETS="$WORK/nets-empty" FIX_SUBS="$WORK/subs-empty" FIX_SGS="$WORK/sgs-empty" FIX_IMGS="$WORK/imgs-empty"

# POST 5.1 done, no amphora yet -> CONTROL-PLANE-DONE (real-world o-hm0 state = UNKNOWN)
run 0 'CONTROL-PLANE-DONE' "post: active + resources + o-hm0(UNKNOWN), no image" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UNK_OHM0" OHM0_LINK="$LINK_OK" NET_MTU="$MTU_OK" \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-empty"

# Full PASS: 5.1 + 5.2 done
run 0 'PASS' "post: active + resources + o-hm0 + amphora" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="$LINK_OK" NET_MTU="$MTU_OK" \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-active"

# config gate FAIL: amp-image-tag mismatch
run 1 'HOLD|FAIL' "config fail: amp-image-tag mismatch" \
  FIX_JUJU_STATUS="$WORK/status-blocked.json" CFG_OTAG="octavia-amphora-x" \
  FIX_NETS="$WORK/nets-empty" FIX_SUBS="$WORK/subs-empty" FIX_SGS="$WORK/sgs-empty" FIX_IMGS="$WORK/imgs-empty"

# config gate FAIL: image-format not raw
run 1 'HOLD|FAIL' "config fail: image-format qcow2" \
  FIX_JUJU_STATUS="$WORK/status-blocked.json" CFG_IMGFMT="qcow2" \
  FIX_NETS="$WORK/nets-empty" FIX_SUBS="$WORK/subs-empty" FIX_SGS="$WORK/sgs-empty" FIX_IMGS="$WORK/imgs-empty"

# POST FAIL: active but lb-mgmt resources missing
run 1 'HOLD|FAIL' "post fail: active but resources missing" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="$LINK_OK" NET_MTU="$MTU_OK" \
  FIX_NETS="$WORK/nets-empty" FIX_SUBS="$WORK/subs-empty" FIX_SGS="$WORK/sgs-empty" FIX_IMGS="$WORK/imgs-empty"

# POST FAIL: o-hm0 not up
run 1 'HOLD|FAIL' "post fail: o-hm0 down" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="o-hm0 DOWN" OHM0_LINK="$LINK_OK" NET_MTU="$MTU_OK" \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-empty"

# POST FAIL: o-hm0 absent (juju exec returned nothing)
run 1 'HOLD|FAIL' "post fail: o-hm0 absent" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="" \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-empty"

# PRE anomaly: blocked but resources already present
run 1 'HOLD|FAIL' "pre anomaly: blocked but resources exist" \
  FIX_JUJU_STATUS="$WORK/status-blocked.json" \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-empty"

# ---- R8a (RULED 2026-07-27): the LP#2018998 o-hm0 / lb-mgmt-net MTU comparison -------
# A jumbo lb-mgmt-net beside a 1500 o-hm0 SILENTLY drops health heartbeats >1500B and
# surfaces as SPURIOUS load-balancer failovers -- a symptom that reads as a Ceph, network
# or amphora fault long before anyone suspects MTU. Fix Released for our lineage, but
# recurring 2025-12-31 on octavia 14.0.0 / 2024.1 stable, the exact channel we pin.
# Proven able to fail in BOTH directions, and to REFUSE when it cannot read a value.
LINK_1500='7: o-hm0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN'

run 1 'HOLD|FAIL' "R8a: MTU mismatch (o-hm0 1500 vs net 8942)" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="$LINK_1500" NET_MTU=8942 \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-active"

run 1 'HOLD|FAIL' "R8a: INVERSE mismatch (o-hm0 8942 vs net 1500)" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="$LINK_OK" NET_MTU=1500 \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-active"

# agreement is the invariant, NOT jumbo: a matched 1500/1500 pair must PASS.
run 0 'PASS' "R8a: matched 1500/1500 passes (agreement, not jumbo)" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="$LINK_1500" NET_MTU=1500 \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-active"

# "could not look" is never "nothing there" -- an unreadable value REFUSES, never passes.
run 1 'HOLD|FAIL' "R8a: unreadable o-hm0 MTU REFUSES" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="" NET_MTU=8942 \
  FIX_NETS="$WORK/nets-present" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-active"

# ...and an unresolvable lb-mgmt-net must REFUSE rather than abort the script. The first
# draft of this check DID abort here: a bare $(...) under `set -e` + inherit_errexit
# killed the run mid-check, which this harness caught.
run 1 'HOLD|FAIL' "R8a: unresolvable lb-mgmt-net REFUSES, does not abort" \
  FIX_JUJU_STATUS="$WORK/status-active.json" OHM0_OUT="$UP_OHM0" OHM0_LINK="$LINK_OK" NET_MTU="" \
  FIX_NETS="$WORK/nets-empty" FIX_SUBS="$WORK/subs-present" FIX_SGS="$WORK/sgs-present" FIX_IMGS="$WORK/imgs-active"

echo
if [ "$rc_all" -eq 0 ]; then echo "ALL PASS"; else echo "SOME FAILED"; fi
exit "$rc_all"
