lesavka/tests/chaos/system/interrupted_install_safe_state_contract.rs

144 lines
4.9 KiB
Rust

// 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_CORE_RESTART:-0",
"LESAVKA_RECOVERY_ALLOW_REBOOT:-0",
"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"
);
}