aboutsummaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/test_zfssnapshot.bats153
1 files changed, 153 insertions, 0 deletions
diff --git a/tests/unit/test_zfssnapshot.bats b/tests/unit/test_zfssnapshot.bats
new file mode 100644
index 0000000..74e13cc
--- /dev/null
+++ b/tests/unit/test_zfssnapshot.bats
@@ -0,0 +1,153 @@
+#!/usr/bin/env bats
+# Unit tests for installer/zfssnapshot
+#
+# Coverage scope: pure-logic helpers and subcommand dispatch. The
+# subcommand bodies that shell out to zfs / fzf / arch-chroot are VM-
+# tested per testing-strategy.org.
+#
+# Sourcing zfssnapshot relies on the source-guard at the bottom of the
+# script: when sourced, function definitions load but main is not
+# called.
+
+setup() {
+ # shellcheck disable=SC1091
+ source "${BATS_TEST_DIRNAME}/../../installer/zfssnapshot"
+}
+
+#############################
+# sanitize_description
+#############################
+
+@test "sanitize_description lowercases mixed case" {
+ [ "$(sanitize_description 'Before Upgrade')" = "before_upgrade" ]
+}
+
+@test "sanitize_description converts spaces to underscores" {
+ [ "$(sanitize_description 'pre system update')" = "pre_system_update" ]
+}
+
+@test "sanitize_description leaves valid input unchanged" {
+ [ "$(sanitize_description 'before-upgrade')" = "before-upgrade" ]
+}
+
+@test "sanitize_description handles a single word" {
+ [ "$(sanitize_description 'experiment')" = "experiment" ]
+}
+
+#############################
+# validate_description
+#############################
+
+@test "validate_description accepts alphanumeric" {
+ run validate_description "abc123"
+ [ "$status" -eq 0 ]
+}
+
+@test "validate_description accepts hyphens" {
+ run validate_description "before-upgrade"
+ [ "$status" -eq 0 ]
+}
+
+@test "validate_description accepts underscores" {
+ run validate_description "pre_system_update"
+ [ "$status" -eq 0 ]
+}
+
+@test "validate_description rejects slashes" {
+ run validate_description "bad/name"
+ [ "$status" -ne 0 ]
+}
+
+@test "validate_description rejects spaces" {
+ run validate_description "two words"
+ [ "$status" -ne 0 ]
+}
+
+@test "validate_description rejects shell metacharacters" {
+ run validate_description "name; rm -rf"
+ [ "$status" -ne 0 ]
+}
+
+@test "validate_description rejects an empty string" {
+ run validate_description ""
+ [ "$status" -ne 0 ]
+}
+
+#############################
+# format_snapshot_name
+#############################
+# format_snapshot_name uses an injected timestamp (callers pass it in)
+# rather than calling date() inside the helper, so the test doesn't
+# need to mock the clock.
+
+@test "format_snapshot_name composes timestamp + description" {
+ [ "$(format_snapshot_name '2026-04-27_13-22-00' 'before-upgrade')" \
+ = "2026-04-27_13-22-00_before-upgrade" ]
+}
+
+#############################
+# main dispatch
+#############################
+# main routes the first positional arg to a cmd_* function. Tests
+# replace the cmd_* functions with mocks that record invocation, so
+# this layer's behavior (which subcommand for which arg) is what's
+# pinned, not the bodies.
+
+@test "main with no args shows help and exits 0" {
+ run main
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"Usage:"* ]]
+}
+
+@test "main --help shows help and exits 0" {
+ run main --help
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"Usage:"* ]]
+}
+
+@test "main -h shows help and exits 0" {
+ run main -h
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"Usage:"* ]]
+}
+
+@test "main help shows help and exits 0" {
+ run main help
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"Usage:"* ]]
+}
+
+@test "main list dispatches to cmd_list" {
+ cmd_list() { echo "called list with: $*"; }
+ run main list
+ [ "$status" -eq 0 ]
+ [[ "$output" == "called list with: " ]]
+}
+
+@test "main create dispatches to cmd_create with remaining args" {
+ cmd_create() { echo "called create with: $*"; }
+ run main create "before upgrade"
+ [ "$status" -eq 0 ]
+ [[ "$output" == "called create with: before upgrade" ]]
+}
+
+@test "main rollback dispatches to cmd_rollback with remaining args" {
+ cmd_rollback() { echo "called rollback with: $*"; }
+ run main rollback -s
+ [ "$status" -eq 0 ]
+ [[ "$output" == "called rollback with: -s" ]]
+}
+
+@test "main delete dispatches to cmd_delete" {
+ cmd_delete() { echo "called delete with: $*"; }
+ run main delete
+ [ "$status" -eq 0 ]
+ [[ "$output" == "called delete with: " ]]
+}
+
+@test "main rejects an unknown subcommand and exits non-zero" {
+ run main not-a-subcommand
+ [ "$status" -ne 0 ]
+ [[ "$output" == *"not-a-subcommand"* ]]
+ [[ "$output" == *"Usage:"* ]]
+}