// Chaos contract for interrupted or partially failed installs. // // Scope: preserve best-effort cleanup and explicit recovery boundaries in // install scripts without executing privileged operations. // Targets: client/server install scripts. // Why: failed installs should leave the Pi and desktop in a recoverable state, // especially around capture power, temporary PKI material, and USB gadget reset. const SERVER_INSTALL: &str = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/scripts/install/server.sh" )); const CLIENT_INSTALL: &str = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/scripts/install/client.sh" )); #[test] fn server_restores_capture_power_after_discovery_failures() { for marker in [ "trap restore_capture_power_after_discovery EXIT", "restore_capture_power_after_discovery", "borrowing relay GPIO power for capture discovery", "trap - EXIT", ] { assert!( SERVER_INSTALL.contains(marker), "server install should preserve capture-power recovery marker {marker}" ); } } #[test] fn attached_host_gadget_rebuilds_require_two_explicit_knobs() { for marker in [ "LESAVKA_FORCE_GADGET_REBUILD", "LESAVKA_ALLOW_GADGET_RESET", "EXPLICIT_GADGET_REBUILD=1", "Preserving the attached gadget to avoid wedging the Pi USB controller", "Run during a maintenance window", ] { assert!( SERVER_INSTALL.contains(marker), "server install should preserve attached-gadget safety marker {marker}" ); } } #[test] fn client_tls_bundle_tempfiles_are_removed_on_failure_and_success() { for marker in [ "tmp_bundle=$(run_as_user mktemp", "rm -f \"$tmp_bundle\"", "tmp=$(mktemp -d)", "sudo rm -rf \"$tmp\"", "rm -f \"$bundle\"", ] { assert!( CLIENT_INSTALL.contains(marker), "client install should preserve temporary bundle cleanup marker {marker}" ); } } #[test] fn server_install_refuses_to_kill_unrelated_port_owners() { for marker in [ "unexpected process", "no owning PID could be identified", "refusing to start a duplicate server", "clear_stale_server_listener", ] { assert!( SERVER_INSTALL.contains(marker), "stale listener cleanup should preserve fail-safe marker {marker}" ); } } #[test] fn installers_refuse_to_replace_live_entrypoints_with_empty_artifacts() { for marker in [ "install_verified_executable()", "source '$src' is missing or empty", "staged install output was not a non-empty executable", "Preserving the existing installed executable", ] { assert!( SERVER_INSTALL.contains(marker), "server installer should preserve verified executable install marker {marker}" ); assert!( CLIENT_INSTALL.contains(marker), "client installer should preserve verified executable install marker {marker}" ); } for marker in [ "install_verified_executable \"$SRC_DIR/target/release/lesavka-server\" /usr/local/bin/lesavka-server", "install_verified_executable \"$SRC_DIR/target/release/lesavka-uvc\" /usr/local/bin/lesavka-uvc", "install_verified_executable \"$SRC_DIR/scripts/daemon/lesavka-core.sh\" /usr/local/bin/lesavka-core.sh", "install_verified_executable \"$SRC_DIR/scripts/daemon/lesavka-uvc.sh\" /usr/local/bin/lesavka-uvc.sh", "install_verified_executable \"$SRC_DIR/scripts/daemon/lesavka-recovery-ladder.sh\" /usr/local/bin/lesavka-recovery-ladder", ] { assert!( SERVER_INSTALL.contains(marker), "server installer should protect systemd entrypoint {marker}" ); } assert!( CLIENT_INSTALL.contains( "install_verified_executable \"$SRC/target/release/lesavka-client\" /usr/local/bin/lesavka-client" ), "client installer should protect the launchable desktop binary" ); } #[test] fn recovery_ladder_restores_before_rebooting_or_touching_the_core_gadget() { let ladder = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), "/scripts/daemon/lesavka-recovery-ladder.sh" )); for marker in [ "restore_last_good", "restart_server_only", "restart_uvc_and_server", "LESAVKA_RECOVERY_ALLOW_UVC_RESTART:-0", "LESAVKA_RECOVERY_ALLOW_CORE_RESTART:-0", "LESAVKA_RECOVERY_ALLOW_REBOOT:-0", "UVC helper restart disabled; preserving attached USB gadget", "core restart disabled; preserving attached USB gadget", "reboot disabled; leaving host online for operator inspection", ] { assert!( ladder.contains(marker), "recovery ladder should preserve soft-recovery marker {marker}" ); } assert!( ladder.find("restore_last_good").unwrap() < ladder.find("reboot_if_allowed").unwrap(), "the ladder should try last-known-good restore before any optional reboot" ); }