#!/usr/bin/env bash # Unit tests for prompt helper functions in lib/prompt.sh. # # All interactive functions write prompts to stderr and read from stdin. # Tests pipe input via stdin and capture stdout, suppressing stderr. TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(dirname "$TESTS_DIR")" source "$TESTS_DIR/framework.sh" source "$ROOT_DIR/lib/colors.sh" source "$ROOT_DIR/lib/validate.sh" source "$ROOT_DIR/lib/prompt.sh" # Helpers: feed stdin to a function and capture its stdout feed() { # feed [args...] local input="$1"; shift printf "%s" "$input" | "$@" 2>/dev/null } # --------------------------------------------------------------------------- suite "_prompt_enum: numeric selection" # --------------------------------------------------------------------------- assert_eq "a" "$(feed "1"$'\n' _prompt_enum "V" "a,b,c" "a")" "choice 1 -> first option" assert_eq "b" "$(feed "2"$'\n' _prompt_enum "V" "a,b,c" "a")" "choice 2 -> second option" assert_eq "c" "$(feed "3"$'\n' _prompt_enum "V" "a,b,c" "a")" "choice 3 -> third option" # --------------------------------------------------------------------------- suite "_prompt_enum: string selection" # --------------------------------------------------------------------------- assert_eq "a" "$(feed "a"$'\n' _prompt_enum "V" "a,b,c" "")" "string 'a' selects a" assert_eq "b" "$(feed "b"$'\n' _prompt_enum "V" "a,b,c" "")" "string 'b' selects b" assert_eq "c" "$(feed "c"$'\n' _prompt_enum "V" "a,b,c" "")" "string 'c' selects c" # --------------------------------------------------------------------------- suite "_prompt_enum: empty input uses default" # --------------------------------------------------------------------------- assert_eq "a" "$(feed $'\n' _prompt_enum "V" "a,b,c" "a")" "empty -> first default" assert_eq "b" "$(feed $'\n' _prompt_enum "V" "a,b,c" "b")" "empty -> middle default" assert_eq "c" "$(feed $'\n' _prompt_enum "V" "a,b,c" "c")" "empty -> last default" # --------------------------------------------------------------------------- suite "_prompt_enum: invalid choice then valid" # --------------------------------------------------------------------------- # Send out-of-range number, then a valid number assert_eq "b" "$(printf "99\n2\n" | _prompt_enum "V" "a,b,c" "a" 2>/dev/null)" \ "invalid index followed by valid index" # Send unknown string, then valid string assert_eq "c" "$(printf "zzz\nc\n" | _prompt_enum "V" "a,b,c" "a" 2>/dev/null)" \ "invalid string followed by valid string" # --------------------------------------------------------------------------- suite "_prompt_enum: real-world APP_ENV" # --------------------------------------------------------------------------- assert_eq "development" \ "$(feed $'\n' _prompt_enum "APP_ENV" "development,staging,production" "development")" \ "empty -> development (default)" assert_eq "production" \ "$(feed "3"$'\n' _prompt_enum "APP_ENV" "development,staging,production" "development")" \ "choice 3 -> production" assert_eq "staging" \ "$(feed "staging"$'\n' _prompt_enum "APP_ENV" "development,staging,production" "development")" \ "string 'staging' -> staging" # --------------------------------------------------------------------------- suite "_prompt_bool: explicit inputs" # --------------------------------------------------------------------------- assert_eq "true" "$(feed "y"$'\n' _prompt_bool "")" "y -> true" assert_eq "true" "$(feed "yes"$'\n' _prompt_bool "")" "yes -> true" assert_eq "true" "$(feed "true"$'\n' _prompt_bool "")" "true -> true" assert_eq "true" "$(feed "1"$'\n' _prompt_bool "")" "1 -> true" assert_eq "false" "$(feed "n"$'\n' _prompt_bool "")" "n -> false" assert_eq "false" "$(feed "no"$'\n' _prompt_bool "")" "no -> false" assert_eq "false" "$(feed "false"$'\n' _prompt_bool "")" "false -> false" assert_eq "false" "$(feed "0"$'\n' _prompt_bool "")" "0 -> false" # --------------------------------------------------------------------------- suite "_prompt_bool: empty input uses default" # --------------------------------------------------------------------------- assert_eq "true" "$(feed $'\n' _prompt_bool "true")" "empty -> true default" assert_eq "true" "$(feed $'\n' _prompt_bool "yes")" "empty -> yes default normalizes to true" assert_eq "false" "$(feed $'\n' _prompt_bool "false")" "empty -> false default" assert_eq "false" "$(feed $'\n' _prompt_bool "no")" "empty -> no default normalizes to false" # --------------------------------------------------------------------------- suite "_prompt_bool: invalid then valid" # --------------------------------------------------------------------------- assert_eq "true" "$(printf "banana\ny\n" | _prompt_bool "" 2>/dev/null)" \ "invalid input followed by y" assert_eq "false" "$(printf "maybe\nn\n" | _prompt_bool "" 2>/dev/null)" \ "invalid input followed by n" # --------------------------------------------------------------------------- suite "_prompt_secret: matching values accepted" # --------------------------------------------------------------------------- assert_eq "mysecret" \ "$(printf "mysecret\nmysecret\n" | _prompt_secret "V" "" 2>/dev/null)" \ "matching entries return value" assert_eq "p@ss!#\$word" \ "$(printf 'p@ss!#$word\np@ss!#$word\n' | _prompt_secret "V" "" 2>/dev/null)" \ "special characters in secret preserved" # --------------------------------------------------------------------------- suite "_prompt_secret: empty input uses default" # --------------------------------------------------------------------------- assert_eq "defaultsecret" \ "$(printf "\n" | _prompt_secret "V" "defaultsecret" 2>/dev/null)" \ "empty input returns default" # --------------------------------------------------------------------------- suite "_prompt_secret: mismatch retries" # --------------------------------------------------------------------------- # First pair mismatches, second pair matches assert_eq "correct" \ "$(printf "wrong\nmismatch\ncorrect\ncorrect\n" | _prompt_secret "V" "" 2>/dev/null)" \ "mismatch retries until match" # --------------------------------------------------------------------------- suite "prompt_variable: string type stores value" # --------------------------------------------------------------------------- # Use process substitution (<(...)) so prompt_variable runs in the current # shell and its variable assignments (ENV_VALUE_*) persist. ENV_VARS=("MYVAR") ENV_DESC_MYVAR="A test variable" ENV_TYPE_MYVAR="string" ENV_REQUIRED_MYVAR="false" ENV_DEFAULT_MYVAR="default_val" # Accept default prompt_variable "MYVAR" < <(printf "\n") 2>/dev/null assert_eq "default_val" "$ENV_VALUE_MYVAR" "empty input stores default" # Provide a value prompt_variable "MYVAR" < <(printf "custom\n") 2>/dev/null assert_eq "custom" "$ENV_VALUE_MYVAR" "typed value stored" # --------------------------------------------------------------------------- suite "prompt_variable: number type re-prompts on invalid input" # --------------------------------------------------------------------------- ENV_TYPE_MYVAR="number" ENV_DEFAULT_MYVAR="42" prompt_variable "MYVAR" < <(printf "abc\n99\n") 2>/dev/null assert_eq "99" "$ENV_VALUE_MYVAR" "invalid number re-prompts, then stores valid" # --------------------------------------------------------------------------- suite "prompt_variable: bool type" # --------------------------------------------------------------------------- ENV_TYPE_MYVAR="bool" ENV_DEFAULT_MYVAR="false" prompt_variable "MYVAR" < <(printf "y\n") 2>/dev/null assert_eq "true" "$ENV_VALUE_MYVAR" "bool y stored as true" prompt_variable "MYVAR" < <(printf "\n") 2>/dev/null assert_eq "false" "$ENV_VALUE_MYVAR" "bool empty uses false default" # --------------------------------------------------------------------------- suite "prompt_variable: enum type" # --------------------------------------------------------------------------- ENV_TYPE_MYVAR="enum:x,y,z" ENV_DEFAULT_MYVAR="x" prompt_variable "MYVAR" < <(printf "2\n") 2>/dev/null assert_eq "y" "$ENV_VALUE_MYVAR" "enum choice 2 stores y" prompt_variable "MYVAR" < <(printf "\n") 2>/dev/null assert_eq "x" "$ENV_VALUE_MYVAR" "enum empty uses default x" print_summary