#!/bin/bash # Fixture-only self-test for node-facts.sh: runs the REAL main body against node-facts-test/ (fake .env, # compose files, canned HTTP answers via NODE_FACTS_FIXTURES, canned docker ps). No network, no docker. set -u cd "$(dirname "$0")" T=node-facts-test; fail=0 t0=$(date +%s%N) W=$(mktemp -d); cp -r $T/. $W/; cp node-facts.sh $W/node-facts.sh out=$(cd $W && NODE_FACTS_FIXTURES="$W/fixtures" timeout 30 ./node-facts.sh); rc=$?; rm -rf $W ms=$(( ($(date +%s%N) - t0) / 1000000 )) n=$(printf '%s\n' "$out" | grep -c '^{') [ $rc -eq 0 ] || { echo "FAIL: exit $rc"; fail=1; } [ "$n" -eq 3 ] || { echo "FAIL: expected 3 documents, got $n"; fail=1; } [ $ms -lt 10000 ] || { echo "FAIL: took ${ms}ms"; fail=1; } OUTF=$(mktemp); printf '%s\n' "$out" > "$OUTF" python3 - "$OUTF" <<'PY' || fail=1 import json,sys req=["schema","at","host","node_path","network","client","version","peers","schedule","signals","head","consensus","timing_ms"] docs=[json.loads(l) for l in open(sys.argv[1]) if l.startswith("{")] by={d["node_path"].split("/")[-1]:d for d in docs} for d in docs: miss=[k for k in req if k not in d]; assert not miss, f"{d['node_path']}: missing {miss}" a=by["avalanche-mainnet-go-pruned-leveldb"]; assert a["client"]["name"]=="avalanchego", a["client"] assert a["peers"]["count"]==3 and a["peers"]["newer_than_us"]==2, a["peers"] assert "helicon" in a["schedule"]["upgrades"] and a["signals"]["acps"]["194"]["supporters"]==2 o=by["op-mainnet-op-reth-pruned-trace--kona"]; assert o["client"]["consensus"]["name"]=="kona", o["client"] assert o["consensus"]["peers"]["count"]==3 and "karst" in o["consensus"]["schedule"]["upgrades"] and o["consensus"]["head"]["height"]==157836486 g=by["ethereum-mainnet-geth-pruned-pebble-path--nimbus"]; assert g["client"]["name"]=="geth" assert g["peers"]["count"]==4 and g["peers"]["newer_than_us"]==3, g["peers"] assert g["schedule"]["upgrades"]["prague"]["time"].startswith("2025-05-07") assert g["consensus"]["version"]["reported"].startswith("Nimbus") and g["consensus"]["peers"]["count"]==2 and "FULU" in g["consensus"]["schedule"]["upgrades"] and "FAR_FUTURE" not in g["consensus"]["schedule"]["upgrades"] assert g["head"]["height"]==0x18ec9c7 print("PASS: 3 documents, schema v1, avalanche/op-stack/geth+beacon assertions") PY rm -f "$OUTF" # fail-fast without .env d=$(mktemp -d); cp node-facts.sh $d/; t1=$(date +%s%N); (cd $d && timeout 10 ./node-facts.sh >/dev/null 2>&1); rc=$?; ms2=$(( ($(date +%s%N) - t1) / 1000000 )); rm -rf $d [ $rc -eq 2 ] && [ $ms2 -lt 2000 ] && echo "PASS: fail-fast exit 2 in ${ms2}ms without .env" || { echo "FAIL: fail-fast rc=$rc ${ms2}ms"; fail=1; } [ $fail -eq 0 ] && echo "SUCCESS (${ms}ms)" || { echo "FAILED"; exit 1; }