#!/bin/bash set -euo pipefail ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) failures=0 run() { local label=$1 shift if "$@"; then printf '[pass] %s\n' "$label"; else printf '[fail] %s\n' "$label" >&2; failures=$((failures + 1)); fi } shell_syntax() { bash -n "$ROOT/install.sh" "$ROOT"/usr/local/lib/pivilion/*.sh "$ROOT"/usr/local/bin/* "$ROOT/etc/profile.d/pivilion-motd.sh" } php_syntax() { command -v php >/dev/null 2>&1 || return 0 while IFS= read -r file; do php -l "$file" >/dev/null || return 1; done < <(find "$ROOT/var" "$ROOT/home" -name '*.php' -type f) } systemd_syntax() { local output command -v systemd-analyze >/dev/null 2>&1 || return 0 output=$(systemd-analyze verify "$ROOT"/etc/systemd/system/*.service 2>&1) || true output=$(printf '%s\n' "$output" | grep -Ev 'SO_PASS(RIGHTS|CRED)|handoff timestamp|user lookup socket' || true) [ -z "$output" ] } captive_rules() { grep -q 'R=302' "$ROOT/home/pi/pivilion/config/html/.htaccess" && ! grep -q 'R=301' "$ROOT/home/pi/pivilion/config/html/.htaccess" && ! grep -Eq 'dport[[:space:]]+443|redirect to :443' "$ROOT/usr/local/lib/pivilion/mode.sh" } installer_safety() { grep -q -- '--dry-run' "$ROOT/install.sh" && grep -q -- '--repair' "$ROOT/install.sh" && grep -q 'backup_managed_files' "$ROOT/install.sh" && grep -q 'rollback()' "$ROOT/install.sh" && ! grep -Eq 'disable( --now)? dnsmasq' "$ROOT/install.sh" && ! grep -q 'rm -f.*CAPTIVE\|rm -f.*\.htaccess' "$ROOT/usr/local/bin/onion" } state_contract() { grep -q "printf 'desired_mode" "$ROOT/usr/local/lib/pivilion/common.sh" && grep -q "printf 'active_mode" "$ROOT/usr/local/lib/pivilion/common.sh" && grep -q "printf 'health" "$ROOT/usr/local/lib/pivilion/common.sh" && grep -q 'degraded' "$ROOT/usr/local/lib/pivilion/mode.sh" } shellcheck_scripts() { command -v shellcheck >/dev/null 2>&1 || return 0 shellcheck "$ROOT/install.sh" "$ROOT"/usr/local/lib/pivilion/*.sh "$ROOT"/usr/local/bin/pivilion* "$ROOT/usr/local/bin/hotspot" "$ROOT/usr/local/bin/onion" "$ROOT/usr/local/bin/pikey" } run "configuration and state fixtures" "$ROOT/tests/test-common.sh" run "shell syntax" shell_syntax run "PHP syntax" php_syntax run "systemd unit syntax" systemd_syntax run "temporary HTTP-only captive rules" captive_rules run "transactional installer safety contract" installer_safety run "truthful runtime state contract" state_contract run "ShellCheck (when installed)" shellcheck_scripts [ "$failures" -eq 0 ] || { echo "$failures test group(s) failed." >&2; exit 1; } echo "All available tests passed."