mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-14 10:51:00 +01:00
vmbox: add the end-to-end acceptance demos
shutdown_verdict_demo.sh exercises every branch of the verdict table against a
real guest -- clean poweroff, reboot-instead-of-poweroff, kernel panic, and a
command that leaves the machine running. The failure branches matter as much
as the success one: a verifier that has only ever returned "pass" is untested.
destructive_demo.sh is the acceptance test for the tool as a whole: destroy
/etc and /usr in the guest, assert the guest really is broken, reset with one
command, assert it is pristine again, then assert the HOST is untouched
(/etc/hosts attributes unchanged, base image hash unchanged, /etc and /usr
still present).
Measured on the first real run, which confirms the design's central claim:
QMP reported SHUTDOWN {"guest": true, "reason": "guest-shutdown"} and the
serial log reached "reboot: Power down", so the verdict was "clean poweroff"
with exit 0 -- while ssh saw only "Connection closed by remote host", which
would have been indistinguishable from a crash or a dropped link.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdyMBGCrGVGDWtJ9xe48f3
This commit is contained in:
parent
4cb7abc3ac
commit
36234e980a
73
vmbox/tests/destructive_demo.sh
Executable file
73
vmbox/tests/destructive_demo.sh
Executable file
@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# vmbox end-to-end demo: break a sandbox as thoroughly as possible, verify the
|
||||
# breakage, reset it, and prove the host was never touched.
|
||||
#
|
||||
# This is the acceptance test for the whole tool. It is deliberately
|
||||
# destructive INSIDE the guest -- that is the point -- and it asserts after
|
||||
# each step rather than just printing output.
|
||||
# ============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
VMBOX_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
||||
readonly VMBOX_ROOT
|
||||
readonly VM="${VM:-destruct}"
|
||||
readonly VM_CLI="$VMBOX_ROOT/bin/vm"
|
||||
|
||||
# Host-side facts captured up front, re-checked at the end.
|
||||
HOST_HOSTS_ATTRS="$(lsattr /etc/hosts 2>/dev/null | awk '{print $1}')"
|
||||
readonly HOST_HOSTS_ATTRS
|
||||
BASE_SHA_BEFORE="$(sha256sum "$HOME/.local/share/vmbox/base/base.qcow2" 2>/dev/null | awk '{print $1}')"
|
||||
readonly BASE_SHA_BEFORE
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
|
||||
step() { printf '\n\033[0;34m=== %s\033[0m\n' "$*"; }
|
||||
check() {
|
||||
local label="$1" expected="$2" actual="$3"
|
||||
if [[ "$actual" == *"$expected"* ]]; then
|
||||
printf '\033[0;32m PASS\033[0m %s\n' "$label"
|
||||
pass=$(( pass + 1 ))
|
||||
else
|
||||
printf '\033[0;31m FAIL\033[0m %s\n wanted: %s\n got: %s\n' \
|
||||
"$label" "$expected" "$actual"
|
||||
fail=$(( fail + 1 ))
|
||||
fi
|
||||
}
|
||||
|
||||
step "0. Fresh sandbox, clock pinned into the shutdown window"
|
||||
"$VM_CLI" rm "$VM" >/dev/null 2>&1
|
||||
"$VM_CLI" new "$VM" --rtc 2026-08-22T20:55:00
|
||||
|
||||
step "1. Baseline: the guest is a healthy Arch system"
|
||||
out="$("$VM_CLI" run "$VM" 'cat /etc/os-release | head -1' 2>&1)"
|
||||
check "guest is Arch Linux" "Arch Linux" "$out"
|
||||
|
||||
step "2. DESTROY: wipe /etc and /usr"
|
||||
"$VM_CLI" run "$VM" 'sudo rm -rf --no-preserve-root /etc /usr 2>/dev/null; true' 2>&1 | tail -3
|
||||
|
||||
step "3. Confirm the guest really is broken"
|
||||
out="$("$VM_CLI" run "$VM" 'cat /etc/os-release 2>&1 || echo GUEST_IS_BROKEN' 2>&1)"
|
||||
check "guest is destroyed" "GUEST_IS_BROKEN" "$out"
|
||||
|
||||
step "4. RESET -- one command"
|
||||
time_start=$SECONDS
|
||||
"$VM_CLI" reset "$VM"
|
||||
printf ' reset took %ds\n' "$(( SECONDS - time_start ))"
|
||||
|
||||
step "5. Confirm the sandbox is pristine again"
|
||||
out="$("$VM_CLI" run "$VM" 'cat /etc/os-release | head -1' 2>&1)"
|
||||
check "guest restored to Arch Linux" "Arch Linux" "$out"
|
||||
|
||||
step "6. THE HOST MUST BE UNTOUCHED"
|
||||
now_attrs="$(lsattr /etc/hosts 2>/dev/null | awk '{print $1}')"
|
||||
check "host /etc/hosts attributes unchanged" "$HOST_HOSTS_ATTRS" "$now_attrs"
|
||||
now_sha="$(sha256sum "$HOME/.local/share/vmbox/base/base.qcow2" 2>/dev/null | awk '{print $1}')"
|
||||
check "base image unmodified" "$BASE_SHA_BEFORE" "$now_sha"
|
||||
check "host /etc still exists" "PRESENT" "$([[ -d /etc && -d /usr ]] && echo PRESENT)"
|
||||
|
||||
printf '\n\033[0;34m=== RESULT: %d passed, %d failed\033[0m\n' "$pass" "$fail"
|
||||
exit $(( fail > 0 ? 1 : 0 ))
|
||||
63
vmbox/tests/shutdown_verdict_demo.sh
Executable file
63
vmbox/tests/shutdown_verdict_demo.sh
Executable file
@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# vmbox: the headline test -- "does shutdown.sh really shut the machine down?"
|
||||
#
|
||||
# Exercises every branch of the verdict table against a REAL guest, including
|
||||
# the failure modes. A verifier that has only ever returned "pass" is untested,
|
||||
# so the reboot / panic cases matter as much as the clean one.
|
||||
# ============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
VMBOX_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
||||
readonly VMBOX_ROOT
|
||||
readonly VM="${VM:-verdict}"
|
||||
readonly VM_CLI="$VMBOX_ROOT/bin/vm"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
|
||||
step() { printf '\n\033[0;34m=== %s\033[0m\n' "$*"; }
|
||||
expect() {
|
||||
local label="$1" want="$2" got="$3"
|
||||
if [[ "$got" == *"$want"* ]]; then
|
||||
printf '\033[0;32m PASS\033[0m %s\n' "$label"
|
||||
pass=$(( pass + 1 ))
|
||||
else
|
||||
printf '\033[0;31m FAIL\033[0m %s\n wanted verdict: %s\n got: %s\n' \
|
||||
"$label" "$want" "$got"
|
||||
fail=$(( fail + 1 ))
|
||||
fi
|
||||
}
|
||||
|
||||
reset_vm() { "$VM_CLI" reset "$VM" >/dev/null 2>&1 || "$VM_CLI" new "$VM" >/dev/null 2>&1; }
|
||||
|
||||
"$VM_CLI" rm "$VM" >/dev/null 2>&1
|
||||
"$VM_CLI" new "$VM" --rtc 2026-08-22T20:55:00
|
||||
|
||||
step "1. A genuine, clean poweroff"
|
||||
out="$("$VM_CLI" run "$VM" 'sudo systemctl poweroff' 2>&1)"
|
||||
printf '%s\n' "$out" | tail -4
|
||||
expect "clean poweroff is recognised" "clean poweroff" "$out"
|
||||
|
||||
step "2. A script that REBOOTS instead of powering off"
|
||||
reset_vm
|
||||
out="$("$VM_CLI" run "$VM" 'sudo systemctl reboot' 2>&1)"
|
||||
printf '%s\n' "$out" | tail -4
|
||||
expect "reboot is not mistaken for a shutdown" "REBOOTED" "$out"
|
||||
|
||||
step "3. A hard kernel panic"
|
||||
reset_vm
|
||||
out="$("$VM_CLI" run "$VM" 'echo c | sudo tee /proc/sysrq-trigger' 2>&1)"
|
||||
printf '%s\n' "$out" | tail -4
|
||||
expect "panic is distinguished from a clean stop" "PANIC" "$out"
|
||||
|
||||
step "4. A command that does NOT stop the machine"
|
||||
reset_vm
|
||||
out="$("$VM_CLI" run "$VM" 'echo still-here' 2>&1)"
|
||||
printf '%s\n' "$out" | tail -4
|
||||
expect "a live guest is reported as still running" "still running" "$out"
|
||||
|
||||
printf '\n\033[0;34m=== RESULT: %d passed, %d failed\033[0m\n' "$pass" "$fail"
|
||||
exit $(( fail > 0 ? 1 : 0 ))
|
||||
Loading…
Reference in New Issue
Block a user