#!/usr/bin/env bash
# tests/site-tailscale/run-tests.sh -- offline harness for scripts/site-tailscale.sh.
# Fakes `tailscale` (status --json / up / version), `ip`, `sysctl` (forwarding), `dpkg` (prep
# install); real python3 for jget. NO network, mutates nothing outside mktemp. Proves each exit
# path can FIRE (a new assertion is decoration until it has a failing-direction fixture):
# prep-happy -> 0 (pkg present + forwarding on)
# prep-fromdeb -> 0 (pkg absent, $TS_DEB staged, dpkg installs it, forwarding on)
# prep-noforward -> 4 (forwarding sysctl reads 0 -- the load-bearing subnet-router property)
# prep-nopkg-nodeb-> 4 (pkg absent and no $TS_DEB staged)
# check-happy -> 0 (Running + route approved + forwarding + untagged + .7 leg)
# check-notrun -> 1 (BackendState != Running)
# check-noroute -> 1 (metal-admin CIDR not in approved PrimaryRoutes)
# check-noforward -> 1 (IP forwarding off -- green everywhere else, forwards nothing)
# check-noleg -> 1 (no .7 metal-admin leg on the host)
# check-tag-happy -> 0 (TS_TAG set + node tagged)
# check-tag-notag -> 1 (TS_TAG set but node NOT tagged)
# install-happy -> 0 (untagged up ok + check green)
# install-tag-happy-> 0 (tagged up ok + check green)
# install-nokey -> 4 (no pre-auth key)
# install-noTS -> 4 (tailscale binary absent -> "run prep first")
# install-upfail -> 4 (tailscale up rejected)
# badmode/badsite/noargs -> 2
# dc1 resolves its own CIDR (10.12.68.0/22), not dc0's
# site-table rows carry MEASURED tags (hard rule 2)
# Exit: 0 all pass | 1 any case failed. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
SUT="$REPO/scripts/site-tailscale.sh"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
BIN="$TMP/bin"; mkdir -p "$BIN" "$TMP/sysctl.d"
printf 'fake-deb-content\n' > "$TMP/fake.deb"
# ---- fake tailscale --------------------------------------------------------
cat > "$BIN/tailscale" <<'FB'
#!/usr/bin/env bash
mode="${TS_FAKE_MODE:-happy}"; cidr="${TS_FAKE_CIDR:-10.12.8.0/22}"
case "$1" in
version) echo "1.98.9"; exit 0;;
up) [ "$mode" = upfail ] && exit 1; exit 0;;
status)
# $2 == --json
st='"Running"'; routes="[\"$cidr\"]"
tags='[]'; [ -n "${TS_TAG:-}" ] && tags="[\"$TS_TAG\"]"
case "$mode" in
notrun) st='"Stopped"';;
noroute) routes='[]';;
notag) tags='[]';;
esac
printf '{"BackendState":%s,"Self":{"PrimaryRoutes":%s,"Tags":%s}}\n' "$st" "$routes" "$tags"
exit 0;;
*) exit 0;;
esac
FB
chmod +x "$BIN/tailscale"
# ---- fake ip (own_leg_matches) --------------------------------------------
cat > "$BIN/ip" <<'FB'
#!/usr/bin/env bash
# emit a .7 metal-admin leg unless TS_FAKE_MODE=noleg
[ "${TS_FAKE_MODE:-}" = noleg ] && { echo "1: lo inet 127.0.0.1/8 scope host lo"; exit 0; }
echo "2: enp1s0 inet ${TS_FAKE_LEG:-10.12.8.7}/22 scope global enp1s0"
exit 0
FB
chmod +x "$BIN/ip"
# ---- fake sysctl (forwarding) ---------------------------------------------
cat > "$BIN/sysctl" <<'FB'
#!/usr/bin/env bash
fwd="${TS_FAKE_FWD:-1}"
case "$*" in
"--system") exit 0;;
"-n net.ipv4.ip_forward") echo "$fwd"; exit 0;;
"-n net.ipv6.conf.all.forwarding") echo "$fwd"; exit 0;;
*) exit 0;;
esac
FB
chmod +x "$BIN/sysctl"
# ---- fake dpkg (prep install-from-deb) ------------------------------------
# "installs" by copying the fake tailscale to $TS_INSTALL_TARGET so command -v succeeds after.
cat > "$BIN/dpkg" <<FB
#!/usr/bin/env bash
if [ "\$1" = "-i" ] && [ -n "\${TS_INSTALL_TARGET:-}" ]; then
cp "$BIN/tailscale" "\$TS_INSTALL_TARGET" && chmod +x "\$TS_INSTALL_TARGET"; exit 0
fi
exit 0
FB
chmod +x "$BIN/dpkg"
run(){ # <name> <expected-exit> <mode> <args...>
local name="$1" exp="$2" mode="$3"; shift 3
local out code
out="$(PATH="$BIN:$PATH" \
TAILSCALE="${CASE_TS:-$BIN/tailscale}" \
SYSCTL="$BIN/sysctl" DPKG="$BIN/dpkg" APTGET="/bin/false" \
TS_SYSCTL_D="$TMP/sysctl.d" \
TS_FAKE_MODE="$mode" TS_FAKE_FWD="${CASE_FWD:-1}" \
TS_FAKE_CIDR="${CASE_CIDR:-10.12.8.0/22}" TS_FAKE_LEG="${CASE_LEG:-10.12.8.7}" \
TS_TAG="${CASE_TAG-}" TS_AUTHKEY="${CASE_AUTHKEY:-}" \
TS_DEB="${CASE_DEB:-}" TS_INSTALL_TARGET="${CASE_TS:-}" \
bash "$SUT" "$@" 2>&1)"; code=$?
if [ "$code" -eq "$exp" ]; then PASS=$((PASS+1)); echo "PASS: $name (exit $code)"
else FAIL=$((FAIL+1)); echo "FAIL: $name (got $code want $exp)"; echo "$out" | sed 's/^/ /'; fi
}
echo "=== site-tailscale harness ==="
# prep cases
run prep-happy 0 happy prep vr1-dc0
CASE_TS="$BIN/staged-ts" CASE_DEB="$TMP/fake.deb" run prep-fromdeb 0 happy prep vr1-dc0
CASE_FWD=0 run prep-noforward 4 happy prep vr1-dc0
CASE_TS="$BIN/nope" run prep-nopkg-nodeb 4 happy prep vr1-dc0
# check cases (default untagged)
run check-happy 0 happy check vr1-dc0
run check-notrun 1 notrun check vr1-dc0
run check-noroute 1 noroute check vr1-dc0
CASE_FWD=0 run check-noforward 1 happy check vr1-dc0
run check-noleg 1 noleg check vr1-dc0
# tagged mode (opt-in via TS_TAG)
CASE_TAG="tag:subnet-router" run check-tag-happy 0 happy check vr1-dc0
CASE_TAG="tag:subnet-router" run check-tag-notag 1 notag check vr1-dc0
# install cases
CASE_AUTHKEY="tskey-fake" run install-happy 0 happy install vr1-dc0
CASE_TAG="tag:subnet-router" CASE_AUTHKEY="tskey-fake" run install-tag-happy 0 happy install vr1-dc0
run install-nokey 4 happy install vr1-dc0
CASE_AUTHKEY="tskey-fake" run install-upfail 4 upfail install vr1-dc0
CASE_TS="$BIN/nope" CASE_AUTHKEY="tskey-fake" run install-noTS 4 happy install vr1-dc0
# arg/site validation
run badmode 2 happy frobnicate vr1-dc0
run badsite 2 happy check vr1-dc9
run noargs 2 happy
# dc1 resolves its OWN cidr (not dc0's) -- run check-happy with dc1 fixture
CASE_CIDR='10.12.68.0/22' CASE_LEG='10.12.68.7' run dc1-cidr 0 happy check vr1-dc1
out="$(PATH="$BIN:$PATH" TAILSCALE="$BIN/tailscale" SYSCTL="$BIN/sysctl" TS_SYSCTL_D="$TMP/sysctl.d" \
TS_FAKE_MODE=happy TS_FAKE_CIDR='10.12.68.0/22' TS_FAKE_LEG='10.12.68.7' \
bash "$SUT" check vr1-dc1 2>&1)"
if printf '%s' "$out" | grep -q '10.12.68.0/22'; then PASS=$((PASS+1)); echo "PASS: dc1 emits 10.12.68.0/22"
else FAIL=$((FAIL+1)); echo "FAIL: dc1 site cidr"; echo "$out" | sed 's/^/ /'; fi
# site-table rows must carry MEASURED tags (hard rule 2)
for s in 'vr1-dc0.*MEASURED' 'vr1-dc1.*MEASURED'; do
if grep -qE "$s" "$SUT"; then PASS=$((PASS+1)); echo "PASS: site row tagged MEASURED ($s)"
else FAIL=$((FAIL+1)); echo "FAIL: site row missing MEASURED ($s)"; fi
done
echo "=== $PASS pass / $FAIL fail ==="
[ "$FAIL" -eq 0 ]