From 1a261b0c220903c8bb628e7f2b94cf75a843f688 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 26 Apr 2026 01:09:01 -0500 Subject: test: expand bats coverage across installer modules Added unit tests for `disk.sh`, `btrfs.sh`, the archangel monolith's `gather_input` unattended branch, and filled gap cases in `config.sh`. The suite grew from 71 to 110 tests. `installer/lib/disk.sh` was completely uncovered. New `tests/unit/test_disk.bats` covers the four pure partition-path helpers (`get_efi_partition`, `get_root_partition`, `get_efi_partitions`, `get_root_partitions`) across SATA, virtio, and NVMe inputs, mixed arrays, and the empty-input behavior. Side-effecting functions in the same file (sgdisk, mkfs.fat, partprobe, and fzf wrappers) stay deliberately VM-tested. `installer/lib/btrfs.sh` had no bats coverage. New `tests/unit/test_btrfs.bats` covers `get_luks_devices`, the only pure helper in the file. It pins the asymmetric naming convention where the first device gets the bare `LUKS_MAPPER_NAME` and subsequent devices append the index. The archangel monolith was un-source-able for tests because its top-level code created a /tmp log file and redirected stdout via `exec > >(tee...)`, plus called `main "$@"` unconditionally at the bottom. I extracted the logging setup into an `init_logging` function called from `main`, and wrapped the main call in a `[[ "${BASH_SOURCE[0]}" == "${0}" ]]` guard. Sourcing the script now loads function definitions silently, with no log file and no banner. Running it directly works exactly as before. Verified both paths. That refactor unlocks `tests/unit/test_archangel.bats`, which covers `gather_input` in unattended mode. Required-field validation for HOSTNAME, TIMEZONE, ROOT_PASSWORD, and DISKS. Optional-field defaulting (FILESYSTEM to zfs, LOCALE to en_US.UTF-8, KEYMAP to us, ENABLE_SSH to yes). Filesystem-specific encryption checks (ZFS_PASSPHRASE required when not NO_ENCRYPT, same for LUKS_PASSPHRASE on Btrfs). Filesystem validity. RAID_LEVEL defaulting for multi-disk installs. The interactive branch stays out of scope per the testing-strategy policy. `tests/unit/test_config.bats` got five gap tests: `check_config` when CONFIG_FILE is set, `validate_config` against a non-block-device entry (e.g. /dev/null) and a missing path, and `parse_args` accepting `--color` and `--config-file` together in either order. `testing-strategy.org` got an expanded "What bats does NOT cover" section. The doc previously named six tools (mkfs, cryptsetup, zpool create, pacstrap, arch-chroot, grub-install). The new list adds sgdisk, partprobe, blkid, mkfs.fat, mkfs.btrfs, snapper, efibootmgr, mount, umount, findmnt, mountpoint, and fzf. It also names the conditions (root needed, real /dev or /sys state) that make a function VM-only. The coverage table at the top now lists the three new test files. No behavior change in production code. The init_logging extraction preserves the existing log path and banner format byte-for-byte. --- installer/archangel | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'installer') diff --git a/installer/archangel b/installer/archangel index a40710b..b2fa2c0 100755 --- a/installer/archangel +++ b/installer/archangel @@ -61,16 +61,21 @@ RAID_LEVEL="" # "", "mirror", "raidz1", "raidz2", "raidz3" ENABLE_SSH="yes" # Enable SSH with root login (default yes for headless) NO_ENCRYPT="no" # Skip ZFS encryption (for testing only) -# Logging -LOGFILE="/tmp/archangel-$(date +'%Y-%m-%d-%H-%M-%S').log" -exec > >(tee -a "$LOGFILE") 2>&1 +# Logging — initialized by init_logging() at install time only. Sourcing +# this script (e.g. from a bats test) loads the function definitions +# without redirecting stdout or creating a log file in /tmp. +LOGFILE="" -# Log header with timestamp -echo "" -echo "================================================================================" -echo "archangel started @ $(date +'%Y-%m-%d %H:%M:%S')" -echo "================================================================================" -echo "" +init_logging() { + LOGFILE="/tmp/archangel-$(date +'%Y-%m-%d-%H-%M-%S').log" + exec > >(tee -a "$LOGFILE") 2>&1 + + echo "" + echo "================================================================================" + echo "archangel started @ $(date +'%Y-%m-%d %H:%M:%S')" + echo "================================================================================" + echo "" +} # Output functions now in lib/common.sh # Config functions now in lib/config.sh @@ -1385,6 +1390,7 @@ print_btrfs_summary() { ############################# main() { + init_logging parse_args "$@" preflight_checks check_config @@ -1490,4 +1496,9 @@ install_btrfs() { trap 'error "Installation interrupted!"' INT TERM -main "$@" +# Only invoke main when archangel is executed directly. When sourced +# (e.g. from a bats test) the function definitions load but main does +# not run, so tests can exercise individual helpers like gather_input. +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi -- cgit v1.2.3