diff options
Diffstat (limited to 'tests/unit/test_test_install.bats')
| -rw-r--r-- | tests/unit/test_test_install.bats | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/tests/unit/test_test_install.bats b/tests/unit/test_test_install.bats index bf43dd8..52ea037 100644 --- a/tests/unit/test_test_install.bats +++ b/tests/unit/test_test_install.bats @@ -318,6 +318,10 @@ error: failed to commit transaction (invalid or corrupted package (checksum)) @test "ssh_cmd picks up a caller-scoped INSTALLED_PASSWORD" { # Proves local-instead-of-export still reaches ssh_cmd: bash's dynamic # scoping exposes a caller's local to the functions it calls. + # + # timeout is stubbed as a pass-through because the real one is an external + # binary: it would exec the real sshpass and never see these stubs. + timeout() { shift; "$@"; } sshpass() { echo "$2"; } ssh() { :; } caller_with_local() { @@ -329,6 +333,7 @@ error: failed to commit transaction (invalid or corrupted package (checksum)) } @test "a caller-scoped INSTALLED_PASSWORD does not leak past a failed return" { + timeout() { shift; "$@"; } sshpass() { echo "$2"; } ssh() { :; } SSH_PASSWORD="live-iso-password" @@ -350,3 +355,150 @@ error: failed to commit transaction (invalid or corrupted package (checksum)) grep -qE '^[[:space:]]*local INSTALLED_PASSWORD=' "$src" ! grep -qE '^[[:space:]]*export INSTALLED_PASSWORD' "$src" } + +############################# +# config_encrypt_flag +############################# +# Decides which passphrase-entry path a reboot needs. It's the one pure piece +# of the boot-from-disk sequence, which is otherwise qemu orchestration, so it +# carries the precedence rules the rest of that sequence depends on. + +mkcfg() { + local f + f=$(mktemp) + printf '%s\n' "$@" > "$f" + echo "$f" +} + +@test "config_encrypt_flag reports luks for a LUKS config" { + local f; f=$(mkcfg 'LUKS_PASSPHRASE=secret' 'DISKS=/dev/vda') + [ "$(config_encrypt_flag "$f")" = "luks" ] + rm -f "$f" +} + +@test "config_encrypt_flag reports zfs for a ZFS-passphrase config" { + local f; f=$(mkcfg 'ZFS_PASSPHRASE=secret' 'DISKS=/dev/vda') + [ "$(config_encrypt_flag "$f")" = "zfs" ] + rm -f "$f" +} + +@test "config_encrypt_flag prefers luks when a config carries both" { + # Preserves the precedence the inline block had: LUKS is checked first, + # and a config with both is a misconfiguration rather than a real mode. + local f; f=$(mkcfg 'LUKS_PASSPHRASE=a' 'ZFS_PASSPHRASE=b') + [ "$(config_encrypt_flag "$f")" = "luks" ] + rm -f "$f" +} + +@test "config_encrypt_flag reports nothing when NO_ENCRYPT overrides a passphrase" { + # The test configs set a passphrase *and* NO_ENCRYPT=yes; sending a + # passphrase to an unencrypted boot would type it at a login prompt. + # + # Asserted through `run` on purpose. A bare [ -z "$(...)" ] also passes + # when the function doesn't exist, so it can't fail for the reason the + # test exists — checking status too makes absence register as 127. + local f; f=$(mkcfg 'ZFS_PASSPHRASE=testpass' 'NO_ENCRYPT=yes') + run config_encrypt_flag "$f" + [ "$status" -eq 0 ] + [ -z "$output" ] + rm -f "$f" +} + +@test "config_encrypt_flag reports nothing for an unencrypted config" { + local f; f=$(mkcfg 'DISKS=/dev/vda' 'HOSTNAME=x') + run config_encrypt_flag "$f" + [ "$status" -eq 0 ] + [ -z "$output" ] + rm -f "$f" +} + +############################# +# ssh_cmd timeout bound +############################# +# ConnectTimeout bounds the connection, not execution. On 2026-08-03 a remote +# `zfs destroy` blocked behind an uninterruptible txg_quiesce with the +# connection healthy, and the suite sat dead for 40 minutes. A wedged guest +# should cost one scenario, not the run. + +@test "ssh_cmd bounds the remote command with the default timeout" { + timeout() { echo "bound=$1"; } + run ssh_cmd true + [[ "$output" == "bound=$SSH_CMD_TIMEOUT" ]] +} + +@test "ssh_cmd honors a per-call timeout override" { + # run_install's installer call legitimately runs for many minutes and + # raises this; every other call keeps the short default. + timeout() { echo "bound=$1"; } + SSH_CMD_TIMEOUT=1800 run ssh_cmd true + [[ "$output" == "bound=1800" ]] +} + +############################# +# clear_archzfs_cache +############################# +# Exercised against an injected directory, never the real one. An earlier +# version of this block hardcoded the system path, so running the unit suite +# invoked `sudo rm -rf /var/cache/pacoloco/pkgs/archzfs` on the live machine. +# A unit test must not reach outside its sandbox. + +@test "clear_archzfs_cache is a no-op when the cache directory is absent" { + ARCHZFS_CACHE_DIR="$BATS_TEST_TMPDIR/absent" run clear_archzfs_cache + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "clear_archzfs_cache removes an existing cache directory" { + local dir="$BATS_TEST_TMPDIR/archzfs" + mkdir -p "$dir" + touch "$dir/archzfs.db" "$dir/zfs-dkms-1-1-x86_64.pkg.tar.zst" + # Stub sudo so the test needs no privilege and stays in its sandbox. + sudo() { shift; "$@"; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + [ "$status" -eq 0 ] + [ ! -d "$dir" ] +} + +@test "clear_archzfs_cache clears the db too, not just the package bodies" { + # Removing bodies while leaving a stale archzfs.db trades a checksum error + # for "Maximum file size exceeded" — same cause, new message. + local dir="$BATS_TEST_TMPDIR/db/archzfs" + mkdir -p "$dir" + touch "$dir/archzfs.db" + sudo() { shift; "$@"; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + [ ! -e "$dir/archzfs.db" ] +} + +@test "clear_archzfs_cache warns and succeeds when removal is denied" { + local dir="$BATS_TEST_TMPDIR/denied/archzfs" + mkdir -p "$dir" + sudo() { return 1; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + # Non-fatal: a run without root is still worth having, but must say so. + [ "$status" -eq 0 ] + [[ "$output" == *"needs root"* ]] + [ -d "$dir" ] +} + +@test "clear_archzfs_cache refuses a path that isn't shaped like the cache" { + # The removal runs `rm -rf` under sudo, so a mistyped override must cost a + # warning rather than the machine. + local dir="$BATS_TEST_TMPDIR/not-the-cache" + mkdir -p "$dir" + sudo() { echo "SUDO RAN"; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + [ "$status" -eq 0 ] + [[ "$output" == *"Refusing"* ]] + [[ "$output" != *"SUDO RAN"* ]] + [ -d "$dir" ] +} + +@test "clear_archzfs_cache refuses a top-level directory" { + sudo() { echo "SUDO RAN"; } + ARCHZFS_CACHE_DIR="/archzfs" run clear_archzfs_cache + # /archzfs won't exist, so this exits on the -d guard; the point is that + # neither guard lets a root-level path reach the removal. + [ "$status" -eq 0 ] + [[ "$output" != *"SUDO RAN"* ]] +} |
