diff options
| -rw-r--r-- | Makefile | 22 | ||||
| -rwxr-xr-x | scripts/setup-email.sh | 82 | ||||
| -rw-r--r-- | tests/test-setup-email.bats | 82 |
3 files changed, 147 insertions, 39 deletions
@@ -6,6 +6,7 @@ # make test-unit - Run unit tests only # make test-file FILE=test-foo.el - Run specific test file # make test-name TEST=test-foo-* - Run tests matching pattern +# make test-bash - Run the bats shell-script tests # make benchmark - Run performance benchmarks (:perf-tagged tests) # make coverage - Generate simplecov coverage report # make coverage-clean - Remove coverage report file @@ -31,6 +32,7 @@ EMACS_HOME = $(HOME)/.emacs.d UNIT_TESTS = $(filter-out $(TEST_DIR)/test-integration-%.el, $(wildcard $(TEST_DIR)/test-*.el)) INTEGRATION_TESTS = $(wildcard $(TEST_DIR)/test-integration-%.el) ALL_TESTS = $(UNIT_TESTS) $(INTEGRATION_TESTS) +BASH_TESTS = $(wildcard $(TEST_DIR)/*.bats) # Module files MODULE_FILES = $(wildcard $(MODULE_DIR)/*.el) @@ -42,7 +44,7 @@ EMACS_TEST = $(EMACS_BATCH) -L $(TEST_DIR) -L $(MODULE_DIR) # No colors - using plain text symbols instead .PHONY: help targets test test-all test-unit test-integration test-file test-name \ - benchmark coverage coverage-clean \ + test-bash benchmark coverage coverage-clean \ validate-parens validate-modules compile lint profile \ clean clean-compiled clean-tests reset @@ -61,6 +63,7 @@ help: @echo " make test-integration - Run integration tests only ($(words $(INTEGRATION_TESTS)) files)" @echo " make test-file FILE=<filename> - Run specific test file" @echo " make test-name TEST=<pattern> - Run tests matching pattern" + @echo " make test-bash - Run the bats shell-script tests ($(words $(BASH_TESTS)) files)" @echo " make benchmark - Run performance benchmarks (:perf-tagged)" @echo "" @echo " Coverage:" @@ -92,13 +95,28 @@ help: test: test-all test-all: - @echo "[i] Running all tests ($(words $(ALL_TESTS)) files)..." + @echo "[i] Running all tests ($(words $(ALL_TESTS)) Elisp files)..." @$(MAKE) test-unit @if [ $(words $(INTEGRATION_TESTS)) -gt 0 ]; then \ $(MAKE) test-integration; \ fi + @if [ $(words $(BASH_TESTS)) -gt 0 ] && command -v bats >/dev/null 2>&1; then \ + $(MAKE) test-bash; \ + fi @echo "✓ All tests complete" +test-bash: + @if [ $(words $(BASH_TESTS)) -eq 0 ]; then \ + echo "No bats tests found"; \ + exit 0; \ + fi + @if ! command -v bats >/dev/null 2>&1; then \ + echo "[!] bats not installed — skipping shell-script tests"; \ + exit 0; \ + fi + @echo "[i] Running bats shell-script tests ($(words $(BASH_TESTS)) files)..." + @bats $(BASH_TESTS) + test-unit: @echo "[i] Running unit tests ($(words $(UNIT_TESTS)) files)..." @echo "" diff --git a/scripts/setup-email.sh b/scripts/setup-email.sh index 5d461691..39423c97 100755 --- a/scripts/setup-email.sh +++ b/scripts/setup-email.sh @@ -75,41 +75,49 @@ decrypt_password() { fi } -# Decrypt Mail Passwords -# Skip if destination already exists, install or decrypt if missing. -echo "→ checking mail passwords..." -if [[ ! -d "$ENCRYPTED_PASSWORDS_DIR" ]]; then - echo " ✗ encrypted passwords directory not found: $ENCRYPTED_PASSWORDS_DIR" - exit 1 +main() { + # Decrypt Mail Passwords + # Skip if destination already exists, install or decrypt if missing. + echo "→ checking mail passwords..." + if [[ ! -d "$ENCRYPTED_PASSWORDS_DIR" ]]; then + echo " ✗ encrypted passwords directory not found: $ENCRYPTED_PASSWORDS_DIR" + exit 1 + fi + mkdir -p "$PASSWORD_DEST_DIR" + install_encrypted_password ".gmailpass.gpg" + decrypt_password ".cmailpass.gpg" ".cmailpass" + install_encrypted_password ".dmailpass.gpg" + + # Check All Prerequisites + [[ -x "$MBSYNC" ]] || { echo "ERROR: mbsync not found. Install 'isync'."; exit 1; } + [[ -x "$MU" ]] || { echo "ERROR: mu not found. Install 'mu'."; exit 1; } + [[ -d "$MU4EDIR" ]] || { echo "ERROR: mu4e elisp not found at $MU4EDIR. Install 'mu'."; exit 1; } + [[ -f "$MBSYNCRC" ]] || { echo "ERROR: '~/.mbsyncrc' missing."; exit 1; } + [[ -x "$MSMTP" ]] || { echo "ERROR: msmtp not found. Install 'msmtp'."; exit 1; } + [[ -f "$MSMTPRC" ]] || { echo "ERROR: '~/.msmtprc' missing."; exit 1; } + + # Ensure Mail Dirs Exist + mkdir -p "$GMAILDIR" "$CMAILDIR" "$DMAILDIR" + + # Initial Sync + echo "→ syncing all mail with mbsync ..." + "$MBSYNC" -aV + + # Init MU and Index Email + echo "→ initializing mu ..." + "$MU" init --maildir="$MAILROOT" \ + --my-address="craigmartinjennings@gmail.com" \ + --my-address="c@cjennings.net" \ + --my-address="craig.jennings@deepsat.com" + + echo "→ indexing mail ..." + "$MU" index + + echo "✅ Mail setup complete." +} + +# Run the setup when executed directly. Sourcing this file (for example from +# a bats test) just defines the helper functions above. +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" fi -mkdir -p "$PASSWORD_DEST_DIR" -install_encrypted_password ".gmailpass.gpg" -decrypt_password ".cmailpass.gpg" ".cmailpass" -install_encrypted_password ".dmailpass.gpg" - -# Check All Prerequisites -[[ -x "$MBSYNC" ]] || { echo "ERROR: mbsync not found. Install 'isync'."; exit 1; } -[[ -x "$MU" ]] || { echo "ERROR: mu not found. Install 'mu'."; exit 1; } -[[ -d "$MU4EDIR" ]] || { echo "ERROR: mu4e elisp not found at $MU4EDIR. Install 'mu'."; exit 1; } -[[ -f "$MBSYNCRC" ]] || { echo "ERROR: '~/.mbsyncrc' missing."; exit 1; } -[[ -x "$MSMTP" ]] || { echo "ERROR: msmtp not found. Install 'msmtp'."; exit 1; } -[[ -f "$MSMTPRC" ]] || { echo "ERROR: '~/.msmtprc' missing."; exit 1; } - -# Ensure Mail Dirs Exist -mkdir -p "$GMAILDIR" "$CMAILDIR" "$DMAILDIR" - -# Initial Sync -echo "→ syncing all mail with mbsync ..." -"$MBSYNC" -aV - -# Init MU and Index Email -echo "→ initializing mu ..." -"$MU" init --maildir="$MAILROOT" \ - --my-address="craigmartinjennings@gmail.com" \ - --my-address="c@cjennings.net" \ - --my-address="craig.jennings@deepsat.com" - -echo "→ indexing mail ..." -"$MU" index - -echo "✅ Mail setup complete." diff --git a/tests/test-setup-email.bats b/tests/test-setup-email.bats new file mode 100644 index 00000000..e42335e5 --- /dev/null +++ b/tests/test-setup-email.bats @@ -0,0 +1,82 @@ +#!/usr/bin/env bats +# Tests for the password helpers in scripts/setup-email.sh. +# +# `install_encrypted_password' copies a password file from the encrypted +# assets dir into PASSWORD_DEST_DIR; `decrypt_password' pipes one through +# `gpg -d' into PASSWORD_DEST_DIR. Both skip when the destination already +# exists and exit 1 when the source is missing. These tests source the +# script (which only defines the helpers — `main' runs only when the script +# is executed directly) and point the two directory vars at a per-test +# tmpdir, so nothing touches ~/.config or the real mail setup. + +setup() { + source "${BATS_TEST_DIRNAME}/../scripts/setup-email.sh" + ENCRYPTED_PASSWORDS_DIR="${BATS_TEST_TMPDIR}/src" + PASSWORD_DEST_DIR="${BATS_TEST_TMPDIR}/dest" + mkdir -p "$ENCRYPTED_PASSWORDS_DIR" "$PASSWORD_DEST_DIR" +} + +# --------------------------- install_encrypted_password --------------------- + +@test "install_encrypted_password: copies the source and locks it to 600" { + printf 'secret' > "$ENCRYPTED_PASSWORDS_DIR/.gmailpass.gpg" + run install_encrypted_password ".gmailpass.gpg" + [ "$status" -eq 0 ] + [ "$(cat "$PASSWORD_DEST_DIR/.gmailpass.gpg")" = "secret" ] + [ "$(stat -c '%a' "$PASSWORD_DEST_DIR/.gmailpass.gpg")" = "600" ] + [[ "$output" == *"created"* ]] +} + +@test "install_encrypted_password: skips and keeps an existing destination" { + printf 'new' > "$ENCRYPTED_PASSWORDS_DIR/.gmailpass.gpg" + printf 'kept' > "$PASSWORD_DEST_DIR/.gmailpass.gpg" + run install_encrypted_password ".gmailpass.gpg" + [ "$status" -eq 0 ] + [ "$(cat "$PASSWORD_DEST_DIR/.gmailpass.gpg")" = "kept" ] + [[ "$output" == *"already exists, skipping"* ]] +} + +@test "install_encrypted_password: exits 1 when source and destination both missing" { + run install_encrypted_password ".gmailpass.gpg" + [ "$status" -eq 1 ] + [[ "$output" == *"missing"* ]] + [ ! -e "$PASSWORD_DEST_DIR/.gmailpass.gpg" ] +} + +# ------------------------------- decrypt_password --------------------------- + +@test "decrypt_password: writes the decrypted plaintext and locks it to 600" { + printf 'ciphertext' > "$ENCRYPTED_PASSWORDS_DIR/.cmailpass.gpg" + gpg() { printf 'plaintext'; } # stub: no real GPG key here + run decrypt_password ".cmailpass.gpg" ".cmailpass" + [ "$status" -eq 0 ] + [ "$(cat "$PASSWORD_DEST_DIR/.cmailpass")" = "plaintext" ] + [ "$(stat -c '%a' "$PASSWORD_DEST_DIR/.cmailpass")" = "600" ] + [[ "$output" == *"created"* ]] +} + +@test "decrypt_password: skips and keeps an existing destination" { + printf 'ciphertext' > "$ENCRYPTED_PASSWORDS_DIR/.cmailpass.gpg" + printf 'kept' > "$PASSWORD_DEST_DIR/.cmailpass" + gpg() { printf 'plaintext'; } + run decrypt_password ".cmailpass.gpg" ".cmailpass" + [ "$status" -eq 0 ] + [ "$(cat "$PASSWORD_DEST_DIR/.cmailpass")" = "kept" ] + [[ "$output" == *"already exists, skipping"* ]] +} + +@test "decrypt_password: exits 1 when the source is missing" { + run decrypt_password ".cmailpass.gpg" ".cmailpass" + [ "$status" -eq 1 ] + [[ "$output" == *"missing"* ]] + [ ! -e "$PASSWORD_DEST_DIR/.cmailpass" ] +} + +@test "decrypt_password: removes the partial file and exits 1 when gpg fails" { + printf 'ciphertext' > "$ENCRYPTED_PASSWORDS_DIR/.cmailpass.gpg" + gpg() { return 1; } # stub: decryption failure + run decrypt_password ".cmailpass.gpg" ".cmailpass" + [ "$status" -eq 1 ] + [[ "$output" == *"failed to decrypt"* ]] + [ ! -e "$PASSWORD_DEST_DIR/.cmailpass" ] +} |
