pivilion/tests/run.sh

129 lines
7 KiB
Bash
Raw Permalink Normal View History

#!/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() {
2026-07-01 20:21:55 +02:00
grep -Fqx 'RewriteCond %{REQUEST_FILENAME} -f [OR]' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
grep -Fqx 'RewriteCond %{REQUEST_FILENAME} -d' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
grep -Fqx 'RewriteRule ^ - [END]' "$ROOT/home/pi/pivilion/config/html/.htaccess" &&
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 -Fq 'systemctl disable --now dnsmasq.service' "$ROOT/install.sh" &&
grep -Fq 'systemctl disable pivilion-dnsmasq.service pivilion-hostapd.service' "$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 'pivilion_write_state none.*failed' "$ROOT/usr/local/lib/pivilion/mode.sh"
}
2026-06-29 23:08:56 +02:00
tty_banner_contract() {
grep -q 'StandardOutput=null' "$ROOT/etc/systemd/system/pivilion-login-banner.service" &&
! grep -q 'journal+console' "$ROOT/etc/systemd/system/pivilion-login-banner.service" &&
grep -q '/etc/issue.d/90-pivilion.issue' "$ROOT/usr/local/lib/pivilion/issue-banner.sh" &&
grep -q 'SSH_CONNECTION' "$ROOT/etc/profile.d/pivilion-motd.sh" &&
grep -q 'agetty --reload' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
! grep -Eq '(^|[[:space:]])/etc/issue([[:space:]]|$)' "$ROOT/install.sh"
}
boot_config_contract() {
grep -q 'pivilion_boot_config_path' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
grep -q 'pivilion.conf.last-good' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
grep -q 'derive_psk' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
2026-07-01 14:24:12 +02:00
grep -q 'pivilion_apply_hostname' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
! grep -R -q --include='*.sh' --include='*.service' 'hostnamectl' "$ROOT/install.sh" "$ROOT/usr" "$ROOT/etc" &&
grep -q 'Before=.*NetworkManager' "$ROOT/etc/systemd/system/pivilion-config.service" &&
grep -q 'config-sync.sh set PIVILION_MODE' "$ROOT/usr/local/bin/hotspot" &&
grep -q 'config-sync.sh set-wifi' "$ROOT/usr/local/bin/pikey"
}
same_boot_mode_contract() {
grep -q 'ExecStart=/usr/local/lib/pivilion/mode.sh --boot-selected' "$ROOT/etc/systemd/system/pivilion-mode.service" &&
grep -q 'Requires=pivilion-config.service' "$ROOT/etc/systemd/system/pivilion-mode.service" &&
grep -q 'RequiresMountsFor=/boot /boot/firmware' "$ROOT/etc/systemd/system/pivilion-config.service" &&
grep -q 'PIVILION_SELECTED_MODE_FILE' "$ROOT/usr/local/lib/pivilion/config-sync.sh" &&
grep -q 'MODE=$(pivilion_selected_mode)' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'wait_for_diagnostics hotspot' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'wait_for_diagnostics onion' "$ROOT/usr/local/lib/pivilion/mode.sh"
}
2026-07-01 18:33:13 +02:00
networkmanager_startup_contract() {
grep -q 'pivilion_nm_prepare_interface "$IFACE"' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'pivilion_nm_connection_up "$NM_HOTSPOT" "$IFACE"' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'nmcli connection up "$connection" ifname "$iface"' "$ROOT/usr/local/lib/pivilion/networkmanager.sh"
}
dnsmasq_ownership_contract() {
[ -f "$ROOT/etc/systemd/system/pivilion-dnsmasq.service" ] &&
! find "$ROOT/etc/systemd/system" -type l \( -name 'dnsmasq.service' -o -name 'pivilion-dnsmasq.service' \) -print -quit | grep -q . &&
grep -Fq 'systemctl stop dnsmasq.service pivilion-dnsmasq.service' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -Fq 'systemctl stop pivilion-dnsmasq.service' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'start_pivilion_dnsmasq' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'systemctl is-active --quiet pivilion-dnsmasq.service' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -Fq 'address=/#/10.1.1.1' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
grep -q 'android_probe.*302' "$ROOT/usr/local/lib/pivilion/diagnose.sh" &&
grep -q 'apple_probe.*302' "$ROOT/usr/local/lib/pivilion/diagnose.sh" &&
grep -q 'microsoft_probe.*302' "$ROOT/usr/local/lib/pivilion/diagnose.sh" &&
grep -Fqx 'RewriteCond %{REQUEST_FILENAME} -f [OR]' "$ROOT/home/pi/pivilion/config/html/.htaccess"
}
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"
2026-07-01 14:24:12 +02:00
run "early-boot hostname application" "$ROOT/tests/test-hostname.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
2026-06-29 23:08:56 +02:00
run "agetty login-banner contract" tty_banner_contract
run "canonical boot configuration contract" boot_config_contract
run "same-boot hotspot and onion activation contract" same_boot_mode_contract
2026-07-01 18:33:13 +02:00
run "NetworkManager software-radio and delayed-device handling" "$ROOT/tests/test-networkmanager.sh"
run "NetworkManager hotspot startup contract" networkmanager_startup_contract
run "exclusive captive dnsmasq ownership contract" dnsmasq_ownership_contract
run "ShellCheck (when installed)" shellcheck_scripts
[ "$failures" -eq 0 ] || { echo "$failures test group(s) failed." >&2; exit 1; }
echo "All available tests passed."