- Prevent 'nx9-wg version' from creating data directories by avoiding database initialization. - Create parent directories when an explicit --database path is provided. - Redact printed generated administrator passwords; announce file path or redact instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1333 lines
28 KiB
Bash
Executable File
1333 lines
28 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# ============================================================================
|
|
# nx9-wg — Comprehensive CLI Verification Script
|
|
# ============================================================================
|
|
#
|
|
# PURPOSE
|
|
# -------
|
|
# Comprehensive operator-facing verification of the nx9-wg CLI.
|
|
#
|
|
# This script checks:
|
|
#
|
|
# 01. Binary/version
|
|
# 02. Top-level CLI commands
|
|
# 03. Subcommand help
|
|
# 04. Administrator initialization
|
|
# 05. System
|
|
# 06. Administrator/authentication
|
|
# 07. Client profiles / MTU
|
|
# 08. Networks
|
|
# 09. WireGuard interfaces
|
|
# 10. Peers
|
|
# 11. Routes
|
|
# 12. Firewall
|
|
# 13. NAT
|
|
# 14. Forwarding
|
|
# 15. Reconciliation
|
|
# 16. Backup
|
|
# 17. Audit
|
|
# 18. Live kernel state
|
|
# 19. Diagnostics
|
|
# 20. Output format matrix
|
|
# 21. Global CLI options
|
|
# 22. NX9_WG_* environment namespace
|
|
# 23. Explicit database paths
|
|
# 24. Data-directory resolution
|
|
# 25. Source-level architectural safety
|
|
# 26. Final summary
|
|
#
|
|
# SAFETY
|
|
# ------
|
|
# Default mode is SAFE.
|
|
#
|
|
# It uses:
|
|
#
|
|
# /tmp/nx9-wg-cli-test-XXXXXX
|
|
#
|
|
# and never modifies the host WireGuard/network configuration.
|
|
#
|
|
# LIVE=1 enables read-only live Linux inspection commands.
|
|
#
|
|
# Example:
|
|
#
|
|
# ./scripts/test-cli-comprehensive.sh
|
|
#
|
|
# LIVE=1 ./scripts/test-cli-comprehensive.sh
|
|
#
|
|
# BIN=./target/release/nx9-wg ./scripts/test-cli-comprehensive.sh
|
|
#
|
|
# IMPORTANT
|
|
# ---------
|
|
# Do NOT run this with:
|
|
#
|
|
# source scripts/test-cli-comprehensive.sh
|
|
#
|
|
# or:
|
|
#
|
|
# . scripts/test-cli-comprehensive.sh
|
|
#
|
|
# Run it as a program:
|
|
#
|
|
# bash scripts/test-cli-comprehensive.sh
|
|
#
|
|
# or:
|
|
#
|
|
# ./scripts/test-cli-comprehensive.sh
|
|
#
|
|
# ============================================================================
|
|
|
|
set -uo pipefail
|
|
|
|
###############################################################################
|
|
# Configuration
|
|
###############################################################################
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
LIVE="${LIVE:-0}"
|
|
TIMEOUT="${TIMEOUT:-20}"
|
|
|
|
BIN="${BIN:-}"
|
|
|
|
if [[ -z "$BIN" ]]; then
|
|
if [[ -x "$ROOT/target/debug/nx9-wg" ]]; then
|
|
BIN="$ROOT/target/debug/nx9-wg"
|
|
elif [[ -x "$ROOT/target/release/nx9-wg" ]]; then
|
|
BIN="$ROOT/target/release/nx9-wg"
|
|
else
|
|
echo
|
|
echo "ERROR: nx9-wg binary not found."
|
|
echo
|
|
echo "Build with:"
|
|
echo " cargo build"
|
|
echo
|
|
echo "or:"
|
|
echo " cargo build --release"
|
|
echo
|
|
echo "Then run this script again."
|
|
echo
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [[ ! -x "$BIN" ]]; then
|
|
echo
|
|
echo "ERROR: binary is not executable:"
|
|
echo " $BIN"
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
###############################################################################
|
|
# Temporary isolated test environment
|
|
###############################################################################
|
|
|
|
TEST_ROOT="$(mktemp -d /tmp/nx9-wg-cli-test.XXXXXX)"
|
|
DATA_DIR="$TEST_ROOT/data"
|
|
DB="$DATA_DIR/nx9-wg.db"
|
|
PASSWORD_FILE="$TEST_ROOT/admin-password"
|
|
|
|
mkdir -p "$DATA_DIR"
|
|
|
|
###############################################################################
|
|
# Counters
|
|
###############################################################################
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
SKIP=0
|
|
TOTAL=0
|
|
|
|
###############################################################################
|
|
# State
|
|
###############################################################################
|
|
|
|
KEEP_TEST_DATA=0
|
|
|
|
###############################################################################
|
|
# Formatting
|
|
###############################################################################
|
|
|
|
BOLD=""
|
|
DIM=""
|
|
GREEN=""
|
|
RED=""
|
|
YELLOW=""
|
|
CYAN=""
|
|
RESET=""
|
|
|
|
if [[ -t 1 ]]; then
|
|
BOLD=$'\033[1m'
|
|
DIM=$'\033[2m'
|
|
GREEN=$'\033[32m'
|
|
RED=$'\033[31m'
|
|
YELLOW=$'\033[33m'
|
|
CYAN=$'\033[36m'
|
|
RESET=$'\033[0m'
|
|
fi
|
|
|
|
###############################################################################
|
|
# Cleanup
|
|
###############################################################################
|
|
|
|
cleanup() {
|
|
local rc=$?
|
|
|
|
echo
|
|
echo "============================================================================"
|
|
echo " nx9-wg CLI verification finished"
|
|
echo "============================================================================"
|
|
echo
|
|
|
|
if [[ "$FAIL" -eq 0 && "$rc" -eq 0 ]]; then
|
|
echo "${GREEN}RESULT: PASS${RESET}"
|
|
echo
|
|
echo " PASS : $PASS"
|
|
echo " FAIL : $FAIL"
|
|
echo " SKIP : $SKIP"
|
|
echo " TOTAL: $TOTAL"
|
|
echo
|
|
|
|
echo "Temporary test data:"
|
|
echo " $TEST_ROOT"
|
|
echo
|
|
|
|
echo "Removing temporary test data..."
|
|
rm -rf "$TEST_ROOT"
|
|
else
|
|
echo "${RED}RESULT: FAIL${RESET}"
|
|
echo
|
|
echo " PASS : $PASS"
|
|
echo " FAIL : $FAIL"
|
|
echo " SKIP : $SKIP"
|
|
echo " TOTAL: $TOTAL"
|
|
echo
|
|
|
|
KEEP_TEST_DATA=1
|
|
|
|
echo "${YELLOW}Temporary test data PRESERVED for investigation:${RESET}"
|
|
echo " $TEST_ROOT"
|
|
echo
|
|
echo "Database:"
|
|
echo " $DB"
|
|
echo
|
|
echo "Password file:"
|
|
echo " $PASSWORD_FILE"
|
|
echo
|
|
|
|
echo "To inspect:"
|
|
echo " ls -la \"$TEST_ROOT\""
|
|
echo " ls -la \"$DATA_DIR\""
|
|
echo
|
|
fi
|
|
|
|
echo
|
|
return "$rc"
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
###############################################################################
|
|
# Reporting functions
|
|
###############################################################################
|
|
|
|
section() {
|
|
echo
|
|
echo "============================================================================"
|
|
echo " $1"
|
|
echo "============================================================================"
|
|
}
|
|
|
|
info() {
|
|
echo " ${CYAN}[INFO]${RESET} $1"
|
|
}
|
|
|
|
pass() {
|
|
PASS=$((PASS + 1))
|
|
TOTAL=$((TOTAL + 1))
|
|
echo " ${GREEN}[PASS]${RESET} $1"
|
|
}
|
|
|
|
fail() {
|
|
FAIL=$((FAIL + 1))
|
|
TOTAL=$((TOTAL + 1))
|
|
echo " ${RED}[FAIL]${RESET} $1"
|
|
}
|
|
|
|
skip() {
|
|
SKIP=$((SKIP + 1))
|
|
TOTAL=$((TOTAL + 1))
|
|
echo " ${YELLOW}[SKIP]${RESET} $1"
|
|
}
|
|
|
|
###############################################################################
|
|
# Generic command execution
|
|
###############################################################################
|
|
|
|
run_test() {
|
|
local description="$1"
|
|
shift
|
|
|
|
echo
|
|
echo " ${BOLD}>>> $description${RESET}"
|
|
echo " $BIN $*"
|
|
|
|
timeout "$TIMEOUT" "$BIN" "$@" >/tmp/nx9-wg-cli-command.out 2>/tmp/nx9-wg-cli-command.err
|
|
local rc=$?
|
|
|
|
if [[ "$rc" -eq 0 ]]; then
|
|
pass "$description"
|
|
cat /tmp/nx9-wg-cli-command.out
|
|
return 0
|
|
fi
|
|
|
|
fail "$description (exit=$rc)"
|
|
|
|
if [[ -s /tmp/nx9-wg-cli-command.out ]]; then
|
|
echo " --- stdout ---"
|
|
sed 's/^/ /' /tmp/nx9-wg-cli-command.out
|
|
fi
|
|
|
|
if [[ -s /tmp/nx9-wg-cli-command.err ]]; then
|
|
echo " --- stderr ---"
|
|
sed 's/^/ /' /tmp/nx9-wg-cli-command.err
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
run_quiet() {
|
|
local description="$1"
|
|
shift
|
|
|
|
timeout "$TIMEOUT" "$BIN" "$@" >/dev/null 2>&1
|
|
local rc=$?
|
|
|
|
if [[ "$rc" -eq 0 ]]; then
|
|
pass "$description"
|
|
else
|
|
fail "$description (exit=$rc)"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
###############################################################################
|
|
# Safe assertion helpers
|
|
###############################################################################
|
|
|
|
assert_file() {
|
|
local description="$1"
|
|
local file="$2"
|
|
|
|
if [[ -f "$file" ]]; then
|
|
pass "$description"
|
|
else
|
|
fail "$description"
|
|
fi
|
|
}
|
|
|
|
assert_nonempty_file() {
|
|
local description="$1"
|
|
local file="$2"
|
|
|
|
if [[ -s "$file" ]]; then
|
|
pass "$description"
|
|
else
|
|
fail "$description"
|
|
fi
|
|
}
|
|
|
|
###############################################################################
|
|
# CLI context
|
|
#
|
|
# Every runtime database operation in this script is isolated.
|
|
###############################################################################
|
|
|
|
CLI=(
|
|
--data-dir "$DATA_DIR"
|
|
--database "$DB"
|
|
)
|
|
|
|
###############################################################################
|
|
# Wrapper
|
|
###############################################################################
|
|
|
|
nx() {
|
|
"$BIN" "${CLI[@]}" "$@"
|
|
}
|
|
|
|
###############################################################################
|
|
# 01 — Binary
|
|
###############################################################################
|
|
|
|
section "01 — Binary and Version"
|
|
|
|
run_test \
|
|
"Version" \
|
|
version
|
|
|
|
run_test \
|
|
"Version JSON" \
|
|
--format json \
|
|
version
|
|
|
|
###############################################################################
|
|
# 02 — Top-level help
|
|
###############################################################################
|
|
|
|
section "02 — Top-Level CLI Surface"
|
|
|
|
run_test \
|
|
"Top-level --help" \
|
|
--help
|
|
|
|
###############################################################################
|
|
# 03 — Top-level command discovery
|
|
###############################################################################
|
|
|
|
section "03 — Top-Level Command Help Audit"
|
|
|
|
TOP_LEVEL_COMMANDS=(
|
|
serve
|
|
init
|
|
system
|
|
admin
|
|
interface
|
|
peer
|
|
network
|
|
route
|
|
firewall
|
|
nat
|
|
forwarding
|
|
reconcile
|
|
backup
|
|
audit
|
|
live
|
|
diagnostics
|
|
profile
|
|
version
|
|
)
|
|
|
|
for cmd in "${TOP_LEVEL_COMMANDS[@]}"; do
|
|
run_quiet \
|
|
"$cmd --help" \
|
|
"$cmd" \
|
|
--help
|
|
done
|
|
|
|
###############################################################################
|
|
# 04 — Subcommand help
|
|
###############################################################################
|
|
|
|
section "04 — Feature/Subcommand Help Audit"
|
|
|
|
declare -A SUBCOMMANDS
|
|
|
|
SUBCOMMANDS[system]="info health settings"
|
|
SUBCOMMANDS[admin]="status create password sessions tokens"
|
|
SUBCOMMANDS[interface]="list show create update enable disable delete status reconcile"
|
|
SUBCOMMANDS[peer]="list show create update enable disable revoke delete status config qr expire lifecycle"
|
|
SUBCOMMANDS[network]="list show create update delete available allocations"
|
|
SUBCOMMANDS[route]="list show add update delete status sync"
|
|
SUBCOMMANDS[firewall]="list show add update delete enable disable status sync"
|
|
SUBCOMMANDS[nat]="status enable disable list sync"
|
|
SUBCOMMANDS[forwarding]="status enable disable sync"
|
|
SUBCOMMANDS[reconcile]="status plan apply verify"
|
|
SUBCOMMANDS[backup]="create list show verify restore delete"
|
|
SUBCOMMANDS[audit]="list show"
|
|
SUBCOMMANDS[live]="interface peer routes firewall forwarding nat"
|
|
SUBCOMMANDS[profile]="list show validate resolve"
|
|
|
|
for cmd in "${!SUBCOMMANDS[@]}"; do
|
|
|
|
read -ra subs <<< "${SUBCOMMANDS[$cmd]}"
|
|
|
|
for sub in "${subs[@]}"; do
|
|
run_quiet \
|
|
"$cmd $sub --help" \
|
|
"$cmd" \
|
|
"$sub" \
|
|
--help
|
|
done
|
|
done
|
|
|
|
###############################################################################
|
|
# 05 — Administrator bootstrap
|
|
###############################################################################
|
|
|
|
section "05 — Administrator Bootstrap"
|
|
|
|
rm -rf "$DATA_DIR"
|
|
mkdir -p "$DATA_DIR"
|
|
|
|
run_test \
|
|
"Initialize administrator with generated password" \
|
|
"${CLI[@]}" \
|
|
init \
|
|
--generate-password \
|
|
--write-password-file "$PASSWORD_FILE"
|
|
|
|
assert_nonempty_file \
|
|
"Generated administrator password file exists" \
|
|
"$PASSWORD_FILE"
|
|
|
|
###############################################################################
|
|
# 06 — System
|
|
###############################################################################
|
|
|
|
section "06 — System"
|
|
|
|
run_test \
|
|
"System info" \
|
|
"${CLI[@]}" \
|
|
system \
|
|
info
|
|
|
|
run_test \
|
|
"System health" \
|
|
"${CLI[@]}" \
|
|
system \
|
|
health
|
|
|
|
run_test \
|
|
"System info JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
system \
|
|
info
|
|
|
|
run_test \
|
|
"System health JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
system \
|
|
health
|
|
|
|
run_test \
|
|
"System info YAML" \
|
|
"${CLI[@]}" \
|
|
--format yaml \
|
|
system \
|
|
info
|
|
|
|
run_test \
|
|
"System health CSV" \
|
|
"${CLI[@]}" \
|
|
--format csv \
|
|
system \
|
|
health
|
|
|
|
###############################################################################
|
|
# 07 — Administrator
|
|
###############################################################################
|
|
|
|
section "07 — Administrator / Authentication"
|
|
|
|
run_test \
|
|
"Admin status" \
|
|
"${CLI[@]}" \
|
|
admin \
|
|
status
|
|
|
|
run_test \
|
|
"Admin status JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
admin \
|
|
status
|
|
|
|
###############################################################################
|
|
# 08 — Client profiles / MTU
|
|
###############################################################################
|
|
|
|
section "08 — Client Environment / MTU Profiles"
|
|
|
|
run_test \
|
|
"Profile list" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
list
|
|
|
|
run_test \
|
|
"Profile list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
list
|
|
|
|
run_test \
|
|
"Default mobile profile" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
show \
|
|
default-mobile
|
|
|
|
run_test \
|
|
"Default CGNAT profile" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
show \
|
|
default-cgnat
|
|
|
|
run_test \
|
|
"Default Wi-Fi profile" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
show \
|
|
default-wifi
|
|
|
|
run_test \
|
|
"Default Web profile" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
show \
|
|
default-web
|
|
|
|
run_test \
|
|
"Default Wired profile" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
show \
|
|
default-wired
|
|
|
|
run_test \
|
|
"Validate MTU 1280" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
validate \
|
|
1280
|
|
|
|
run_test \
|
|
"Validate MTU 1360" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
validate \
|
|
1360
|
|
|
|
run_test \
|
|
"Validate MTU 1420" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
validate \
|
|
1420
|
|
|
|
run_test \
|
|
"Validate MTU 1500" \
|
|
"${CLI[@]}" \
|
|
profile \
|
|
validate \
|
|
1500
|
|
|
|
run_test \
|
|
"Resolve Android Mobile CGNAT" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--connection mobile \
|
|
--device android \
|
|
--nat cgnat
|
|
|
|
run_test \
|
|
"Resolve iOS Mobile CGNAT" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--connection mobile \
|
|
--device ios \
|
|
--nat cgnat
|
|
|
|
run_test \
|
|
"Resolve Linux Wi-Fi Direct" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--connection wifi \
|
|
--device linux \
|
|
--nat direct
|
|
|
|
run_test \
|
|
"Resolve Windows Wired" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--connection wired \
|
|
--device windows \
|
|
--nat direct
|
|
|
|
run_test \
|
|
"Resolve Jio Mobile CGNAT" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--provider jio \
|
|
--connection mobile \
|
|
--nat cgnat
|
|
|
|
run_test \
|
|
"Resolve T-Mobile Mobile CGNAT" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--provider tmobile \
|
|
--connection mobile \
|
|
--nat cgnat
|
|
|
|
run_test \
|
|
"Resolve Verizon Mobile CGNAT" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--provider verizon \
|
|
--connection mobile \
|
|
--nat cgnat
|
|
|
|
run_test \
|
|
"Resolve Starlink CGNAT" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--provider starlink \
|
|
--connection other \
|
|
--nat cgnat
|
|
|
|
run_test \
|
|
"Resolve manual MTU override" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--connection mobile \
|
|
--device android \
|
|
--nat cgnat \
|
|
--mtu 1300
|
|
|
|
run_test \
|
|
"Resolve explicit profile" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
profile \
|
|
resolve \
|
|
--profile android-mobile
|
|
|
|
###############################################################################
|
|
# 09 — Network
|
|
###############################################################################
|
|
|
|
section "09 — Network Management"
|
|
|
|
run_test \
|
|
"Network list" \
|
|
"${CLI[@]}" \
|
|
network \
|
|
list
|
|
|
|
run_test \
|
|
"Network list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
network \
|
|
list
|
|
|
|
###############################################################################
|
|
# 10 — Interface
|
|
###############################################################################
|
|
|
|
section "10 — WireGuard Interface Management"
|
|
|
|
run_test \
|
|
"Interface list" \
|
|
"${CLI[@]}" \
|
|
interface \
|
|
list
|
|
|
|
run_test \
|
|
"Interface list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
interface \
|
|
list
|
|
|
|
###############################################################################
|
|
# 11 — Peer
|
|
###############################################################################
|
|
|
|
section "11 — Peer Management"
|
|
|
|
run_test \
|
|
"Peer list" \
|
|
"${CLI[@]}" \
|
|
peer \
|
|
list
|
|
|
|
run_test \
|
|
"Peer list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
peer \
|
|
list
|
|
|
|
###############################################################################
|
|
# 12 — Route
|
|
###############################################################################
|
|
|
|
section "12 — Routing"
|
|
|
|
run_test \
|
|
"Route list" \
|
|
"${CLI[@]}" \
|
|
route \
|
|
list
|
|
|
|
run_test \
|
|
"Route list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
route \
|
|
list
|
|
|
|
###############################################################################
|
|
# 13 — Firewall
|
|
###############################################################################
|
|
|
|
section "13 — Firewall"
|
|
|
|
run_test \
|
|
"Firewall list" \
|
|
"${CLI[@]}" \
|
|
firewall \
|
|
list
|
|
|
|
run_test \
|
|
"Firewall list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
firewall \
|
|
list
|
|
|
|
###############################################################################
|
|
# 14 — NAT
|
|
###############################################################################
|
|
|
|
section "14 — NAT"
|
|
|
|
run_test \
|
|
"NAT status" \
|
|
"${CLI[@]}" \
|
|
nat \
|
|
status
|
|
|
|
run_test \
|
|
"NAT list" \
|
|
"${CLI[@]}" \
|
|
nat \
|
|
list
|
|
|
|
run_test \
|
|
"NAT status JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
nat \
|
|
status
|
|
|
|
###############################################################################
|
|
# 15 — Forwarding
|
|
###############################################################################
|
|
|
|
section "15 — IP Forwarding"
|
|
|
|
run_test \
|
|
"Forwarding status" \
|
|
"${CLI[@]}" \
|
|
forwarding \
|
|
status
|
|
|
|
run_test \
|
|
"Forwarding status JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
forwarding \
|
|
status
|
|
|
|
###############################################################################
|
|
# 16 — Reconciliation
|
|
###############################################################################
|
|
|
|
section "16 — Reconciliation"
|
|
|
|
run_test \
|
|
"Reconciliation status" \
|
|
"${CLI[@]}" \
|
|
reconcile \
|
|
status
|
|
|
|
run_test \
|
|
"Reconciliation plan" \
|
|
"${CLI[@]}" \
|
|
reconcile \
|
|
plan
|
|
|
|
run_test \
|
|
"Reconciliation verify" \
|
|
"${CLI[@]}" \
|
|
reconcile \
|
|
verify
|
|
|
|
run_test \
|
|
"Reconciliation status JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
reconcile \
|
|
status
|
|
|
|
###############################################################################
|
|
# 17 — Backup
|
|
###############################################################################
|
|
|
|
section "17 — Backup"
|
|
|
|
run_test \
|
|
"Backup list" \
|
|
"${CLI[@]}" \
|
|
backup \
|
|
list
|
|
|
|
run_test \
|
|
"Backup list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
backup \
|
|
list
|
|
|
|
###############################################################################
|
|
# 18 — Audit
|
|
###############################################################################
|
|
|
|
section "18 — Audit"
|
|
|
|
run_test \
|
|
"Audit list" \
|
|
"${CLI[@]}" \
|
|
audit \
|
|
list
|
|
|
|
run_test \
|
|
"Audit list JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
audit \
|
|
list
|
|
|
|
###############################################################################
|
|
# 19 — Live state
|
|
###############################################################################
|
|
|
|
section "19 — Live Linux State"
|
|
|
|
if [[ "$LIVE" == "1" ]]; then
|
|
|
|
info "LIVE=1 enabled."
|
|
|
|
run_test \
|
|
"Live interface list" \
|
|
"${CLI[@]}" \
|
|
live \
|
|
interface \
|
|
list
|
|
|
|
run_test \
|
|
"Live peer list" \
|
|
"${CLI[@]}" \
|
|
live \
|
|
peer \
|
|
list
|
|
|
|
run_test \
|
|
"Live routes" \
|
|
"${CLI[@]}" \
|
|
live \
|
|
routes
|
|
|
|
run_test \
|
|
"Live firewall" \
|
|
"${CLI[@]}" \
|
|
live \
|
|
firewall
|
|
|
|
run_test \
|
|
"Live forwarding" \
|
|
"${CLI[@]}" \
|
|
live \
|
|
forwarding
|
|
|
|
run_test \
|
|
"Live NAT" \
|
|
"${CLI[@]}" \
|
|
live \
|
|
nat
|
|
|
|
else
|
|
|
|
skip "Live interface list — use LIVE=1"
|
|
skip "Live peer list — use LIVE=1"
|
|
skip "Live routes — use LIVE=1"
|
|
skip "Live firewall — use LIVE=1"
|
|
skip "Live forwarding — use LIVE=1"
|
|
skip "Live NAT — use LIVE=1"
|
|
|
|
fi
|
|
|
|
###############################################################################
|
|
# 20 — Diagnostics
|
|
###############################################################################
|
|
|
|
section "20 — Native Diagnostics"
|
|
|
|
run_test \
|
|
"Diagnostics all" \
|
|
"${CLI[@]}" \
|
|
diagnostics \
|
|
all
|
|
|
|
run_test \
|
|
"Diagnostics all JSON" \
|
|
"${CLI[@]}" \
|
|
--format json \
|
|
diagnostics \
|
|
all
|
|
|
|
DIAGNOSTIC_SUBSYSTEMS=(
|
|
system
|
|
network
|
|
wan
|
|
wireguard
|
|
routing
|
|
forwarding
|
|
firewall
|
|
nat
|
|
mtu
|
|
reconciliation
|
|
)
|
|
|
|
for subsystem in "${DIAGNOSTIC_SUBSYSTEMS[@]}"; do
|
|
run_test \
|
|
"Diagnostics: $subsystem" \
|
|
"${CLI[@]}" \
|
|
diagnostics \
|
|
"$subsystem"
|
|
done
|
|
|
|
if [[ "$LIVE" == "1" ]]; then
|
|
run_test \
|
|
"Diagnostics: peer" \
|
|
"${CLI[@]}" \
|
|
diagnostics \
|
|
peer
|
|
else
|
|
skip "Diagnostics: peer — requires LIVE=1 and peer context"
|
|
fi
|
|
|
|
###############################################################################
|
|
# 21 — Output format matrix
|
|
###############################################################################
|
|
|
|
section "21 — Output Format Matrix"
|
|
|
|
FORMATS=(
|
|
table
|
|
json
|
|
yaml
|
|
csv
|
|
)
|
|
|
|
for format in "${FORMATS[@]}"; do
|
|
|
|
run_test \
|
|
"Network list — $format" \
|
|
"${CLI[@]}" \
|
|
--format "$format" \
|
|
network \
|
|
list
|
|
|
|
run_test \
|
|
"Profile list — $format" \
|
|
"${CLI[@]}" \
|
|
--format "$format" \
|
|
profile \
|
|
list
|
|
|
|
run_test \
|
|
"System info — $format" \
|
|
"${CLI[@]}" \
|
|
--format "$format" \
|
|
system \
|
|
info
|
|
|
|
run_test \
|
|
"System health — $format" \
|
|
"${CLI[@]}" \
|
|
--format "$format" \
|
|
system \
|
|
health
|
|
|
|
run_test \
|
|
"Audit list — $format" \
|
|
"${CLI[@]}" \
|
|
--format "$format" \
|
|
audit \
|
|
list
|
|
|
|
done
|
|
|
|
###############################################################################
|
|
# 22 — Global options
|
|
###############################################################################
|
|
|
|
section "22 — Global CLI Options"
|
|
|
|
run_test \
|
|
"Quiet mode" \
|
|
"${CLI[@]}" \
|
|
--quiet \
|
|
system \
|
|
health
|
|
|
|
run_test \
|
|
"JSON shortcut" \
|
|
"${CLI[@]}" \
|
|
--json \
|
|
system \
|
|
health
|
|
|
|
run_test \
|
|
"Verbose mode" \
|
|
"${CLI[@]}" \
|
|
--verbose \
|
|
system \
|
|
health
|
|
|
|
###############################################################################
|
|
# 23 — Environment namespace
|
|
###############################################################################
|
|
|
|
section "23 — NX9_WG_* Environment Namespace"
|
|
|
|
NX9_WG_LOG_LEVEL=debug \
|
|
"$BIN" \
|
|
--data-dir "$DATA_DIR" \
|
|
--database "$DB" \
|
|
system \
|
|
health \
|
|
>/dev/null \
|
|
2>/dev/null
|
|
|
|
if [[ "$?" -eq 0 ]]; then
|
|
pass "NX9_WG_LOG_LEVEL accepted"
|
|
else
|
|
fail "NX9_WG_LOG_LEVEL accepted"
|
|
fi
|
|
|
|
###############################################################################
|
|
# 24 — Explicit database path
|
|
###############################################################################
|
|
|
|
section "24 — Explicit Database Path Resolution"
|
|
|
|
EXPLICIT_DB="$TEST_ROOT/explicit/nested/nx9-wg.db"
|
|
|
|
rm -rf "$TEST_ROOT/explicit"
|
|
|
|
run_test \
|
|
"Explicit nested database path" \
|
|
--database "$EXPLICIT_DB" \
|
|
system \
|
|
health
|
|
|
|
assert_file \
|
|
"Explicit database file created" \
|
|
"$EXPLICIT_DB"
|
|
|
|
###############################################################################
|
|
# 25 — Data directory resolution
|
|
###############################################################################
|
|
|
|
section "25 — Data Directory Resolution"
|
|
|
|
DATA_ONLY="$TEST_ROOT/data-only"
|
|
|
|
rm -rf "$DATA_ONLY"
|
|
mkdir -p "$DATA_ONLY"
|
|
|
|
run_test \
|
|
"Database created from data-dir" \
|
|
--data-dir "$DATA_ONLY" \
|
|
system \
|
|
health
|
|
|
|
FOUND_DB="$(
|
|
find "$DATA_ONLY" \
|
|
-type f \
|
|
\( \
|
|
-name '*.db' \
|
|
-o -name '*.sqlite' \
|
|
-o -name '*.sqlite3' \
|
|
\) \
|
|
-print \
|
|
-quit \
|
|
2>/dev/null
|
|
)"
|
|
|
|
if [[ -n "$FOUND_DB" ]]; then
|
|
pass "Database exists below data-dir: $FOUND_DB"
|
|
else
|
|
fail "No database found below data-dir"
|
|
fi
|
|
|
|
###############################################################################
|
|
# 26 — Source architecture safety
|
|
###############################################################################
|
|
|
|
section "26 — Source-Level Architecture Safety"
|
|
|
|
PROCESS_SCAN="$TEST_ROOT/forbidden-process.txt"
|
|
MARKER_SCAN="$TEST_ROOT/forbidden-markers.txt"
|
|
SQL_SCAN="$TEST_ROOT/sql-outside-db.txt"
|
|
ENV_SCAN="$TEST_ROOT/env-scan.txt"
|
|
|
|
if grep -RInE \
|
|
'Command::new|tokio::process|std::process::Command' \
|
|
"$ROOT/src" \
|
|
"$ROOT/crates" \
|
|
--include='*.rs' \
|
|
>"$PROCESS_SCAN" \
|
|
2>/dev/null
|
|
then
|
|
echo
|
|
echo "Forbidden subprocess references:"
|
|
sed 's/^/ /' "$PROCESS_SCAN"
|
|
fail "No external subprocess invocation in production Rust"
|
|
else
|
|
pass "No external subprocess invocation in production Rust"
|
|
fi
|
|
|
|
if grep -RInE \
|
|
'TODO|FIXME|XXX|HACK|unimplemented!\(\)|todo!\(\)' \
|
|
"$ROOT/src" \
|
|
"$ROOT/crates" \
|
|
--include='*.rs' \
|
|
>"$MARKER_SCAN" \
|
|
2>/dev/null
|
|
then
|
|
echo
|
|
echo "Forbidden markers:"
|
|
sed 's/^/ /' "$MARKER_SCAN"
|
|
fail "No forbidden technical-debt markers"
|
|
else
|
|
pass "No forbidden technical-debt markers"
|
|
fi
|
|
|
|
if grep -RIn \
|
|
'sqlx::query' \
|
|
"$ROOT/src" \
|
|
--include='*.rs' \
|
|
>"$SQL_SCAN" \
|
|
2>/dev/null
|
|
then
|
|
echo
|
|
echo "SQL found outside nx9-wg-db:"
|
|
sed 's/^/ /' "$SQL_SCAN"
|
|
fail "SQL isolation"
|
|
else
|
|
pass "SQL isolated from application root"
|
|
fi
|
|
|
|
###############################################################################
|
|
# Environment variable source scan
|
|
###############################################################################
|
|
|
|
section "27 — Environment Variable Namespace Source Audit"
|
|
|
|
if grep -RInE \
|
|
'env\s*\(\s*"(NX9_|WG_|RUST_LOG)' \
|
|
"$ROOT/src" \
|
|
"$ROOT/crates" \
|
|
"$ROOT/Cargo.toml" \
|
|
--include='*.rs' \
|
|
--include='*.toml' \
|
|
>"$ENV_SCAN" \
|
|
2>/dev/null
|
|
then
|
|
|
|
echo
|
|
echo "Potential non-canonical environment references:"
|
|
sed 's/^/ /' "$ENV_SCAN"
|
|
|
|
if grep -E \
|
|
'env\s*\(\s*"(NX9_[^W]|WG_|RUST_LOG)' \
|
|
"$ENV_SCAN" \
|
|
>/dev/null 2>&1
|
|
then
|
|
fail "Strict NX9_WG_* environment namespace"
|
|
else
|
|
pass "Environment namespace appears canonical"
|
|
fi
|
|
|
|
else
|
|
pass "No suspicious environment namespace references"
|
|
fi
|
|
|
|
###############################################################################
|
|
# Final summary
|
|
###############################################################################
|
|
|
|
section "28 — FINAL VERIFICATION SUMMARY"
|
|
|
|
echo
|
|
echo " Binary:"
|
|
echo " $BIN"
|
|
echo
|
|
echo " Project:"
|
|
echo " $ROOT"
|
|
echo
|
|
echo " Temporary test root:"
|
|
echo " $TEST_ROOT"
|
|
echo
|
|
echo " Test database:"
|
|
echo " $DB"
|
|
echo
|
|
echo " LIVE mode:"
|
|
echo " $LIVE"
|
|
echo
|
|
echo " ------------------------------------------------------------------------"
|
|
echo " PASS : $PASS"
|
|
echo " FAIL : $FAIL"
|
|
echo " SKIP : $SKIP"
|
|
echo " TOTAL: $TOTAL"
|
|
echo " ------------------------------------------------------------------------"
|
|
echo
|
|
|
|
if [[ "$FAIL" -eq 0 ]]; then
|
|
echo "${GREEN}${BOLD}RESULT: PASS${RESET}"
|
|
echo
|
|
echo "All executed nx9-wg CLI verification checks passed."
|
|
echo
|
|
exit 0
|
|
fi
|
|
|
|
echo "${RED}${BOLD}RESULT: FAIL${RESET}"
|
|
echo
|
|
echo "One or more verification checks failed."
|
|
echo
|
|
echo "The temporary environment will be preserved for investigation."
|
|
echo
|
|
exit 1
|