338 lines
15 KiB
Bash
Executable File
338 lines
15 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# t-bank.sh — Command Bank feature: bank-lib.sh unit tests + pos-bank CLI
|
|
# integration tests.
|
|
#
|
|
# Storage contract: pipe-delimited name|description|command in $BANK_FILE.
|
|
# Parameter syntax: {param} placeholders in command templates.
|
|
|
|
run_test() {
|
|
local sandbox helper
|
|
sandbox="$(mksandbox bank)"
|
|
helper="$sandbox/helper.sh"
|
|
|
|
# ── Build a sourced helper that gives us bank-lib functions ──
|
|
cat > "$helper" <<'HELPER'
|
|
source "$ROOT/tests/bank-test-helper.sh"
|
|
HELPER
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Part A: bank-lib.sh unit tests
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
# A1: bank_add + bank_load — add two entries, verify arrays
|
|
(
|
|
BANK_FILE="$sandbox/a1.bank.env"
|
|
: > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_add "deploy" "Deploy to prod" "ssh prod 'cd /app && git pull'"
|
|
bank_add "backup" "Backup database" "pg_dump mydb | gzip > /tmp/db.sql.gz"
|
|
bank_load
|
|
# Check arrays
|
|
[ "${#BANK_NAMES[@]}" -eq 2 ] || { echo "FAIL A1: expected 2 names, got ${#BANK_NAMES[@]}"; exit 1; }
|
|
[ "${BANK_NAMES[0]}" = "deploy" ] || { echo "FAIL A1: name[0] mismatch"; exit 1; }
|
|
[ "${BANK_DESCS[0]}" = "Deploy to prod" ] || { echo "FAIL A1: desc[0] mismatch"; exit 1; }
|
|
[ "${BANK_CMDS[1]}" = "pg_dump mydb | gzip > /tmp/db.sql.gz" ] || { echo "FAIL A1: cmd[1] mismatch"; exit 1; }
|
|
echo "PASS A1: bank_add + bank_load populates arrays correctly"
|
|
) && printf ' PASS bank_add + bank_load populates arrays\n' \
|
|
|| printf ' FAIL bank_add + bank_load populates arrays\n'
|
|
|
|
# A2: bank_add duplicate — returns rc 1
|
|
(
|
|
BANK_FILE="$sandbox/a2.bank.env"
|
|
printf 'old|Old cmd|echo old\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_add "old" "New desc" "echo new"
|
|
exit $?
|
|
)
|
|
check_rc "bank_add duplicate returns rc 1" 1 $?
|
|
|
|
# A3: bank_remove — remove entry, verify gone
|
|
(
|
|
BANK_FILE="$sandbox/a3.bank.env"
|
|
printf 'aaa|desc a|cmd a\nbbb|desc b|cmd b\nccc|desc c|cmd c\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_remove "bbb"
|
|
bank_load
|
|
[ "${#BANK_NAMES[@]}" -eq 2 ] || { echo "FAIL A3: expected 2, got ${#BANK_NAMES[@]}"; exit 1; }
|
|
[ "${BANK_NAMES[0]}" = "aaa" ] || { echo "FAIL A3: name[0] mismatch"; exit 1; }
|
|
[ "${BANK_NAMES[1]}" = "ccc" ] || { echo "FAIL A3: name[1] mismatch"; exit 1; }
|
|
# Verify file on disk
|
|
grep -q '^bbb|' "$BANK_FILE" && { echo "FAIL A3: bbb still in file"; exit 1; }
|
|
echo "PASS A3: bank_remove"
|
|
) && printf ' PASS bank_remove removes entry\n' \
|
|
|| printf ' FAIL bank_remove removes entry\n'
|
|
|
|
# A4: bank_remove missing — returns rc 1
|
|
(
|
|
BANK_FILE="$sandbox/a4.bank.env"
|
|
printf 'xxx|desc|cmd\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_remove "nonexistent"
|
|
exit $?
|
|
)
|
|
check_rc "bank_remove missing returns rc 1" 1 $?
|
|
|
|
# A5: bank_update — update entry, verify changed
|
|
(
|
|
BANK_FILE="$sandbox/a5.bank.env"
|
|
printf 'mycmd|old desc|old cmd\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_update "mycmd" "new desc" "new cmd"
|
|
bank_load
|
|
[ "${BANK_DESCS[0]}" = "new desc" ] || { echo "FAIL A5: desc mismatch"; exit 1; }
|
|
[ "${BANK_CMDS[0]}" = "new cmd" ] || { echo "FAIL A5: cmd mismatch"; exit 1; }
|
|
# Verify on disk
|
|
grep -q 'new desc|new cmd' "$BANK_FILE" || { echo "FAIL A5: not persisted"; exit 1; }
|
|
echo "PASS A5: bank_update"
|
|
) && printf ' PASS bank_update updates entry\n' \
|
|
|| printf ' FAIL bank_update updates entry\n'
|
|
|
|
# A6: bank_update missing — returns rc 1
|
|
(
|
|
BANK_FILE="$sandbox/a6.bank.env"
|
|
printf 'exists|desc|cmd\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_update "nope" "desc" "cmd"
|
|
exit $?
|
|
)
|
|
check_rc "bank_update missing returns rc 1" 1 $?
|
|
|
|
# A7: bank_find — found sets BANK_IDX, not-found returns 1
|
|
(
|
|
BANK_FILE="$sandbox/a7.bank.env"
|
|
printf 'alpha|d1|c1\nbeta|d2|c2\ngamma|d3|c3\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
bank_find "beta"
|
|
[ "${BANK_IDX:-}" = "1" ] || { echo "FAIL A7: expected BANK_IDX=1, got ${BANK_IDX:-unset}"; exit 1; }
|
|
bank_find "nonexistent" && { echo "FAIL A7: should return 1 for missing"; exit 1; }
|
|
echo "PASS A7: bank_find"
|
|
) && printf ' PASS bank_find sets BANK_IDX / returns 1\n' \
|
|
|| printf ' FAIL bank_find sets BANK_IDX / returns 1\n'
|
|
|
|
# A8: bank_get — returns tab-separated output
|
|
(
|
|
BANK_FILE="$sandbox/a8.bank.env"
|
|
printf 'mytool|My Tool|echo hello\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
local out
|
|
out="$(bank_get "mytool")"
|
|
[ "$out" = "mytool My Tool echo hello" ] || { echo "FAIL A8: unexpected output [$out]"; exit 1; }
|
|
echo "PASS A8: bank_get"
|
|
) && printf ' PASS bank_get returns tab-separated output\n' \
|
|
|| printf ' FAIL bank_get returns tab-separated output\n'
|
|
|
|
# A9: bank_list_names — lists all names
|
|
(
|
|
BANK_FILE="$sandbox/a9.bank.env"
|
|
printf 'one|x|y\ntwo|a|b\nthree|c|d\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
local names
|
|
names="$(bank_list_names)"
|
|
local expected
|
|
expected="$(printf 'one\ntwo\nthree')"
|
|
[ "$names" = "$expected" ] || { echo "FAIL A9: expected [$expected] got [$names]"; exit 1; }
|
|
echo "PASS A9: bank_list_names"
|
|
) && printf ' PASS bank_list_names lists all names\n' \
|
|
|| printf ' FAIL bank_list_names lists all names\n'
|
|
|
|
# A10: bank_count — returns correct count
|
|
(
|
|
BANK_FILE="$sandbox/a10.bank.env"
|
|
printf 'a|1|2\nb|3|4\nc|5|6\nd|7|8\n' > "$BANK_FILE"
|
|
source "$helper"
|
|
local cnt
|
|
cnt="$(bank_count)"
|
|
[ "$cnt" -eq 4 ] || { echo "FAIL A10: expected 4, got $cnt"; exit 1; }
|
|
echo "PASS A10: bank_count"
|
|
) && printf ' PASS bank_count returns correct count\n' \
|
|
|| printf ' FAIL bank_count returns correct count\n'
|
|
|
|
# A11: bank_count — empty file returns 0
|
|
(
|
|
BANK_FILE="$sandbox/a11.bank.env"
|
|
: > "$BANK_FILE"
|
|
source "$helper"
|
|
local cnt
|
|
cnt="$(bank_count)"
|
|
[ "$cnt" -eq 0 ] || { echo "FAIL A11: expected 0, got $cnt"; exit 1; }
|
|
echo "PASS A11: bank_count empty"
|
|
) && printf ' PASS bank_count returns 0 for empty file\n' \
|
|
|| printf ' FAIL bank_count returns 0 for empty file\n'
|
|
|
|
# A12: bank_valid_name — valid names pass
|
|
(
|
|
source "$helper"
|
|
local fail=0
|
|
for n in "deploy" "my-cmd" "test_123" "A" "z9"; do
|
|
bank_valid_name "$n" || { fail=1; printf 'FAIL A12: %s should be valid\n' "$n" >&2; }
|
|
done
|
|
[ "$fail" -eq 0 ] || exit 1
|
|
echo "PASS A12: bank_valid_name accepts valid names"
|
|
) && printf ' PASS bank_valid_name accepts valid names\n' \
|
|
|| printf ' FAIL bank_valid_name accepts valid names\n'
|
|
|
|
# A13: bank_valid_name — invalid names fail
|
|
(
|
|
source "$helper"
|
|
local fail=0
|
|
for n in "" "1abc" "-start" "_under" "has space" "special!"; do
|
|
bank_valid_name "$n" && { fail=1; printf 'FAIL A13: %s should be invalid\n' "$n" >&2; }
|
|
done
|
|
[ "$fail" -eq 0 ] || exit 1
|
|
echo "PASS A13: bank_valid_name rejects invalid names"
|
|
) && printf ' PASS bank_valid_name rejects invalid names\n' \
|
|
|| printf ' FAIL bank_valid_name rejects invalid names\n'
|
|
|
|
# A14: _extract_params — extracts {param} names from template
|
|
(
|
|
source "$helper"
|
|
local params
|
|
params="$(_extract_params "echo {name} {path}")"
|
|
local expected
|
|
expected="$(printf 'name\npath')"
|
|
[ "$params" = "$expected" ] || { echo "FAIL A14: expected [$expected] got [$params]"; exit 1; }
|
|
echo "PASS A14: _extract_params"
|
|
) && printf ' PASS _extract_params extracts param names\n' \
|
|
|| printf ' FAIL _extract_params extracts param names\n'
|
|
|
|
# A15: _extract_params — no params returns empty
|
|
(
|
|
source "$helper"
|
|
local params
|
|
params="$(_extract_params "echo hello world")"
|
|
[ -z "$params" ] || { echo "FAIL A15: expected empty, got [$params]"; exit 1; }
|
|
echo "PASS A15: _extract_params empty"
|
|
) && printf ' PASS _extract_params returns empty for no params\n' \
|
|
|| printf ' FAIL _extract_params returns empty for no params\n'
|
|
|
|
# A16: _extract_params — deduplicates repeated params
|
|
(
|
|
source "$helper"
|
|
local params
|
|
params="$(_extract_params "cp {src} {dst} && ls {src}")"
|
|
local count
|
|
count="$(printf '%s' "$params" | grep -c '^src$' || true)"
|
|
[ "$count" -eq 1 ] || { echo "FAIL A16: expected 1 occurrence of src, got $count"; exit 1; }
|
|
echo "PASS A16: _extract_params deduplicates"
|
|
) && printf ' PASS _extract_params deduplicates\n' \
|
|
|| printf ' FAIL _extract_params deduplicates\n'
|
|
|
|
# A17: _substitute_params — replaces {param} with quoted values
|
|
(
|
|
source "$helper"
|
|
local result
|
|
result="$(_substitute_params "scp {file} user@host:{dest}" "file=/tmp/data.csv" "dest=/var/data")"
|
|
local expected='scp "/tmp/data.csv" user@host:"/var/data"'
|
|
[ "$result" = "$expected" ] || { echo "FAIL A17: expected [$expected] got [$result]"; exit 1; }
|
|
echo "PASS A17: _substitute_params"
|
|
) && printf ' PASS _substitute_params replaces params\n' \
|
|
|| printf ' FAIL _substitute_params replaces params\n'
|
|
|
|
# A18: bank_load — comments and blank lines are skipped
|
|
(
|
|
BANK_FILE="$sandbox/a18.bank.env"
|
|
cat > "$BANK_FILE" <<'BANK'
|
|
# Command Bank — managed by pos bank (do not hand-edit)
|
|
# Format: name|description|command
|
|
|
|
real|Real command|echo real
|
|
# another comment
|
|
|
|
# indented comment
|
|
|
|
BANK
|
|
source "$helper"
|
|
bank_load
|
|
[ "${#BANK_NAMES[@]}" -eq 1 ] || { echo "FAIL A18: expected 1, got ${#BANK_NAMES[@]}"; exit 1; }
|
|
[ "${BANK_NAMES[0]}" = "real" ] || { echo "FAIL A18: name mismatch"; exit 1; }
|
|
echo "PASS A18: bank_load skips comments and blanks"
|
|
) && printf ' PASS bank_load skips comments and blank lines\n' \
|
|
|| printf ' FAIL bank_load skips comments and blank lines\n'
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Part B: pos-bank CLI integration tests
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
local pos_bank="$ROOT/bin/pos-bank"
|
|
|
|
# B1: pos bank --help shows usage
|
|
test_run env BANK_FILE="$sandbox/b1.bank.env" "$pos_bank" --help
|
|
check_rc "pos bank --help exits 0" 0 "$TR_RC"
|
|
check_contains "pos bank --help shows Usage" "Usage:" "$TR_OUT"
|
|
check_contains "pos bank --help mentions subcommands" "Subcommands:" "$TR_OUT"
|
|
|
|
# B2: pos bank list on empty bank shows empty message
|
|
: > "$sandbox/b2.bank.env"
|
|
test_run env BANK_FILE="$sandbox/b2.bank.env" "$pos_bank" list
|
|
check_rc "pos bank list empty exits 0" 0 "$TR_RC"
|
|
check_contains "pos bank list empty message" "Command bank is empty" "$TR_OUT"
|
|
|
|
# B3: pos bank add — adds a command, verify in bank.env
|
|
: > "$sandbox/b3.bank.env"
|
|
test_run env BANK_FILE="$sandbox/b3.bank.env" "$pos_bank" add "disk-usage" "Check disk usage" "df -h"
|
|
check_rc "pos bank add exits 0" 0 "$TR_RC"
|
|
check_contains "pos bank add confirms" "Saved: disk-usage" "$TR_OUT"
|
|
# Verify the file
|
|
if grep -q '^disk-usage|Check disk usage|df -h$' "$sandbox/b3.bank.env"; then
|
|
printf ' PASS pos bank add persists to bank.env\n'
|
|
else
|
|
printf ' FAIL pos bank add did not persist to bank.env\n'
|
|
fi
|
|
|
|
# B4: pos bank add duplicate — fails
|
|
test_run env BANK_FILE="$sandbox/b3.bank.env" "$pos_bank" add "disk-usage" "dup" "echo dup"
|
|
check_not_contains "pos bank add duplicate errors" "0" "$TR_RC"
|
|
check_contains "pos bank add duplicate message" "already exists" "$TR_OUT"
|
|
|
|
# B5: pos bank show — shows command details
|
|
test_run env BANK_FILE="$sandbox/b3.bank.env" "$pos_bank" show "disk-usage"
|
|
check_rc "pos bank show exits 0" 0 "$TR_RC"
|
|
check_contains "pos bank show shows name" "Name: disk-usage" "$TR_OUT"
|
|
check_contains "pos bank show shows description" "Check disk usage" "$TR_OUT"
|
|
check_contains "pos bank show shows command" "df -h" "$TR_OUT"
|
|
|
|
# B6: pos bank show missing — fails
|
|
test_run env BANK_FILE="$sandbox/b3.bank.env" "$pos_bank" show "nonexistent"
|
|
check_not_contains "pos bank show missing exits non-zero" "0" "$TR_RC"
|
|
check_contains "pos bank show missing message" "not found" "$TR_OUT"
|
|
|
|
# B7: pos bank list after add — shows the entry
|
|
test_run env BANK_FILE="$sandbox/b3.bank.env" "$pos_bank" list
|
|
check_rc "pos bank list populated exits 0" 0 "$TR_RC"
|
|
check_contains "pos bank list shows header" "COMMAND BANK" "$TR_OUT"
|
|
check_contains "pos bank list shows entry" "disk-usage" "$TR_OUT"
|
|
|
|
# B8: pos bank remove — removes a command
|
|
test_run env BANK_FILE="$sandbox/b3.bank.env" "$pos_bank" remove "disk-usage"
|
|
check_rc "pos bank remove exits 0" 0 "$TR_RC"
|
|
check_contains "pos bank remove confirms" "Removed: disk-usage" "$TR_OUT"
|
|
# Verify gone
|
|
if grep -q '^disk-usage|' "$sandbox/b3.bank.env"; then
|
|
printf ' FAIL pos bank remove did not delete from bank.env\n'
|
|
else
|
|
printf ' PASS pos bank remove deletes from bank.env\n'
|
|
fi
|
|
|
|
# B9: pos bank remove missing — fails
|
|
: > "$sandbox/b9.bank.env"
|
|
test_run env BANK_FILE="$sandbox/b9.bank.env" "$pos_bank" remove "ghost"
|
|
check_not_contains "pos bank remove missing exits non-zero" "0" "$TR_RC"
|
|
check_contains "pos bank remove missing message" "not found" "$TR_OUT"
|
|
|
|
# B10: pos bank add with parameters — show lists them
|
|
: > "$sandbox/b10.bank.env"
|
|
test_run env BANK_FILE="$sandbox/b10.bank.env" "$pos_bank" add "convert" "Convert video" "ffmpeg -i {input} -q:v {quality} {output}"
|
|
check_rc "pos bank add with params exits 0" 0 "$TR_RC"
|
|
test_run env BANK_FILE="$sandbox/b10.bank.env" "$pos_bank" show "convert"
|
|
check_contains "show lists input param" "input" "$TR_OUT"
|
|
check_contains "show lists quality param" "quality" "$TR_OUT"
|
|
check_contains "show lists output param" "output" "$TR_OUT"
|
|
|
|
# B11: pos bank add invalid name — fails
|
|
: > "$sandbox/b11.bank.env"
|
|
test_run env BANK_FILE="$sandbox/b11.bank.env" "$pos_bank" add "1bad" "desc" "cmd"
|
|
check_not_contains "pos bank add invalid name exits non-zero" "0" "$TR_RC"
|
|
check_contains "pos bank add invalid name message" "Invalid name" "$TR_OUT"
|
|
}
|