From d2cd3b1b260246577e199824829fe07d825dd0ab Mon Sep 17 00:00:00 2001 From: Tim Byrne Date: Wed, 23 Mar 2016 19:18:33 -0500 Subject: [PATCH] Add tests Tests will be run using `bats`. --- Makefile | 4 + test/000_unit_syntax.bats | 11 + test/001_unit_configure_paths.bats | 66 +++++ test/002_unit_yadm_dir.bats | 31 +++ test/100_accept_version.bats | 22 ++ test/101_accept_help.bats | 32 +++ test/102_accept_clean.bats | 18 ++ test/103_accept_git.bats | 95 +++++++ test/104_accept_init.bats | 177 +++++++++++++ test/105_accept_clone.bats | 175 +++++++++++++ test/106_accept_config.bats | 122 +++++++++ test/107_accept_list.bats | 92 +++++++ test/108_accept_alt.bats | 160 ++++++++++++ test/109_accept_encryption.bats | 393 +++++++++++++++++++++++++++++ test/110_accept_perms.bats | 179 +++++++++++++ test/common.bash | 151 +++++++++++ 16 files changed, 1728 insertions(+) create mode 100644 test/000_unit_syntax.bats create mode 100644 test/001_unit_configure_paths.bats create mode 100644 test/002_unit_yadm_dir.bats create mode 100644 test/100_accept_version.bats create mode 100644 test/101_accept_help.bats create mode 100644 test/102_accept_clean.bats create mode 100644 test/103_accept_git.bats create mode 100644 test/104_accept_init.bats create mode 100644 test/105_accept_clone.bats create mode 100644 test/106_accept_config.bats create mode 100644 test/107_accept_list.bats create mode 100644 test/108_accept_alt.bats create mode 100644 test/109_accept_encryption.bats create mode 100644 test/110_accept_perms.bats create mode 100644 test/common.bash diff --git a/Makefile b/Makefile index 48376cf..20ff9aa 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ pdf: @sleep 1 @rm yadm.ps +.PHONY: test +test: + @bats test + man: groff -man -Tascii ./yadm.1 | less diff --git a/test/000_unit_syntax.bats b/test/000_unit_syntax.bats new file mode 100644 index 0000000..e115281 --- /dev/null +++ b/test/000_unit_syntax.bats @@ -0,0 +1,11 @@ +load common +load_fixtures + +@test "Syntax check" { + echo " + $T_YADM must parse correctly + " + + #; check the syntax of yadm + bash -n $T_YADM +} diff --git a/test/001_unit_configure_paths.bats b/test/001_unit_configure_paths.bats new file mode 100644 index 0000000..4904fba --- /dev/null +++ b/test/001_unit_configure_paths.bats @@ -0,0 +1,66 @@ +load common +load_fixtures + +@test "configure_paths() (standard YADM_DIR)" { + echo " + Correct paths should be defined + YADM_REPO=$DEFAULT_YADM_DIR/$DEFAULT_REPO + YADM_CONFIG=$DEFAULT_YADM_DIR/$DEFAULT_CONFIG + YADM_ENCRYPT=$DEFAULT_YADM_DIR/$DEFAULT_ENCRYPT + YADM_ARCHIVE=$DEFAULT_YADM_DIR/$DEFAULT_ARCHIVE + GIT_DIR=$DEFAULT_YADM_DIR/$DEFAULT_REPO + " + + #; load yadm functions + YADM_TEST=1 source $T_YADM + + #; configure the paths + configure_paths + + echo "CONFIGURED PATHS:" + echo " YADM_REPO:$YADM_REPO" + echo " YADM_CONFIG:$YADM_CONFIG" + echo "YADM_ENCRYPT:$YADM_ENCRYPT" + echo "YADM_ARCHIVE:$YADM_ARCHIVE" + echo " GIT_DIR:$GIT_DIR" + + #; test value of configured paths + [ "$DEFAULT_YADM_DIR/$DEFAULT_REPO" = "$YADM_REPO" ] + [ "$DEFAULT_YADM_DIR/$DEFAULT_CONFIG" = "$YADM_CONFIG" ] + [ "$DEFAULT_YADM_DIR/$DEFAULT_ENCRYPT" = "$YADM_ENCRYPT" ] + [ "$DEFAULT_YADM_DIR/$DEFAULT_ARCHIVE" = "$YADM_ARCHIVE" ] + [ "$DEFAULT_YADM_DIR/$DEFAULT_REPO" = "$GIT_DIR" ] +} + +@test "configure_paths() (custom YADM_DIR)" { + echo " + Correct paths should be defined + YADM_REPO=$T_DIR_YADM/$DEFAULT_REPO + YADM_CONFIG=$T_DIR_YADM/$DEFAULT_CONFIG + YADM_ENCRYPT=$T_DIR_YADM/$DEFAULT_ENCRYPT + YADM_ARCHIVE=$T_DIR_YADM/$DEFAULT_ARCHIVE + GIT_DIR=$T_DIR_YADM/$DEFAULT_REPO + " + + #; load yadm functions + YADM_TEST=1 source $T_YADM + + #; configure the paths + TEST_ARGS=(-Y $T_DIR_YADM) + process_global_args ${TEST_ARGS[*]} + configure_paths + + echo "CONFIGURED PATHS:" + echo " YADM_REPO:$YADM_REPO" + echo " YADM_CONFIG:$YADM_CONFIG" + echo "YADM_ENCRYPT:$YADM_ENCRYPT" + echo "YADM_ARCHIVE:$YADM_ARCHIVE" + echo " GIT_DIR:$GIT_DIR" + + #; test value of configured paths + [ "$T_DIR_YADM/$DEFAULT_REPO" = "$YADM_REPO" ] + [ "$T_DIR_YADM/$DEFAULT_CONFIG" = "$YADM_CONFIG" ] + [ "$T_DIR_YADM/$DEFAULT_ENCRYPT" = "$YADM_ENCRYPT" ] + [ "$T_DIR_YADM/$DEFAULT_ARCHIVE" = "$YADM_ARCHIVE" ] + [ "$T_DIR_YADM/$DEFAULT_REPO" = "$GIT_DIR" ] +} diff --git a/test/002_unit_yadm_dir.bats b/test/002_unit_yadm_dir.bats new file mode 100644 index 0000000..d223bf6 --- /dev/null +++ b/test/002_unit_yadm_dir.bats @@ -0,0 +1,31 @@ +load common +load_fixtures + +@test "Default YADM_DIR" { + echo " + YADM_DIR should default to \$HOME/.yadm + " + + #; load yadm functions + YADM_TEST=1 source $T_YADM + + #; test value of YADM_DIR + [ "$HOME/.yadm" = "$YADM_DIR" ] +} + +@test "Override default YADM_DIR" { + echo " + Override YADM_DIR using -Y $T_DIR_YADM + YADM_DIR should become $T_DIR_YADM + " + + #; load yadm functions + YADM_TEST=1 source $T_YADM + + #; call process_global_args() with -Y + TEST_ARGS=(-Y $T_DIR_YADM) + process_global_args ${TEST_ARGS[*]} + + #; test value of YADM_DIR + [ "$T_DIR_YADM" = "$YADM_DIR" ] +} diff --git a/test/100_accept_version.bats b/test/100_accept_version.bats new file mode 100644 index 0000000..da7a6e5 --- /dev/null +++ b/test/100_accept_version.bats @@ -0,0 +1,22 @@ +load common +load_fixtures + +@test "Command 'version'" { + echo " + When 'version' command is provided, + Print the current version with format 'yadm x.xx' + Exit with 0 + " + + #; run yadm with 'version' command + run $T_YADM version + + #; load yadm variables (including VERSION) + YADM_TEST=1 source $T_YADM + + #; validate status and output + [ $status -eq 0 ] + [ "$output" = "yadm $VERSION" ] + version_regex="^yadm [[:digit:]\.]+$" + [[ "$output" =~ $version_regex ]] +} diff --git a/test/101_accept_help.bats b/test/101_accept_help.bats new file mode 100644 index 0000000..3104fd8 --- /dev/null +++ b/test/101_accept_help.bats @@ -0,0 +1,32 @@ +load common +load_fixtures + +@test "Missing command" { + echo " + When no command is provided, + Produce usage instructions + Exit with 1 + " + + #; run yadm with no command + run $T_YADM + + #; validate status and output + [ $status -eq 1 ] + [[ "${lines[0]}" =~ ^Usage: ]] +} + +@test "Command 'help'" { + echo " + When 'help' command is provided, + Produce usage instructions + Exit with value 1 + " + + #; run yadm with 'help' command + run $T_YADM help + + #; validate status and output + [ $status -eq 1 ] + [[ "${lines[0]}" =~ ^Usage: ]] +} diff --git a/test/102_accept_clean.bats b/test/102_accept_clean.bats new file mode 100644 index 0000000..d6c3efb --- /dev/null +++ b/test/102_accept_clean.bats @@ -0,0 +1,18 @@ +load common +load_fixtures + +@test "Command 'clean'" { + echo " + When 'clean' command is provided, + Do nothing, this is a dangerous Git command when managing dot files + Report the command as disabled + Exit with 1 + " + + #; run yadm with 'clean' command + run $T_YADM clean + + #; validate status and output + [ $status -eq 1 ] + [[ "${lines[0]}" =~ disabled ]] +} diff --git a/test/103_accept_git.bats b/test/103_accept_git.bats new file mode 100644 index 0000000..6857e7b --- /dev/null +++ b/test/103_accept_git.bats @@ -0,0 +1,95 @@ +load common +load_fixtures + +IN_REPO=(.bash_profile .vimrc) + +function setup_environment() { + destroy_tmp + build_repo "${IN_REPO[@]}" +} + +@test "Passthru unknown commands to Git" { + echo " + When the command 'bogus' is provided + Report bogus is not a command + Exit with 0 + " + + #; start fresh + setup_environment + + #; run bogus + run $T_YADM_Y bogus + + #; validate status and output + [ "$status" -eq 0 ] + [[ "$output" =~ .bogus..is.not.a.git.command ]] +} + +@test "Git command 'add'" { + echo " + When the command 'add' is provided + Files are added to the index + Exit with 0 + " + + #; start fresh + setup_environment + + #; create a testfile + local testfile="$T_DIR_WORK/testfile" + echo "$testfile" > $testfile + + #; run add + run $T_YADM_Y add -v "$testfile" + + #; validate status and output + [ "$status" -eq 0 ] + [ "$output" = "add 'testfile'" ] +} + +@test "Git command 'status'" { + echo " + When the command 'status' is provided + Added files are shown + Exit with 0 + " + + #; run status + run $T_YADM_Y status + + #; validate status and output + [ "$status" -eq 0 ] + [[ "$output" =~ new\ file:[[:space:]]+testfile ]] +} + +@test "Git command 'commit'" { + echo " + When the command 'commit' is provided + Index is commited + Exit with 0 + " + + #; run commit + run $T_YADM_Y commit -m 'Add testfile' + + #; validate status and output + [ "$status" -eq 0 ] + [[ "${lines[1]}" =~ 1\ file\ changed ]] + [[ "${lines[1]}" =~ 1\ insertion ]] +} + +@test "Git command 'log'" { + echo " + When the command 'log' is provided + Commits are shown + Exit with 0 + " + + #; run log + run $T_YADM_Y log --oneline + + #; validate status and output + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ Add\ testfile ]] +} diff --git a/test/104_accept_init.bats b/test/104_accept_init.bats new file mode 100644 index 0000000..2c603c4 --- /dev/null +++ b/test/104_accept_init.bats @@ -0,0 +1,177 @@ +load common +load_fixtures + +setup() { + destroy_tmp + create_worktree "$T_DIR_WORK" +} + +@test "Command 'init'" { + echo " + When 'init' command is provided, + Create new repo with attributes: + - 0600 permissions + - not bare + - worktree = \$HOME + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as initialized + Exit with 0 + " + + #; run init + run $T_YADM_Y init + + #; validate status and output + [ $status -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$HOME" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true +} + +@test "Command 'init' -w (alternate worktree)" { + echo " + When 'init' command is provided, + and '-w' is provided, + Create new repo with attributes: + - 0600 permissions + - not bare + - worktree = \$YADM_WORK + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as initialized + Exit with 0 + " + + #; run init + run $T_YADM_Y init -w "$T_DIR_WORK" + + #; validate status and output + [ $status -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$T_DIR_WORK" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true +} + +@test "Command 'init' (existing repo)" { + echo " + When 'init' command is provided, + and a repo already exists, + Refuse to create a new repo + Exit with 1 + " + + #; create existing repo content + mkdir -p $T_DIR_REPO + local testfile="$T_DIR_REPO/testfile" + touch "$testfile" + + #; run init + run $T_YADM_Y init + + #; validate status and output + [ $status -eq 1 ] + [[ "$output" =~ already.exists ]] + + #; verify existing repo is intact + if [ ! -e $testfile ]; then + echo "ERROR: existing repo has been changed" + return 1 + fi + +} + +@test "Command 'init' -f (force overwrite repo)" { + echo " + When 'init' command is provided, + and '-f' is provided + and a repo already exists, + Remove existing repo + Create new repo with attributes: + - 0600 permissions + - not bare + - worktree = \$HOME + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as initialized + Exit with 0 + " + + #; create existing repo content + mkdir -p $T_DIR_REPO + local testfile="$T_DIR_REPO/testfile" + touch "$testfile" + + #; run init + run $T_YADM_Y init -f + + #; validate status and output + [ $status -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; verify existing repo is gone + if [ -e $testfile ]; then + echo "ERROR: existing repo files remain" + return 1 + fi + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$HOME" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true +} + +@test "Command 'init' -f -w (force overwrite repo with alternate worktree)" { + echo " + When 'init' command is provided, + and '-f' is provided + and '-w' is provided + and a repo already exists, + Remove existing repo + Create new repo with attributes: + - 0600 permissions + - not bare + - worktree = \$YADM_WORK + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as initialized + Exit with 0 + " + + #; create existing repo content + mkdir -p $T_DIR_REPO + local testfile="$T_DIR_REPO/testfile" + touch "$testfile" + + #; run init + run $T_YADM_Y init -f -w "$T_DIR_WORK" + + #; validate status and output + [ $status -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; verify existing repo is gone + if [ -e $testfile ]; then + echo "ERROR: existing repo files remain" + return 1 + fi + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$T_DIR_WORK" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true +} diff --git a/test/105_accept_clone.bats b/test/105_accept_clone.bats new file mode 100644 index 0000000..767e230 --- /dev/null +++ b/test/105_accept_clone.bats @@ -0,0 +1,175 @@ +load common +load_fixtures + +IN_REPO=(.bash_profile .vimrc) +T_DIR_REMOTE="$T_TMP/remote" +REMOTE_URL="file:///$T_TMP/remote" + +setup() { + destroy_tmp + build_repo "${IN_REPO[@]}" + cp -rp "$T_DIR_REPO" "$T_DIR_REMOTE" +} + +@test "Command 'clone' (bad remote)" { + echo " + When 'clone' command is provided, + and the remote is bad, + Report error + Remove the YADM_REPO + Exit with 1 + " + + #; remove existing worktree and repo + rm -rf "$T_DIR_WORK" + mkdir -p "$T_DIR_WORK" + rm -rf "$T_DIR_REPO" + + #; run clone + run $T_YADM_Y clone -w "$T_DIR_WORK" "file:///bogus-repo" + + #; validate status and output + [ "$status" -eq 1 ] + [[ "$output" =~ Unable\ to\ fetch\ origin ]] + + #; confirm repo directory is removed + [ ! -d "$T_DIR_REPO" ] +} + +@test "Command 'clone'" { + echo " + When 'clone' command is provided, + Create new repo with attributes: + - 0600 permissions + - not bare + - worktree = \$YADM_WORK + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as cloned + A remote named origin exists + Exit with 0 + " + + #; remove existing worktree and repo + rm -rf "$T_DIR_WORK" + mkdir -p "$T_DIR_WORK" + rm -rf "$T_DIR_REPO" + + #; run clone + run $T_YADM_Y clone -w "$T_DIR_WORK" "$REMOTE_URL" + + #; validate status and output + [ "$status" -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$T_DIR_WORK" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true + + #; test the remote + local remote_output=$(GIT_DIR="$T_DIR_REPO" git remote show) + [ "$remote_output" = "origin" ] +} + +@test "Command 'clone' (existing repo)" { + echo " + When 'clone' command is provided, + and a repo already exists, + Report error + Exit with 1 + " + + #; run clone + run $T_YADM_Y clone -w "$T_DIR_WORK" "$REMOTE_URL" + + #; validate status and output + [ "$status" -eq 1 ] + [[ "$output" =~ Git\ repo\ already\ exists ]] +} + +@test "Command 'clone' -f (force overwrite)" { + echo " + When 'clone' command is provided, + and '-f' is provided, + and a repo already exists, + Overwrite the repo with attributes: + - 0600 permissions + - not bare + - worktree = \$YADM_WORK + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as cloned + A remote named origin exists + Exit with 0 + " + + #; remove existing worktree + rm -rf "$T_DIR_WORK" + mkdir -p "$T_DIR_WORK" + + #; run clone + run $T_YADM_Y clone -w "$T_DIR_WORK" -f "$REMOTE_URL" + + #; validate status and output + [ "$status" -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$T_DIR_WORK" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true + + #; test the remote + local remote_output=$(GIT_DIR="$T_DIR_REPO" git remote show) + [ "$remote_output" = "origin" ] +} + +@test "Command 'clone' (existing conflicts)" { + echo " + When 'clone' command is provided, + and '-f' is provided, + and a repo already exists, + Overwrite the repo with attributes: + - 0600 permissions + - not bare + - worktree = \$YADM_WORK + - showUntrackedFiles = no + - yadm.managed = true + Report the repo as cloned + A remote named origin exists + Exit with 0 + " + + #; remove existing repo + rm -rf "$T_DIR_REPO" + + #; cause a conflict + echo "conflict" >> "$T_DIR_WORK/.bash_profile" + + #; run clone + run $T_YADM_Y clone -w "$T_DIR_WORK" "$REMOTE_URL" + + #; validate status and output + [ "$status" -eq 0 ] + [[ "$output" =~ Initialized ]] + + #; validate merging note + [[ "$output" =~ Merging\ origin\/master\ failed ]] + [[ "$output" =~ NOTE ]] + + #; validate repo attributes + test_perms $T_DIR_REPO "drw.--.--." + test_repo_attribute $T_DIR_REPO core.bare false + test_repo_attribute $T_DIR_REPO core.worktree "$T_DIR_WORK" + test_repo_attribute $T_DIR_REPO status.showUntrackedFiles no + test_repo_attribute $T_DIR_REPO yadm.managed true + + #; test the remote + local remote_output=$(GIT_DIR="$T_DIR_REPO" git remote show) + [ "$remote_output" = "origin" ] +} diff --git a/test/106_accept_config.bats b/test/106_accept_config.bats new file mode 100644 index 0000000..dfb1dab --- /dev/null +++ b/test/106_accept_config.bats @@ -0,0 +1,122 @@ +load common +load_fixtures + +T_SECTION="test" +T_ATTRIB="attribute" +T_KEY="$T_SECTION.$T_ATTRIB" +T_VALUE="testvalue" +T_EXPECTED="[$T_SECTION]\n\t$T_ATTRIB = $T_VALUE" + +setup() { + destroy_tmp +} + +@test "Command 'config' (no parameters)" { + skip + echo " + When 'config' command is provided alone, + Produce instructions about supported configuration options + Exit with 1 + " + + #; TODO: This has not been implemented +} + +@test "Command 'config' (read missing)" { + echo " + When 'config' command is provided, + and an attribute is provided + and the attribute isn't configured + Report an empty value + Exit with 0 + " + + #; run config + run $T_YADM_Y config $T_KEY + + #; validate status and output + [ $status -eq 0 ] + [ "$output" = "" ] +} + +@test "Command 'config' (write)" { + echo " + When 'config' command is provided, + and an attribute is provided + and a value is provided + Report no output + Update configuration file + Exit with 0 + " + + #; run config + run $T_YADM_Y config "$T_KEY" "$T_VALUE" + + #; validate status and output + [ $status -eq 0 ] + [ "$output" = "" ] + + #; validate configuration + local config=$(cat $T_YADM_CONFIG) + local expected=$(echo -e "$T_EXPECTED") + if [ "$config" != "$expected" ]; then + echo "ERROR: Config does not match expected" + echo "$config" + return 1 + fi +} + +@test "Command 'config' (read)" { + echo " + When 'config' command is provided, + and an attribute is provided + and the attribute is configured + Report the requested value + Exit with 0 + " + + #; manually load a value into the configuration + mkdir -p $(dirname "$T_YADM_CONFIG") + echo -e "$T_EXPECTED" > $T_YADM_CONFIG + + #; run config + run $T_YADM_Y config "$T_KEY" + + #; validate status and output + [ $status -eq 0 ] + if [ "$output" != "$T_VALUE" ]; then + echo "ERROR: Incorrect value returned. Expected '$T_VALUE', got '$output'" + return 1 + fi +} + +@test "Command 'config' (update)" { + echo " + When 'config' command is provided, + and an attribute is provided + and the attribute is already configured + Report no output + Update configuration file + Exit with 0 + " + + #; manually load a value into the configuration + mkdir -p $(dirname "$T_YADM_CONFIG") + echo -e "${T_EXPECTED}_with_extra_data" > $T_YADM_CONFIG + + #; run config + run $T_YADM_Y config "$T_KEY" "$T_VALUE" + + #; validate status and output + [ $status -eq 0 ] + [ "$output" = "" ] + + #; validate configuration + local config=$(cat $T_YADM_CONFIG) + local expected=$(echo -e "$T_EXPECTED") + if [ "$config" != "$expected" ]; then + echo "ERROR: Config does not match expected" + echo "$config" + return 1 + fi +} diff --git a/test/107_accept_list.bats b/test/107_accept_list.bats new file mode 100644 index 0000000..ef076b7 --- /dev/null +++ b/test/107_accept_list.bats @@ -0,0 +1,92 @@ +load common +load_fixtures + +IN_REPO=(.bash_profile .hammerspoon/init.lua .vimrc) +SUBDIR=".hammerspoon" +IN_SUBDIR=(init.lua) + +function setup() { + destroy_tmp + build_repo "${IN_REPO[@]}" +} + +@test "Command 'list' -a" { + echo " + When 'list' command is provided, + and '-a' is provided, + List tracked files + Exit with 0 + " + + #; run list -a + run $T_YADM_Y list -a + + #; validate status and output + [ "$status" -eq 0 ] + local line=0 + for f in "${IN_REPO[@]}"; do + [ "${lines[$line]}" = "$f" ] + ((line++)) || true + done +} + +@test "Command 'list' (outside of worktree)" { + echo " + When 'list' command is provided, + and while outside of the worktree + List tracked files + Exit with 0 + " + + #; run list + run $T_YADM_Y list + + #; validate status and output + [ "$status" -eq 0 ] + local line=0 + for f in "${IN_REPO[@]}"; do + [ "${lines[$line]}" = "$f" ] + ((line++)) || true + done +} + +@test "Command 'list' (in root of worktree)" { + echo " + When 'list' command is provided, + and while in root of the worktree + List tracked files + Exit with 0 + " + + #; run list + run bash -c "(cd $T_DIR_WORK; $T_YADM_Y list)" + + #; validate status and output + [ "$status" -eq 0 ] + local line=0 + for f in "${IN_REPO[@]}"; do + [ "${lines[$line]}" = "$f" ] + ((line++)) || true + done +} + +@test "Command 'list' (in subdirectory of worktree)" { + echo " + When 'list' command is provided, + and while in subdirectory of the worktree + List tracked files for current directory + Exit with 0 + " + + #; run list + run bash -c "(cd $T_DIR_WORK/$SUBDIR; $T_YADM_Y list)" + + #; validate status and output + [ "$status" -eq 0 ] + local line=0 + for f in "${IN_SUBDIR[@]}"; do + echo "'${lines[$line]}' = '$f'" + [ "${lines[$line]}" = "$f" ] + ((line++)) || true + done +} diff --git a/test/108_accept_alt.bats b/test/108_accept_alt.bats new file mode 100644 index 0000000..c2e1b10 --- /dev/null +++ b/test/108_accept_alt.bats @@ -0,0 +1,160 @@ +load common +load_fixtures + +IN_REPO=(alt*) + +setup() { + destroy_tmp + build_repo "${IN_REPO[@]}" +} + +function test_alt() { + local alt_type="$1" + local auto_alt="$2" + + #; detemine test parameters + case $alt_type in + base) + link_name="alt-base" + link_match="$link_name##" + ;; + system) + link_name="alt-system" + link_match="$link_name##$T_SYS" + ;; + host) + link_name="alt-host" + link_match="$link_name##$T_SYS.$T_HOST" + ;; + user) + link_name="alt-user" + link_match="$link_name##$T_SYS.$T_HOST.$T_USER" + ;; + esac + + #; verify link doesn't already exist + if [ -L "$T_DIR_WORK/$link_name" ]; then + echo "ERROR: Link already exists before running yadm" + return 1 + fi + + #; configure yadm.auto_alt=false + if [ "$auto_alt" = "false" ]; then + git config --file="$T_YADM_CONFIG" yadm.auto-alt false + fi + + #; run yadm (alt or status) + if [ -z "$auto_alt" ]; then + run $T_YADM_Y alt + #; validate status and output + if [ "$status" != 0 ] || [[ ! "$output" =~ Linking.+$link_name ]]; then + echo "ERROR: Could not confirm status and output of alt command" + return 1; + fi + else + #; running any passed through Git command should trigger auto-alt + run $T_YADM_Y status + if [ ! -z "$auto_alt" ] && [[ "$output" =~ Linking.+$link_name ]]; then + echo "ERROR: Reporting of link should not happen" + return 1 + fi + fi + + #; validate link content + if [ "$alt_type" = "none" ] || [ "$auto_alt" = "false" ]; then + #; no link should be present + if [ -L "$T_DIR_WORK/$link_name" ]; then + echo "ERROR: Link should not exist" + return 1 + fi + else + #; correct link should be present + local link_content=$(cat "$T_DIR_WORK/$link_name") + if [ "$link_content" != "$link_match" ]; then + echo "ERROR: Link content is not correct" + return 1 + fi + fi +} + +@test "Command 'alt' (select base)" { + echo " + When the command 'alt' is provided + and file matches only ## + Report the linking + Verify correct file is linked + Exit with 0 + " + + test_alt 'base' "" +} + +@test "Command 'alt' (select system)" { + echo " + When the command 'alt' is provided + and file matches only ##SYSTEM + Report the linking + Verify correct file is linked + Exit with 0 + " + + test_alt 'system' "" +} + +@test "Command 'alt' (select host)" { + echo " + When the command 'alt' is provided + and file matches only ##SYSTEM.HOST + Report the linking + Verify correct file is linked + Exit with 0 + " + + test_alt 'host' "" +} + +@test "Command 'alt' (select user)" { + echo " + When the command 'alt' is provided + and file matches only ##SYSTEM.HOST.USER + Report the linking + Verify correct file is linked + Exit with 0 + " + + test_alt 'user' "" +} + +@test "Command 'alt' (select none)" { + echo " + When the command 'alt' is provided + and no file matches + Verify there is no link + Exit with 0 + " + + test_alt 'none' "" +} + +@test "Command 'auto-alt' (enabled)" { + echo " + When a command possibly changes the repo + and auto-alt is configured true + automatically process alternates + report no linking (not loud) + verify alternate created + " + + test_alt 'base' "true" +} + +@test "Command 'auto-alt' (disabled)" { + echo " + When a command possibly changes the repo + and auto-alt is configured false + do no linking + verify no links + " + + test_alt 'base' "false" +} diff --git a/test/109_accept_encryption.bats b/test/109_accept_encryption.bats new file mode 100644 index 0000000..ca125a9 --- /dev/null +++ b/test/109_accept_encryption.bats @@ -0,0 +1,393 @@ +load common +load_fixtures + +T_PASSWD="ExamplePassword" + +setup() { + #; start fresh + destroy_tmp + + #; create a worktree & repo + build_repo + + #; define a YADM_ENCRYPT + mkdir -p $(dirname "$T_YADM_ENCRYPT") + echo -e ".ssh/*.key\n.gnupg/*.gpg" > $T_YADM_ENCRYPT + + #; create a YADM_ARCHIVE + ( + cd $T_DIR_WORK + for f in $(sort "$T_YADM_ENCRYPT"); do + tar rf "$T_TMP/build_archive.tar" "$f" + echo "$f" >> "$T_TMP/archived_files" + done + ) + + #; encrypt YADM_ARCHIVE + expect </dev/null + set timeout 2; + spawn gpg --yes -c --output "$T_YADM_ARCHIVE" "$T_TMP/build_archive.tar" + expect "passphrase:" {send "$T_PASSWD\n"} + expect "passphrase:" {send "$T_PASSWD\n"} + expect "$" + foreach {pid spawnid os_error_flag value} [wait] break +EOF +} + +function validate_archive() { + #; inventory what's in the archive + expect </dev/null + set timeout 2; + spawn bash -c "(gpg -q -d '$T_YADM_ARCHIVE' || echo 1) | tar t | sort > $T_TMP/archive_list" + expect "passphrase:" {send "$T_PASSWD\n"} + expect "$" + foreach {pid spawnid os_error_flag value} [wait] break +EOF + + #; inventory what is expected in the archive + ( + cd $T_DIR_WORK + for f in $(cat "$T_YADM_ENCRYPT"); do + echo "$f" + done | sort > "$T_TMP/expected_list" + ) + + #; compare the archive vs expected + if ! cmp -s "$T_TMP/archive_list" "$T_TMP/expected_list"; then + echo "ERROR: Archive does not contain the correct files" + echo "Contains:" + cat "$T_TMP/archive_list" + return 1 + fi + return 0 +} + +function validate_extraction() { + #; test each file which was archived + for f in $(cat "$T_TMP/archived_files"); do + local contents=$(cat "$T_DIR_WORK/$f") + if [ "$contents" != "$f" ]; then + echo "ERROR: Contents of $T_DIR_WORK/$f is incorrect" + return 1 + fi + done + return 0 +} + +@test "Command 'encrypt' (missing YADM_ENCRYPT)" { + echo " + When 'encrypt' command is provided, + and YADM_ENCRYPT does not exist + Report problem + Exit with 1 + " + + #; remove YADM_ENCRYPT + rm -f "$T_YADM_ENCRYPT" + + #; run encrypt + run $T_YADM_Y encrypt + + #; validate status and output + [ "$status" -eq 1 ] + [[ "$output" =~ does\ not\ exist ]] +} + +@test "Command 'encrypt' (mismatched password)" { + echo " + When 'encrypt' command is provided, + and YADM_ENCRYPT is present + and the provided passwords do not match + Report problem + Exit with 1 + " + + #; remove existing T_YADM_ARCHIVE + rm -f "$T_YADM_ARCHIVE" + + #; run encrypt + run expect <> $T_YADM_ENCRYPT + + #; run encrypt + run expect < "$T_YADM_ENCRYPT" + + #; validate the archive + validate_archive +} + +@test "Command 'encrypt' (overwrite)" { + echo " + When 'encrypt' command is provided, + and YADM_ENCRYPT is present + and YADM_ARCHIVE already exists + Overwrite YADM_ARCHIVE + Report the archive created + Archive should be valid + Exit with 0 + " + + #; Explictly create an invalid archive + echo "EXISTING ARCHIVE" > "$T_YADM_ARCHIVE" + + #; run encrypt + run expect <> "$T_DIR_WORK/$f" + done + + #; run encrypt + run expect < "$T_YADM_ENCRYPT" + + #; run perms + run $T_YADM_Y perms + + #; validate status and output + [ "$status" -eq 0 ] + [ "$output" = "" ] + + #; this version has no comments in it + echo -e ".hammerspoon/*" > "$T_YADM_ENCRYPT" + + #; validate permissions + validate_perms ssh gpg encrypt +} + +@test "Command 'perms' (ssh-perms=false)" { + echo " + When the command 'perms' is provided + And yadm.ssh-perms=false + Update permissions for gpg only + Verify correct permissions + Exit with 0 + " + + #; configure yadm.ssh-perms + git config --file="$T_YADM_CONFIG" "yadm.ssh-perms" "false" + + #; run perms + run $T_YADM_Y perms + + #; validate status and output + [ "$status" -eq 0 ] + [ "$output" = "" ] + + #; validate permissions + validate_perms gpg +} + +@test "Command 'perms' (gpg-perms=false)" { + echo " + When the command 'perms' is provided + And yadm.gpg-perms=false + Update permissions for ssh only + Verify correct permissions + Exit with 0 + " + + #; configure yadm.gpg-perms + git config --file="$T_YADM_CONFIG" "yadm.gpg-perms" "false" + + #; run perms + run $T_YADM_Y perms + + #; validate status and output + [ "$status" -eq 0 ] + [ "$output" = "" ] + + #; validate permissions + validate_perms ssh +} + +@test "Command 'auto-perms' (enabled)" { + echo " + When a command possibly changes the repo + Update permissions for ssh/gpg + Verify correct permissions + " + + #; run status + run $T_YADM_Y status + + #; validate status + [ "$status" -eq 0 ] + + #; validate permissions + validate_perms ssh gpg +} + +@test "Command 'auto-perms' (disabled)" { + echo " + When a command possibly changes the repo + And yadm.auto-perms=false + Take no action + Verify permissions are intact + " + + #; configure yadm.auto-perms + git config --file="$T_YADM_CONFIG" "yadm.auto-perms" "false" + + #; run status + run $T_YADM_Y status + + #; validate status + [ "$status" -eq 0 ] + + #; validate permissions + validate_perms +} diff --git a/test/common.bash b/test/common.bash new file mode 100644 index 0000000..c5fb41e --- /dev/null +++ b/test/common.bash @@ -0,0 +1,151 @@ + +#; common fixtures +function load_fixtures() { + DEFAULT_YADM_DIR="$HOME/.yadm" + DEFAULT_REPO="repo.git" + DEFAULT_CONFIG="config" + DEFAULT_ENCRYPT="encrypt" + DEFAULT_ARCHIVE="files.gpg" + + T_YADM="$PWD/yadm" + T_TMP="$BATS_TMPDIR/ytmp" + T_DIR_YADM="$T_TMP/.yadm" + T_DIR_WORK="$T_TMP/yadm-work" + T_DIR_REPO="$T_DIR_YADM/repo.git" + T_YADM_CONFIG="$T_DIR_YADM/config" + T_YADM_ENCRYPT="$T_DIR_YADM/encrypt" + T_YADM_ARCHIVE="$T_DIR_YADM/files.gpg" + T_YADM_Y="$T_YADM -Y $T_DIR_YADM" + + T_SYS=$(uname -s) + T_HOST=$(hostname -s) + T_USER=$(id -u -n) +} + +function configure_git() { + (git config user.name || git config --global user.name 'test') >/dev/null + (git config user.email || git config --global user.email 'test@test.test') > /dev/null +} + +function test_perms() { + local test_path="$1" + local regex="$2" + local ls=$(ls -ld "$test_path") + local perms="${ls:0:10}" + if [[ ! $perms =~ $regex ]]; then + echo "ERROR: Found permissions $perms for $test_path" + return 1 + fi + return 0 +} + +function test_repo_attribute() { + local repo_dir="$1" + local attribute="$2" + local expected="$3" + local actual=$(GIT_DIR="$repo_dir" git config --local "$attribute") + if [ "$actual" != "$expected" ]; then + echo "ERROR: repo attribute $attribute set to $actual" + return 1 + fi + return 0 +} + +#; create worktree at path +function create_worktree() { + local DIR_WORKTREE="$1" + if [ -z "$DIR_WORKTREE" ]; then + echo "ERROR: create_worktree() called without a path" + return 1 + fi + + if [[ ! "$DIR_WORKTREE" =~ ^$T_TMP ]]; then + echo "ERROR: create_worktree() called with a path outside of $T_TMP" + return 1 + fi + + #; remove any existing data + rm -rf "$DIR_WORKTREE" + + #; create some standard files + for f in \ + "alt-none##S" \ + "alt-none##S.H" \ + "alt-none##S.H.U" \ + "alt-base##" \ + "alt-base##S" \ + "alt-base##S.H" \ + "alt-base##S.H.U" \ + "alt-system##" \ + "alt-system##S" \ + "alt-system##S.H" \ + "alt-system##S.H.U" \ + "alt-system##$T_SYS" \ + "alt-host##" \ + "alt-host##S" \ + "alt-host##S.H" \ + "alt-host##S.H.U" \ + "alt-host##$T_SYS.$T_HOST" \ + "alt-user##" \ + "alt-user##S" \ + "alt-user##S.H" \ + "alt-user##S.H.U" \ + "alt-user##$T_SYS.$T_HOST.$T_USER" \ + .bash_profile \ + .gnupg/gpg.conf \ + .gnupg/pubring.gpg \ + .gnupg/secring.gpg \ + .hammerspoon/init.lua \ + .ssh/config \ + .ssh/secret.key \ + .ssh/secret.pub \ + .tmux.conf \ + .vimrc \ + ; + do + mkdir -p $(dirname "$DIR_WORKTREE/$f") + echo "$f" > "$DIR_WORKTREE/$f" + done + + #; change all perms (so permission updates can be observed) + find "$DIR_WORKTREE" -exec chmod 0777 '{}' ';' + +} + +#; create a repo in T_DIR_REPO +function build_repo() { + local files_to_add="$@" + + #; create a worktree + create_worktree "$T_DIR_WORK" + + #; remove the repo if it exists + if [ -e "$T_DIR_REPO" ]; then + rm -rf "$T_DIR_REPO" + fi + + #; create the repo + git init --shared=0600 --bare "$T_DIR_REPO" >/dev/null 2>&1 + + #; standard repo config + GIT_DIR="$T_DIR_REPO" git config core.bare 'false' + GIT_DIR="$T_DIR_REPO" git config core.worktree "$T_DIR_WORK" + GIT_DIR="$T_DIR_REPO" git config status.showUntrackedFiles no + GIT_DIR="$T_DIR_REPO" git config yadm.managed 'true' + + if [ -n "$files_to_add" ]; then + for f in $files_to_add; do + GIT_DIR="$T_DIR_REPO" git add "$T_DIR_WORK/$f" >/dev/null + done + GIT_DIR="$T_DIR_REPO" git commit -m 'Create repo template' >/dev/null + fi + +} + +#; remove all tmp files +function destroy_tmp() { + load_fixtures + rm -rf "$T_TMP" +} + +configure_git