From 19f4624749228fcbe385d1edf1d2542c036440ff Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 12 Apr 2026 23:58:01 -0400 Subject: refactor: extract pure RAID logic to lib/raid.sh with bats coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Peel the testable pieces of get_raid_level() out of the 1600-line installer monolith into installer/lib/raid.sh: - raid_valid_levels_for_count(count) — replaces the inline option-list builder in get_raid_level() - raid_is_valid(level, count) — useful for unattended-config validation - raid_usable_bytes(level, count, smallest, total) — usable-space math - raid_fault_tolerance(level, count) — max tolerable disk failures archangel now sources lib/raid.sh and uses raid_valid_levels_for_count for the fzf option list. Fzf preview subshell still inlines its own usable-bytes arithmetic (calling exported lib functions across preview subshells is fragile; left for a later pass). 30 bats tests in tests/unit/test_raid.bats cover the full enumeration table, every valid/invalid level-vs-count combo from 2 to 5 disks, mixed-size mirror, and unknown-level error paths. make test: 53/53. --- tests/unit/test_raid.bats | 199 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 tests/unit/test_raid.bats (limited to 'tests/unit/test_raid.bats') diff --git a/tests/unit/test_raid.bats b/tests/unit/test_raid.bats new file mode 100644 index 0000000..4e2f842 --- /dev/null +++ b/tests/unit/test_raid.bats @@ -0,0 +1,199 @@ +#!/usr/bin/env bats +# Unit tests for installer/lib/raid.sh + +setup() { + # shellcheck disable=SC1091 + source "${BATS_TEST_DIRNAME}/../../installer/lib/raid.sh" +} + +############################# +# raid_valid_levels_for_count +############################# + +@test "raid_valid_levels_for_count: 0 disks → empty output" { + run raid_valid_levels_for_count 0 + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "raid_valid_levels_for_count: 1 disk → empty output" { + run raid_valid_levels_for_count 1 + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "raid_valid_levels_for_count: 2 disks → mirror + stripe" { + run raid_valid_levels_for_count 2 + [ "$status" -eq 0 ] + [ "$output" = "mirror +stripe" ] +} + +@test "raid_valid_levels_for_count: 3 disks → + raidz1" { + run raid_valid_levels_for_count 3 + [ "$status" -eq 0 ] + [ "$output" = "mirror +stripe +raidz1" ] +} + +@test "raid_valid_levels_for_count: 4 disks → + raidz2" { + run raid_valid_levels_for_count 4 + [ "$status" -eq 0 ] + [ "$output" = "mirror +stripe +raidz1 +raidz2" ] +} + +@test "raid_valid_levels_for_count: 5 disks → + raidz3" { + run raid_valid_levels_for_count 5 + [ "$status" -eq 0 ] + [ "$output" = "mirror +stripe +raidz1 +raidz2 +raidz3" ] +} + +@test "raid_valid_levels_for_count: 8 disks → same as 5 (no new levels)" { + levels_5=$(raid_valid_levels_for_count 5) + levels_8=$(raid_valid_levels_for_count 8) + [ "$levels_5" = "$levels_8" ] +} + +############################# +# raid_is_valid +############################# + +@test "raid_is_valid: empty level + 1 disk = valid (no RAID)" { + run raid_is_valid "" 1 + [ "$status" -eq 0 ] +} + +@test "raid_is_valid: any level + 1 disk = invalid" { + run raid_is_valid mirror 1 + [ "$status" -eq 1 ] +} + +@test "raid_is_valid: mirror + 2 disks = valid" { + run raid_is_valid mirror 2 + [ "$status" -eq 0 ] +} + +@test "raid_is_valid: stripe + 2 disks = valid" { + run raid_is_valid stripe 2 + [ "$status" -eq 0 ] +} + +@test "raid_is_valid: raidz1 + 2 disks = invalid (need 3)" { + run raid_is_valid raidz1 2 + [ "$status" -eq 1 ] +} + +@test "raid_is_valid: raidz1 + 3 disks = valid" { + run raid_is_valid raidz1 3 + [ "$status" -eq 0 ] +} + +@test "raid_is_valid: raidz2 + 3 disks = invalid (need 4)" { + run raid_is_valid raidz2 3 + [ "$status" -eq 1 ] +} + +@test "raid_is_valid: raidz2 + 4 disks = valid" { + run raid_is_valid raidz2 4 + [ "$status" -eq 0 ] +} + +@test "raid_is_valid: raidz3 + 4 disks = invalid (need 5)" { + run raid_is_valid raidz3 4 + [ "$status" -eq 1 ] +} + +@test "raid_is_valid: raidz3 + 5 disks = valid" { + run raid_is_valid raidz3 5 + [ "$status" -eq 0 ] +} + +@test "raid_is_valid: unknown level = invalid" { + run raid_is_valid raidz99 5 + [ "$status" -eq 1 ] +} + +############################# +# raid_usable_bytes +############################# + +@test "raid_usable_bytes: mirror returns smallest disk's bytes" { + run raid_usable_bytes mirror 3 100 300 + [ "$status" -eq 0 ] + [ "$output" = "100" ] +} + +@test "raid_usable_bytes: stripe returns total bytes" { + run raid_usable_bytes stripe 3 100 300 + [ "$status" -eq 0 ] + [ "$output" = "300" ] +} + +@test "raid_usable_bytes: raidz1 = (n-1) * smallest" { + run raid_usable_bytes raidz1 3 100 300 + [ "$status" -eq 0 ] + [ "$output" = "200" ] +} + +@test "raid_usable_bytes: raidz2 = (n-2) * smallest" { + run raid_usable_bytes raidz2 4 100 400 + [ "$status" -eq 0 ] + [ "$output" = "200" ] +} + +@test "raid_usable_bytes: raidz3 = (n-3) * smallest" { + run raid_usable_bytes raidz3 5 100 500 + [ "$status" -eq 0 ] + [ "$output" = "200" ] +} + +@test "raid_usable_bytes: mixed-size mirror honors smallest (not average)" { + run raid_usable_bytes mirror 3 80 300 + [ "$status" -eq 0 ] + [ "$output" = "80" ] +} + +@test "raid_usable_bytes: unknown level returns status 1" { + run raid_usable_bytes bogus 3 100 300 + [ "$status" -eq 1 ] + [ -z "$output" ] +} + +############################# +# raid_fault_tolerance +############################# + +@test "raid_fault_tolerance: mirror of 3 = can lose 2" { + run raid_fault_tolerance mirror 3 + [ "$output" = "2" ] +} + +@test "raid_fault_tolerance: mirror of 5 = can lose 4" { + run raid_fault_tolerance mirror 5 + [ "$output" = "4" ] +} + +@test "raid_fault_tolerance: stripe = 0" { + run raid_fault_tolerance stripe 4 + [ "$output" = "0" ] +} + +@test "raid_fault_tolerance: raidz1/2/3 = 1/2/3 regardless of disk count" { + [ "$(raid_fault_tolerance raidz1 3)" = "1" ] + [ "$(raid_fault_tolerance raidz1 8)" = "1" ] + [ "$(raid_fault_tolerance raidz2 4)" = "2" ] + [ "$(raid_fault_tolerance raidz3 5)" = "3" ] +} + +@test "raid_fault_tolerance: unknown level returns status 1" { + run raid_fault_tolerance bogus 3 + [ "$status" -eq 1 ] +} -- cgit v1.2.3