diff --git a/tests/shell/001_init.sh b/tests/shell/001_init.sh index 6e3c2754..e2550f85 100644 --- a/tests/shell/001_init.sh +++ b/tests/shell/001_init.sh @@ -1,10 +1,7 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init -ls cfbs.json +assert_file_exists cfbs.json + +test_finish diff --git a/tests/shell/002_add.sh b/tests/shell/002_add.sh index 2bc2b633..56873076 100644 --- a/tests/shell/002_add.sh +++ b/tests/shell/002_add.sh @@ -1,11 +1,8 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs --non-interactive add autorun -grep masterfiles cfbs.json +assert_file_contains cfbs.json masterfiles + +test_finish diff --git a/tests/shell/003_download.sh b/tests/shell/003_download.sh index 84d01ca4..d5cca994 100644 --- a/tests/shell/003_download.sh +++ b/tests/shell/003_download.sh @@ -1,12 +1,9 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs download -ls ~/.cfengine/cfbs/downloads/* +assert_file_exists ~/.cfengine/cfbs/downloads/* + +test_finish diff --git a/tests/shell/004_build.sh b/tests/shell/004_build.sh index 3ca0a277..92856b5d 100644 --- a/tests/shell/004_build.sh +++ b/tests/shell/004_build.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs --non-interactive add autorun @@ -13,4 +8,6 @@ cfbs --non-interactive add git cfbs --non-interactive add ansible cfbs build -ls out/ +assert_file_exists out/ + +test_finish diff --git a/tests/shell/005_alias.sh b/tests/shell/005_alias.sh index 2ef90fc8..009bf092 100644 --- a/tests/shell/005_alias.sh +++ b/tests/shell/005_alias.sh @@ -1,13 +1,10 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init -cfbs --non-interactive add groups > output.log +run cfbs --non-interactive add groups -grep "alias" output.log -grep "Added module" output.log +assert_output_contains "alias" +assert_output_contains "Added module" + +test_finish diff --git a/tests/shell/006_search.sh b/tests/shell/006_search.sh index d338b2b0..8b7831f0 100644 --- a/tests/shell/006_search.sh +++ b/tests/shell/006_search.sh @@ -1,17 +1,16 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init -cfbs search > all.log -cfbs search mpf > mpf.log -cfbs search masterfiles > masterfiles.log -grep "python" all.log -! grep "python" mpf.log -grep "masterfiles" mpf.log -grep "masterfiles" masterfiles.log +run cfbs search +assert_output_contains "python" + +run cfbs search mpf +assert_output_not_contains "python" +assert_output_contains "masterfiles" + +run cfbs search masterfiles +assert_output_contains "masterfiles" + +test_finish diff --git a/tests/shell/007_install.sh b/tests/shell/007_install.sh index 7591fd49..c2a9104e 100644 --- a/tests/shell/007_install.sh +++ b/tests/shell/007_install.sh @@ -1,23 +1,13 @@ -if [ x"$UNSAFE_TESTS" = x1 ] -then - echo "Unsafe tests are enabled - running install test" -else - echo "Warning: Unsafe tests are disabled - skipping install test" - exit 0 -fi - -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +skip_unless_unsafe +test_init rm -rf ~/.cfagent/inputs/ cfbs --non-interactive init cfbs --non-interactive add autorun cfbs install -ls ~/.cfagent/inputs/def.json +assert_file_exists ~/.cfagent/inputs/def.json rm -rf ~/.cfagent/inputs/ + +test_finish diff --git a/tests/shell/008_remove.sh b/tests/shell/008_remove.sh index dec19c88..564d6753 100644 --- a/tests/shell/008_remove.sh +++ b/tests/shell/008_remove.sh @@ -1,12 +1,9 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init -grep '"name": "masterfiles"' cfbs.json +assert_file_contains cfbs.json '"name": "masterfiles"' cfbs --non-interactive remove masterfiles --non-interactive -! grep '"name": "masterfiles"' cfbs.json +assert_file_not_contains cfbs.json '"name": "masterfiles"' + +test_finish diff --git a/tests/shell/009_clean.sh b/tests/shell/009_clean.sh index d971c298..99f9f51c 100644 --- a/tests/shell/009_clean.sh +++ b/tests/shell/009_clean.sh @@ -1,20 +1,17 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs --non-interactive add promise-type-git -grep '"name": "library-for-promise-types-in-python"' cfbs.json -grep '"name": "promise-type-git"' cfbs.json +assert_file_contains cfbs.json '"name": "library-for-promise-types-in-python"' +assert_file_contains cfbs.json '"name": "promise-type-git"' cfbs --non-interactive remove promise-type-git --non-interactive -! grep '"name": "library-for-promise-types-in-python"' cfbs.json -! grep '"name": "promise-type-git"' cfbs.json +assert_file_not_contains cfbs.json '"name": "library-for-promise-types-in-python"' +assert_file_not_contains cfbs.json '"name": "promise-type-git"' # Check that clean does nothing: cat cfbs.json > before.json cfbs --non-interactive clean -diff cfbs.json before.json +assert_diff cfbs.json before.json + +test_finish diff --git a/tests/shell/010_local_add.sh b/tests/shell/010_local_add.sh index 15f54913..e95fa50a 100644 --- a/tests/shell/010_local_add.sh +++ b/tests/shell/010_local_add.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs status @@ -18,18 +13,20 @@ echo 'bundle agent bogus_bundle { cfbs --non-interactive add ./bogus_file.cf -grep '"name": "./bogus_file.cf"' cfbs.json -grep '"copy ./bogus_file.cf services/cfbs/bogus_file.cf"' cfbs.json -grep '"policy_files services/cfbs/bogus_file.cf"' cfbs.json -grep '"bundles bogus_bundle"' cfbs.json +assert_file_contains cfbs.json '"name": "./bogus_file.cf"' +assert_file_contains cfbs.json '"copy ./bogus_file.cf services/cfbs/bogus_file.cf"' +assert_file_contains cfbs.json '"policy_files services/cfbs/bogus_file.cf"' +assert_file_contains cfbs.json '"bundles bogus_bundle"' cfbs status cfbs build -grep '"inputs"' out/masterfiles/def.json -grep 'bogus_file.cf' out/masterfiles/def.json +assert_file_contains out/masterfiles/def.json '"inputs"' +assert_file_contains out/masterfiles/def.json 'bogus_file.cf' -grep '"control_common_bundlesequence_end"' out/masterfiles/def.json -grep '"bogus_bundle"' out/masterfiles/def.json +assert_file_contains out/masterfiles/def.json '"control_common_bundlesequence_end"' +assert_file_contains out/masterfiles/def.json '"bogus_bundle"' -ls out/masterfiles/services/cfbs/bogus_file.cf +assert_file_exists out/masterfiles/services/cfbs/bogus_file.cf + +test_finish diff --git a/tests/shell/011_update.sh b/tests/shell/011_update.sh index 6bab18cd..e5a7d29a 100644 --- a/tests/shell/011_update.sh +++ b/tests/shell/011_update.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs --non-interactive add promise-type-ansible@0.1.1 @@ -16,37 +11,37 @@ cfbs --non-interactive update # Also, it should not break on new commits(!) # TODO: Use jq, a python script, cfbs validate or something similar -cat cfbs.json | grep -F "name" | grep -F "Example" -cat cfbs.json | grep -F "type" | grep -F "policy-set" -cat cfbs.json | grep -F "description" | grep -F "Example description" - -cat cfbs.json | grep -F "name" | grep -F "promise-type-ansible" -cat cfbs.json | grep -F "version" | grep -F "." -cat cfbs.json | grep -F "commit" -cat cfbs.json | grep -F "added_by" | grep -F "cfbs add" -cat cfbs.json | grep -F "steps" -cat cfbs.json | grep -F "copy ansible_promise.py modules/promises/" -cat cfbs.json | grep -F "append enable.cf services/init.cf" -cat cfbs.json | grep -F "tags" | grep -F "supported" | grep -F "promise-type" -cat cfbs.json | grep -F "by" | grep -F "https://github.com/tranchitella" -cat cfbs.json | grep -F "repo" | grep -F "https://github.com/cfengine/modules" -cat cfbs.json | grep -F "subdirectory" | grep -F "promise-types/ansible" -cat cfbs.json | grep -F "dependencies" | grep -F "library-for-promise-types-in-python" -cat cfbs.json | grep -F "description" | grep -F "Promise type to run ansible playbooks" - -cat cfbs.json | grep -F "name" | grep -F "library-for-promise-types-in-python" -cat cfbs.json | grep -F "description" | grep -F "Library enabling promise types implemented in python" -cat cfbs.json | grep -F "tags" | grep -F "supported" | grep -F "library" -cat cfbs.json | grep -F "repo" | grep -F "https://github.com/cfengine/modules" -cat cfbs.json | grep -F "by" | grep -F "https://github.com/cfengine" -cat cfbs.json | grep -F "version" | grep -F "." -cat cfbs.json | grep -F "commit" -cat cfbs.json | grep -F "subdirectory" | grep -F "libraries/python" -cat cfbs.json | grep -F "added_by" | grep -F "promise-type-ansible" -cat cfbs.json | grep -F "copy cfengine_module_library.py modules/promises/cfengine_module_library.py" +assert_file_matches_regex cfbs.json '"name".*"Example' +assert_file_matches_regex cfbs.json '"type".*"policy-set"' +assert_file_matches_regex cfbs.json '"description".*"Example description"' + +assert_file_matches_regex cfbs.json '"name".*"promise-type-ansible"' +assert_file_matches_regex cfbs.json '"version":.*[0-9]+\.[0-9]+' +assert_file_contains cfbs.json '"commit"' +assert_file_matches_regex cfbs.json '"added_by".*"cfbs add"' +assert_file_contains cfbs.json '"steps"' +assert_file_contains cfbs.json 'copy ansible_promise.py modules/promises/' +assert_file_contains cfbs.json 'append enable.cf services/init.cf' +assert_file_matches_regex cfbs.json '"tags".*"supported"' +assert_file_matches_regex cfbs.json '"by".*"https://github.com/tranchitella"' +assert_file_matches_regex cfbs.json '"repo".*"https://github.com/cfengine/modules"' +assert_file_matches_regex cfbs.json '"subdirectory".*"promise-types/ansible"' +assert_file_matches_regex cfbs.json '"dependencies".*"library-for-promise-types-in-python"' +assert_file_matches_regex cfbs.json '"description".*"Promise type to run ansible playbooks' + +assert_file_matches_regex cfbs.json '"name".*"library-for-promise-types-in-python"' +assert_file_matches_regex cfbs.json '"description".*"Library enabling promise types implemented in python' +assert_file_matches_regex cfbs.json '"tags".*"library"' +assert_file_matches_regex cfbs.json '"repo".*"https://github.com/cfengine/modules"' +assert_file_matches_regex cfbs.json '"by".*"https://github.com/cfengine"' +assert_file_matches_regex cfbs.json '"subdirectory".*"libraries/python"' +assert_file_matches_regex cfbs.json '"added_by".*"promise-type-ansible"' +assert_file_contains cfbs.json 'copy cfengine_module_library.py modules/promises/cfengine_module_library.py' cfbs status cfbs build -ls out/masterfiles/promises.cf -ls out/masterfiles/modules/promises/ansible_promise.py +assert_file_exists out/masterfiles/promises.cf +assert_file_exists out/masterfiles/modules/promises/ansible_promise.py + +test_finish diff --git a/tests/shell/012_prepend_dot_slash.sh b/tests/shell/012_prepend_dot_slash.sh index 2db7617a..0a4b57d9 100644 --- a/tests/shell/012_prepend_dot_slash.sh +++ b/tests/shell/012_prepend_dot_slash.sh @@ -1,8 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ +source "$(dirname "$0")/testlib.sh" +test_init echo ' bundle agent test_bundle @@ -38,3 +35,5 @@ rm -rf .git cfbs --non-interactive init cfbs --non-interactive add test_policy.cf cfbs --non-interactive remove ./test_policy.cf --non-interactive + +test_finish diff --git a/tests/shell/013_add_url_commit.sh b/tests/shell/013_add_url_commit.sh index 00772d0d..094161bc 100644 --- a/tests/shell/013_add_url_commit.sh +++ b/tests/shell/013_add_url_commit.sh @@ -1,21 +1,16 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs --non-interactive add https://github.com/basvandervlies/cf_surfsara_lib@09e07dda690f5806d5f17be8e71d9fdcc51bbdf1 -grep https://github.com/basvandervlies/cf_surfsara_lib cfbs.json -grep 09e07dda690f5806d5f17be8e71d9fdcc51bbdf1 cfbs.json -grep scl cfbs.json +assert_file_contains cfbs.json https://github.com/basvandervlies/cf_surfsara_lib +assert_file_contains cfbs.json 09e07dda690f5806d5f17be8e71d9fdcc51bbdf1 +assert_file_contains cfbs.json scl cfbs build -ls out/masterfiles/lib/scl/ -grep scl_dmidecode_example out/masterfiles/scl_example.json +assert_file_exists out/masterfiles/lib/scl/ +assert_file_contains out/masterfiles/scl_example.json scl_dmidecode_example # Build should be possible to do on another machine # so let's test that it works after deleting the things which @@ -27,8 +22,8 @@ rm -rf out/ rm -rf ~/.cfengine/cfbs cfbs build -ls out/masterfiles/lib/scl/ -grep scl_dmidecode_example out/masterfiles/scl_example.json +assert_file_exists out/masterfiles/lib/scl/ +assert_file_contains out/masterfiles/scl_example.json scl_dmidecode_example # Finally, let's also test that we can build it again (now with the cached # files) @@ -36,5 +31,7 @@ grep scl_dmidecode_example out/masterfiles/scl_example.json rm -rf out/ cfbs build -ls out/masterfiles/lib/scl/ -grep scl_dmidecode_example out/masterfiles/scl_example.json +assert_file_exists out/masterfiles/lib/scl/ +assert_file_contains out/masterfiles/scl_example.json scl_dmidecode_example + +test_finish diff --git a/tests/shell/014_add_nonexistent.sh b/tests/shell/014_add_nonexistent.sh index 7282fa3f..38a0b222 100644 --- a/tests/shell/014_add_nonexistent.sh +++ b/tests/shell/014_add_nonexistent.sh @@ -1,11 +1,8 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init -! ( cfbs --non-interactive add bollocks > output.log 2>&1 ) -grep -F "Error: Module 'bollocks' does not exist" output.log +run_expect_failure cfbs --non-interactive add bollocks +assert_output_contains "Error: Module 'bollocks' does not exist" + +test_finish diff --git a/tests/shell/015_add_version.sh b/tests/shell/015_add_version.sh index 510a1946..4d612ed2 100644 --- a/tests/shell/015_add_version.sh +++ b/tests/shell/015_add_version.sh @@ -1,13 +1,10 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init cfbs --non-interactive add groups@0.1.2 --non-interactive -grep '"version": "0.1.2"' cfbs.json -grep '"commit": "087a2fd81e1bbaf241dfa7bf39013efd9d8d348f"' cfbs.json +assert_file_contains cfbs.json '"version": "0.1.2"' +assert_file_contains cfbs.json '"commit": "087a2fd81e1bbaf241dfa7bf39013efd9d8d348f"' cfbs --non-interactive remove promise-type-groups --non-interactive + +test_finish diff --git a/tests/shell/016_add_folders.sh b/tests/shell/016_add_folders.sh index dd85e5f4..9ea69f18 100644 --- a/tests/shell/016_add_folders.sh +++ b/tests/shell/016_add_folders.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init mkdir -p doofus echo 'bundle agent doofus { @@ -38,21 +33,23 @@ cfbs --non-interactive add ./doofus/ cfbs status cfbs status | grep "./doofus/" -grep '"name": "./doofus/"' cfbs.json -grep '"directory ./ services/cfbs/doofus/"' cfbs.json -grep '"policy_files services/cfbs/doofus/"' cfbs.json -grep '"bundles doofus"' cfbs.json +assert_file_contains cfbs.json '"name": "./doofus/"' +assert_file_contains cfbs.json '"directory ./ services/cfbs/doofus/"' +assert_file_contains cfbs.json '"policy_files services/cfbs/doofus/"' +assert_file_contains cfbs.json '"bundles doofus"' cfbs build -grep '"inputs"' out/masterfiles/def.json -grep '"services/cfbs/doofus/doofus.cf"' out/masterfiles/def.json -grep '"services/cfbs/doofus/foo/foo.cf"' out/masterfiles/def.json +assert_file_contains out/masterfiles/def.json '"inputs"' +assert_file_contains out/masterfiles/def.json '"services/cfbs/doofus/doofus.cf"' +assert_file_contains out/masterfiles/def.json '"services/cfbs/doofus/foo/foo.cf"' -grep '"control_common_bundlesequence_end"' out/masterfiles/def.json -grep '"doofus"' out/masterfiles/def.json +assert_file_contains out/masterfiles/def.json '"control_common_bundlesequence_end"' +assert_file_contains out/masterfiles/def.json '"doofus"' -grep '"foo_thing": "awesome"' out/masterfiles/def.json +assert_file_contains out/masterfiles/def.json '"foo_thing": "awesome"' -ls out/masterfiles/services/cfbs/doofus/doofus.cf -ls out/masterfiles/services/cfbs/doofus/foo/foo.cf +assert_file_exists out/masterfiles/services/cfbs/doofus/doofus.cf +assert_file_exists out/masterfiles/services/cfbs/doofus/foo/foo.cf + +test_finish diff --git a/tests/shell/017_info.sh b/tests/shell/017_info.sh index 5c7427f4..579c005e 100644 --- a/tests/shell/017_info.sh +++ b/tests/shell/017_info.sh @@ -1,19 +1,16 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init -cfbs info masterfiles -cfbs info masterfiles | grep "MPF" +run cfbs info masterfiles +assert_output_contains "MPF" cfbs --non-interactive init -cfbs info autorun -cfbs info autorun | grep "Not added" +run cfbs info autorun +assert_output_contains "Not added" cfbs --non-interactive add autorun -cfbs info autorun -cfbs info autorun | grep "Added" +run cfbs info autorun +assert_output_contains "Added" + +test_finish diff --git a/tests/shell/018_update_input_one_variable.sh b/tests/shell/018_update_input_one_variable.sh index d33f49af..e92cfa54 100644 --- a/tests/shell/018_update_input_one_variable.sh +++ b/tests/shell/018_update_input_one_variable.sh @@ -1,17 +1,14 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/018_update_input_one_variable/example-module . cp ../shell/018_update_input_one_variable/example-cfbs.json cfbs.json cfbs validate cfbs --loglevel=debug --non-interactive update -grep '"label": "Filepath"' example-module/input.json -grep '"question": "Path of file?"' example-module/input.json -grep '"response": "/tmp/create-single-file.txt"' example-module/input.json -grep '"default": "/tmp/test.txt"' example-module/input.json +assert_file_contains example-module/input.json '"label": "Filepath"' +assert_file_contains example-module/input.json '"question": "Path of file?"' +assert_file_contains example-module/input.json '"response": "/tmp/create-single-file.txt"' +assert_file_contains example-module/input.json '"default": "/tmp/test.txt"' + +test_finish diff --git a/tests/shell/019_update_input_two_variables.sh b/tests/shell/019_update_input_two_variables.sh index dd04a11b..ebc03c88 100644 --- a/tests/shell/019_update_input_two_variables.sh +++ b/tests/shell/019_update_input_two_variables.sh @@ -1,20 +1,17 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/019_update_input_two_variables/example-module . cp ../shell/019_update_input_two_variables/example-cfbs.json cfbs.json cfbs validate cfbs --loglevel=debug --non-interactive update -grep '"label": "Path"' example-module/input.json -grep '"question": "Path of file?"' example-module/input.json -grep '"default": "/tmp/test.txt"' example-module/input.json -grep '"label": "Contents"' example-module/input.json -grep '"question": "File contents?"' example-module/input.json -grep '"default": "Hello CFEngine!"' example-module/input.json -grep '"response": "/tmp/create-single-file-with-content.txt"' example-module/input.json +assert_file_contains example-module/input.json '"label": "Path"' +assert_file_contains example-module/input.json '"question": "Path of file?"' +assert_file_contains example-module/input.json '"default": "/tmp/test.txt"' +assert_file_contains example-module/input.json '"label": "Contents"' +assert_file_contains example-module/input.json '"question": "File contents?"' +assert_file_contains example-module/input.json '"default": "Hello CFEngine!"' +assert_file_contains example-module/input.json '"response": "/tmp/create-single-file-with-content.txt"' + +test_finish diff --git a/tests/shell/020_update_input_list.sh b/tests/shell/020_update_input_list.sh index 32b89c79..7070b199 100644 --- a/tests/shell/020_update_input_list.sh +++ b/tests/shell/020_update_input_list.sh @@ -1,21 +1,18 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/020_update_input_list/example-module . cp ../shell/020_update_input_list/example-cfbs.json cfbs.json cfbs validate cfbs --loglevel=debug --non-interactive update -grep '"label": "Filepaths"' example-module/input.json -grep '"while": "Create another file?"' example-module/input.json -grep '"label": "Path"' example-module/input.json -grep '"question": "Path of file?"' example-module/input.json -grep '"default": "/tmp/test.txt"' example-module/input.json -grep '"response":' example-module/input.json -grep '"/tmp/create-multiple-files-1.txt"' example-module/input.json -grep '"/tmp/create-multiple-files-2.txt"' example-module/input.json +assert_file_contains example-module/input.json '"label": "Filepaths"' +assert_file_contains example-module/input.json '"while": "Create another file?"' +assert_file_contains example-module/input.json '"label": "Path"' +assert_file_contains example-module/input.json '"question": "Path of file?"' +assert_file_contains example-module/input.json '"default": "/tmp/test.txt"' +assert_file_contains example-module/input.json '"response":' +assert_file_contains example-module/input.json '"/tmp/create-multiple-files-1.txt"' +assert_file_contains example-module/input.json '"/tmp/create-multiple-files-2.txt"' + +test_finish diff --git a/tests/shell/021_update_input_list_with_keys.sh b/tests/shell/021_update_input_list_with_keys.sh index 24c9c6b4..15c20d4a 100644 --- a/tests/shell/021_update_input_list_with_keys.sh +++ b/tests/shell/021_update_input_list_with_keys.sh @@ -1,23 +1,20 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/021_update_input_list_with_keys/example-module . cp ../shell/021_update_input_list_with_keys/example-cfbs.json cfbs.json cfbs validate cfbs --loglevel=debug --non-interactive update -grep '"while": "Create another file?"' example-module/input.json -grep '"label": "Path"' example-module/input.json -grep '"question": "Path of file?"' example-module/input.json -grep '"default": "/tmp/test.txt"' example-module/input.json -grep '"label": "Contents"' example-module/input.json -grep '"question": "File contents?"' example-module/input.json -grep '"default": "Hello CFEngine!"' example-module/input.json -grep '"response":' example-module/input.json -grep '{ "name": "/tmp/test-1.txt", "content": "Hello CFEngine!" }' example-module/input.json -grep '{ "name": "/tmp/test-2.txt", "content": "Bye CFEngine!" }' example-module/input.json +assert_file_contains example-module/input.json '"while": "Create another file?"' +assert_file_contains example-module/input.json '"label": "Path"' +assert_file_contains example-module/input.json '"question": "Path of file?"' +assert_file_contains example-module/input.json '"default": "/tmp/test.txt"' +assert_file_contains example-module/input.json '"label": "Contents"' +assert_file_contains example-module/input.json '"question": "File contents?"' +assert_file_contains example-module/input.json '"default": "Hello CFEngine!"' +assert_file_contains example-module/input.json '"response":' +assert_file_contains example-module/input.json '{ "name": "/tmp/test-1.txt", "content": "Hello CFEngine!" }' +assert_file_contains example-module/input.json '{ "name": "/tmp/test-2.txt", "content": "Bye CFEngine!" }' + +test_finish diff --git a/tests/shell/022_update_input_fail_variable.sh b/tests/shell/022_update_input_fail_variable.sh index 4564d3d0..35a66c67 100644 --- a/tests/shell/022_update_input_fail_variable.sh +++ b/tests/shell/022_update_input_fail_variable.sh @@ -1,14 +1,11 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/022_update_input_fail_variable/example-module . cp ../shell/022_update_input_fail_variable/example-cfbs.json cfbs.json cfbs validate -cfbs --loglevel=debug --non-interactive update > output.log 2>&1 -grep "Failed to update input data for module 'example-module'" ./output.log +run cfbs --loglevel=debug --non-interactive update +assert_output_contains "Failed to update input data for module 'example-module'" + +test_finish diff --git a/tests/shell/023_update_input_fail_number.sh b/tests/shell/023_update_input_fail_number.sh index d0b6b211..363e3dc7 100644 --- a/tests/shell/023_update_input_fail_number.sh +++ b/tests/shell/023_update_input_fail_number.sh @@ -1,14 +1,11 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/023_update_input_fail_number/example-module . cp ../shell/023_update_input_fail_number/example-cfbs.json cfbs.json cfbs validate -cfbs --loglevel=debug --non-interactive update > output.log 2>&1 -grep "Failed to update input data for module 'example-module'" ./output.log +run cfbs --loglevel=debug --non-interactive update +assert_output_contains "Failed to update input data for module 'example-module'" + +test_finish diff --git a/tests/shell/024_update_input_fail_bundle.sh b/tests/shell/024_update_input_fail_bundle.sh index 4a54f6b6..2b2d49af 100644 --- a/tests/shell/024_update_input_fail_bundle.sh +++ b/tests/shell/024_update_input_fail_bundle.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp -r ../shell/024_update_input_fail_bundle/example-module . cp ../shell/024_update_input_fail_bundle/example-cfbs.json cfbs.json @@ -15,5 +10,7 @@ cp ../shell/024_update_input_fail_bundle/example-cfbs.json cfbs.json # so the test now fails "earlier", during validation of the JSON. # Effectively, it now tests that cfbs update does validation, and fails on # the missing namespace field. -! cfbs --loglevel=debug --non-interactive update > output.log 2>&1 -grep 'The "namespace" field is required in module input elements' ./output.log +run cfbs --loglevel=debug --non-interactive update +assert_output_contains 'The "namespace" field is required in module input elements' + +test_finish diff --git a/tests/shell/025_add_input_remove.sh b/tests/shell/025_add_input_remove.sh index f73490c9..a7c3f8e8 100644 --- a/tests/shell/025_add_input_remove.sh +++ b/tests/shell/025_add_input_remove.sh @@ -1,14 +1,11 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf example-module cp ../shell/025_add_input_remove/example-cfbs.json cfbs.json cfbs --non-interactive add example-module cfbs --non-interactive input example-module -grep '"response": "/tmp/testfile.txt"' ./example-module/input.json +assert_file_contains ./example-module/input.json '"response": "/tmp/testfile.txt"' cfbs --non-interactive remove example-module + +test_finish diff --git a/tests/shell/026_init_no_masterfiles.sh b/tests/shell/026_init_no_masterfiles.sh index e1fe99e3..c06a0168 100644 --- a/tests/shell/026_init_no_masterfiles.sh +++ b/tests/shell/026_init_no_masterfiles.sh @@ -1,12 +1,9 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init --masterfiles=no -!( grep '"name": "masterfiles"' cfbs.json ) +assert_file_not_contains cfbs.json '"name": "masterfiles"' cfbs status -!( cfbs build ) +assert_failure cfbs build + +test_finish diff --git a/tests/shell/027_init_masterfiles_version_master.sh b/tests/shell/027_init_masterfiles_version_master.sh index 69106983..9a8c126d 100644 --- a/tests/shell/027_init_masterfiles_version_master.sh +++ b/tests/shell/027_init_masterfiles_version_master.sh @@ -1,12 +1,9 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init --masterfiles=master -grep '"name": "masterfiles"' cfbs.json -grep '"url": "https://github.com/cfengine/masterfiles"' cfbs.json +assert_file_contains cfbs.json '"name": "masterfiles"' +assert_file_contains cfbs.json '"url": "https://github.com/cfengine/masterfiles"' cfbs build + +test_finish diff --git a/tests/shell/028_init_masterfiles_version_3.18.2.sh b/tests/shell/028_init_masterfiles_version_3.18.2.sh index cfb804ae..99307d06 100644 --- a/tests/shell/028_init_masterfiles_version_3.18.2.sh +++ b/tests/shell/028_init_masterfiles_version_3.18.2.sh @@ -1,13 +1,10 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init --masterfiles=3.18.2 -grep '"name": "masterfiles"' cfbs.json -grep '"version": "3.18.2"' cfbs.json -grep '"commit": "a87b7fea6f7a88808b327730a4ba784a3dc664eb"' cfbs.json +assert_file_contains cfbs.json '"name": "masterfiles"' +assert_file_contains cfbs.json '"version": "3.18.2"' +assert_file_contains cfbs.json '"commit": "a87b7fea6f7a88808b327730a4ba784a3dc664eb"' cfbs build + +test_finish diff --git a/tests/shell/029_init_masterfiles_version_3.18.1-1.sh b/tests/shell/029_init_masterfiles_version_3.18.1-1.sh index d040c1dc..3b26d290 100644 --- a/tests/shell/029_init_masterfiles_version_3.18.1-1.sh +++ b/tests/shell/029_init_masterfiles_version_3.18.1-1.sh @@ -1,16 +1,13 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init --masterfiles=3.18.1-1 -grep '"name": "masterfiles"' cfbs.json -grep '"version": "3.18.1-1"' cfbs.json -grep '"commit": "b6e9eacc65c797f4c2b4a59056293636c320d0c9"' cfbs.json +assert_file_contains cfbs.json '"name": "masterfiles"' +assert_file_contains cfbs.json '"version": "3.18.1-1"' +assert_file_contains cfbs.json '"commit": "b6e9eacc65c797f4c2b4a59056293636c320d0c9"' cfbs build cfbs --non-interactive update -! grep '"version": "3.18.1-1"' cfbs.json -! grep '"commit": "b6e9eacc65c797f4c2b4a59056293636c320d0c9"' cfbs.json +assert_file_not_contains cfbs.json '"version": "3.18.1-1"' +assert_file_not_contains cfbs.json '"commit": "b6e9eacc65c797f4c2b4a59056293636c320d0c9"' + +test_finish diff --git a/tests/shell/030_get_set_input.sh b/tests/shell/030_get_set_input.sh index be7cd70b..0715a9aa 100644 --- a/tests/shell/030_get_set_input.sh +++ b/tests/shell/030_get_set_input.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf create-single-file echo '{ @@ -33,7 +28,7 @@ echo '[ "question": "What file should this module create?" } ]' > expected.output -diff actual.output expected.output +assert_diff actual.output expected.output # Igor adds response with some php magic echo '[ @@ -57,7 +52,7 @@ echo '[ "response": "/tmp/test-1.txt" } ]' > expected.output -diff actual.output expected.output +assert_diff actual.output expected.output # Igor changes the response to something else echo '[ @@ -81,7 +76,7 @@ echo '[ "response": "/tmp/test-2.txt" } ]' > expected.output -diff actual.output expected.output +assert_diff actual.output expected.output # Igor changes the wrong value echo '[ @@ -93,7 +88,7 @@ echo '[ "response": "/tmp/test-2.txt" } ]' > igors-input.json -! cfbs set-input create-single-file igors-input.json +assert_failure cfbs set-input create-single-file igors-input.json # Now Igor instead changes a key echo '[ @@ -105,7 +100,7 @@ echo '[ "response": "/tmp/test-2.txt" } ]' > igors-input.json -! cfbs set-input create-single-file igors-input.json +assert_failure cfbs set-input create-single-file igors-input.json # Igor changes the order but that's all right echo '[ @@ -129,7 +124,7 @@ echo '[ "question": "What file should this module create?" } ]' > expected.output -diff actual.output expected.output +assert_diff actual.output expected.output # Igor asks for input of a module that is not in the project cfbs get-input delete-files@0.0.1 actual.output @@ -158,6 +153,8 @@ echo '[ "while": "Specify another file you want deleted on your hosts?" } ]' > expected.output -diff actual.output expected.output +assert_diff actual.output expected.output echo "Igor is happy!" + +test_finish diff --git a/tests/shell/031_get_set_input_pipe.sh b/tests/shell/031_get_set_input_pipe.sh index 3d440e21..d9baaf6b 100644 --- a/tests/shell/031_get_set_input_pipe.sh +++ b/tests/shell/031_get_set_input_pipe.sh @@ -1,11 +1,5 @@ -set -e -set -x -cd tests/ -source shell/common.sh -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf delete-files cfbs --non-interactive init @@ -45,13 +39,15 @@ echo '[ commit_b=$(git rev-parse HEAD) -test "x$commit_a" != "x$commit_b" +assert_not_equal "$commit_a" "$commit_b" cfbs get-input delete-files - | cfbs set-input delete-files - commit_c=$(git rev-parse HEAD) -test "x$commit_b" = "x$commit_c" +assert_equal "$commit_b" "$commit_c" -git-must-track delete-files/input.json -git-no-diffs +assert_git_tracks delete-files/input.json +assert_git_no_diffs + +test_finish diff --git a/tests/shell/032_set_input_unordered.sh b/tests/shell/032_set_input_unordered.sh index deab2263..9b03d67a 100644 --- a/tests/shell/032_set_input_unordered.sh +++ b/tests/shell/032_set_input_unordered.sh @@ -1,11 +1,5 @@ -set -e -set -x -cd tests/ -source shell/common.sh -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf delete-files cfbs --non-interactive init @@ -42,5 +36,7 @@ echo '[ } ]' | cfbs --log=debug set-input delete-files - -git-must-track delete-files/input.json -git-no-diffs +assert_git_tracks delete-files/input.json +assert_git_no_diffs + +test_finish diff --git a/tests/shell/033_add_commits_local_files.sh b/tests/shell/033_add_commits_local_files.sh index 9b76ae3d..5f55d668 100644 --- a/tests/shell/033_add_commits_local_files.sh +++ b/tests/shell/033_add_commits_local_files.sh @@ -1,10 +1,6 @@ -set -e -set -x -cd tests/ -source shell/common.sh -mkdir -p ./tmp/ -cd ./tmp/ -rm -rf cfbs.json .git def.json policy.cf foo +source "$(dirname "$0")/testlib.sh" +test_init +rm -rf def.json policy.cf foo cfbs --non-interactive init @@ -23,7 +19,7 @@ cat < policy.cf bundle agent foo { reports: - "Hello from $(this.bundle)"; + "Hello from \$(this.bundle)"; } EOF @@ -40,14 +36,16 @@ cat < foo/baz.cf bundle agent baz { reports: - "Hello from $(this.bundle)"; + "Hello from \$(this.bundle)"; } EOF cfbs --non-interactive add ./def.json ./policy.cf ./foo -git-must-track def.json -git-must-track policy.cf -git-must-track foo/bar.json -git-must-track foo/baz.cf -git-no-diffs +assert_git_tracks def.json +assert_git_tracks policy.cf +assert_git_tracks foo/bar.json +assert_git_tracks foo/baz.cf +assert_git_no_diffs + +test_finish diff --git a/tests/shell/034_git_user_name_git_user_email.sh b/tests/shell/034_git_user_name_git_user_email.sh index c7e26946..77ae8ed6 100644 --- a/tests/shell/034_git_user_name_git_user_email.sh +++ b/tests/shell/034_git_user_name_git_user_email.sh @@ -1,9 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -rm -rf cfbs.json .git +source "$(dirname "$0")/testlib.sh" +test_init # Check that the options '--git-user-name' and '--git-user-email' is not # ignored by the 'set-input' command. @@ -41,3 +37,5 @@ cat <" + +test_finish diff --git a/tests/shell/035_cfbs_build_compatibility_1.sh b/tests/shell/035_cfbs_build_compatibility_1.sh index 8a3fc142..41d505c3 100644 --- a/tests/shell/035_cfbs_build_compatibility_1.sh +++ b/tests/shell/035_cfbs_build_compatibility_1.sh @@ -1,9 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -rm -rf ./* +source "$(dirname "$0")/testlib.sh" +test_init # The purpose of this test is to ensure that older CFEngine Build projects # still build in newer versions of cfbs @@ -37,14 +33,14 @@ echo '{ cfbs build # Look for some proof that the build actually did something: -grep 'bundle common inventory' out/masterfiles/promises.cf +assert_file_contains out/masterfiles/promises.cf 'bundle common inventory' # NOTE: We expect cfbs build to work, but not cfbs validate since # this older module entry has an empty string for "subdirectory". -!( cfbs validate ) +assert_failure cfbs validate # Same for cfbs status since it runs validate: -!( cfbs status ) +assert_failure cfbs status # Once more, but let's do download and build as separate steps: rm -rf out/ @@ -55,7 +51,7 @@ cfbs download cfbs build # Perform same checks again: -grep 'bundle common inventory' out/masterfiles/promises.cf +assert_file_contains out/masterfiles/promises.cf 'bundle common inventory' # Finally, let's see validation working if we fix the module: rm -rf out/ @@ -83,3 +79,5 @@ echo '{ ' > cfbs.json cfbs validate + +test_finish diff --git a/tests/shell/036_cfbs_build_compatibility_2.sh b/tests/shell/036_cfbs_build_compatibility_2.sh index a1a15e9b..c56ca00e 100644 --- a/tests/shell/036_cfbs_build_compatibility_2.sh +++ b/tests/shell/036_cfbs_build_compatibility_2.sh @@ -1,9 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -rm -rf ./* +source "$(dirname "$0")/testlib.sh" +test_init # This test is similar to the previous test, except it has more modules. @@ -84,18 +80,18 @@ echo '{ cfbs build # Look for some proof that these modules are actually being built into the policy set: -grep 'services_autorun' out/masterfiles/def.json -grep '"control_executor_splaytime": "1",' out/masterfiles/def.json -grep 'inventory_systemd_service_units_running' out/masterfiles/def.json -grep 'bundle common inventory' out/masterfiles/promises.cf -grep '$(paths.systemctl) list-units --type=service --state=running' out/masterfiles/services/inventory-systemd/main.cf +assert_file_contains out/masterfiles/def.json 'services_autorun' +assert_file_contains out/masterfiles/def.json '"control_executor_splaytime": "1",' +assert_file_contains out/masterfiles/def.json 'inventory_systemd_service_units_running' +assert_file_contains out/masterfiles/promises.cf 'bundle common inventory' +assert_file_contains out/masterfiles/services/inventory-systemd/main.cf '$(paths.systemctl) list-units --type=service --state=running' # NOTE: We expect cfbs build to work, but not cfbs validate since # this older module entry has an empty string for "subdirectory". -!( cfbs validate ) +assert_failure cfbs validate # Same for cfbs status since it runs validate: -!( cfbs status ) +assert_failure cfbs status # Once more, but let's do download and build as separate steps: rm -rf out/ @@ -106,8 +102,10 @@ cfbs download cfbs build # Perform same checks again: -grep 'services_autorun' out/masterfiles/def.json -grep '"control_executor_splaytime": "1",' out/masterfiles/def.json -grep 'inventory_systemd_service_units_running' out/masterfiles/def.json -grep 'bundle common inventory' out/masterfiles/promises.cf -grep '$(paths.systemctl) list-units --type=service --state=running' out/masterfiles/services/inventory-systemd/main.cf +assert_file_contains out/masterfiles/def.json 'services_autorun' +assert_file_contains out/masterfiles/def.json '"control_executor_splaytime": "1",' +assert_file_contains out/masterfiles/def.json 'inventory_systemd_service_units_running' +assert_file_contains out/masterfiles/promises.cf 'bundle common inventory' +assert_file_contains out/masterfiles/services/inventory-systemd/main.cf '$(paths.systemctl) list-units --type=service --state=running' + +test_finish diff --git a/tests/shell/037_cfbs_validate.sh b/tests/shell/037_cfbs_validate.sh index a726c2be..2b1e46df 100644 --- a/tests/shell/037_cfbs_validate.sh +++ b/tests/shell/037_cfbs_validate.sh @@ -1,9 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -rm -rf ./* +source "$(dirname "$0")/testlib.sh" +test_init # A small index: @@ -87,7 +83,7 @@ echo '{ } ' > cfbs.json -!( cfbs validate ) +assert_failure cfbs validate # Same, but without listing a dependency @@ -200,7 +196,7 @@ echo '{ ] }' > cfbs.json -!( cfbs validate ) +assert_failure cfbs validate # Dependency exists in index, but not in build - should error: @@ -225,7 +221,9 @@ echo '{ ] }' > cfbs.json -!( cfbs validate ) +assert_failure cfbs validate # NOTE: This shell test just covers some basic cases # See the unit tests for more thorough testing of validation + +test_finish diff --git a/tests/shell/038_global_dir.sh b/tests/shell/038_global_dir.sh index 40caffac..833d6909 100644 --- a/tests/shell/038_global_dir.sh +++ b/tests/shell/038_global_dir.sh @@ -1,9 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -rm -rf ./* +source "$(dirname "$0")/testlib.sh" +test_init # Try to be nice to the user - back up and restore their # module cache (~/.cfengine/cfbs/downloads): @@ -33,8 +29,8 @@ if [ -d ~/.cfengine/cfbs ]; then # Global dir used by cfbs by default mv ~/.cfengine/cfbs ~/.cfengine/cfbs_backup fi -test ! -e ./out/cfbs_global/ -test ! -e ~/.cfengine/cfbs +assert_file_not_exists ./out/cfbs_global/ +assert_file_not_exists ~/.cfengine/cfbs # CFBS_GLOBAL_DIR allows us to override ~/.cfengine/cfbs with # another path, for example for situations where you want to run @@ -43,9 +39,9 @@ CFBS_GLOBAL_DIR="./out/cfbs_global" cfbs --non-interactive init CFBS_GLOBAL_DIR="./out/cfbs_global" cfbs download # Check that something was downloaded in the correct place: -ls ./out/cfbs_global/downloads/github.com/cfengine/masterfiles/* +assert_file_exists ./out/cfbs_global/downloads/github.com/cfengine/masterfiles/* # And nothing was downloaded or created in the wrong place: -test ! -e ~/.cfengine/cfbs +assert_file_not_exists ~/.cfengine/cfbs # Test some other commands, just in case: rm -rf "./out/cfbs_global" @@ -54,5 +50,7 @@ CFBS_GLOBAL_DIR="./out/cfbs_global" cfbs download CFBS_GLOBAL_DIR="./out/cfbs_global" cfbs build # Same checks as above: -ls ./out/cfbs_global/downloads/github.com/cfengine/masterfiles/* -test ! -e ~/.cfengine/cfbs +assert_file_exists ./out/cfbs_global/downloads/github.com/cfengine/masterfiles/* +assert_file_not_exists ~/.cfengine/cfbs + +test_finish diff --git a/tests/shell/039_add_added_by_field_update_1.sh b/tests/shell/039_add_added_by_field_update_1.sh index 4e89f82a..050d4121 100644 --- a/tests/shell/039_add_added_by_field_update_1.sh +++ b/tests/shell/039_add_added_by_field_update_1.sh @@ -1,23 +1,20 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init # Ensure adding a module during initialization is treated as adding manually -cat cfbs.json | grep -F "added_by" | grep -F "cfbs init" +assert_file_matches_regex cfbs.json '"added_by".*"cfbs init"' # TODO: the case of custom non-masterfiles module(s) should also be tested # Manually adding a module then manually adding its dependency should update the latter's `"added_by"` field in `cfbs.json` cfbs --non-interactive add package-method-winget -cat cfbs.json | grep -F "added_by" | grep -F "package-method-winget" -[ "$(cat cfbs.json | grep -F "added_by" | grep -F "cfbs add" -c)" -eq 1 ] +assert_file_matches_regex cfbs.json '"added_by".*"package-method-winget"' +assert_num_string_occurrences 1 cfbs.json '"cfbs add"' cfbs --non-interactive add powershell-execution-policy -! ( cat cfbs.json | grep -F "added_by" | grep -F "package-method-winget" ) -[ "$(cat cfbs.json | grep -F "added_by" | grep -F "cfbs add" -c)" -eq 2 ] +assert_file_not_contains cfbs.json '"added_by": "package-method-winget"' +assert_num_string_occurrences 2 cfbs.json '"cfbs add"' + +test_finish diff --git a/tests/shell/040_add_added_by_field_update_2.sh b/tests/shell/040_add_added_by_field_update_2.sh index 1ede9509..0f358d0d 100644 --- a/tests/shell/040_add_added_by_field_update_2.sh +++ b/tests/shell/040_add_added_by_field_update_2.sh @@ -1,17 +1,14 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init # Manually adding a dependency of a module then manually adding that module should not update the latter's `"added_by"` field in `cfbs.json` cfbs --non-interactive add powershell-execution-policy -cat cfbs.json | grep -F "added_by" | grep -F "cfbs add" +assert_file_matches_regex cfbs.json '"added_by".*"cfbs add"' cfbs --non-interactive add package-method-winget -! ( cat cfbs.json | grep -F "added_by" | grep -F "package-method-winget" ) +assert_file_not_contains cfbs.json '"added_by": "package-method-winget"' + +test_finish diff --git a/tests/shell/041_add_multidep.sh b/tests/shell/041_add_multidep.sh index 22578051..e9a3d1c5 100644 --- a/tests/shell/041_add_multidep.sh +++ b/tests/shell/041_add_multidep.sh @@ -1,21 +1,17 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init -cfbs --non-interactive add https://github.com/cfengine/test-cfbs-static-repo > output.log 2>&1 +run cfbs --non-interactive add https://github.com/cfengine/test-cfbs-static-repo # All four modules were correctly added -grep -F "Added module: test-library-parsed-local-users" ./output.log -grep -F "Added module: test-library-parsed-etc-group" ./output.log -grep -F "Added module: test-inventory-local-groups" ./output.log -grep -F "Added module: test-inventory-local-users" ./output.log +assert_output_contains "Added module: test-library-parsed-local-users" +assert_output_contains "Added module: test-library-parsed-etc-group" +assert_output_contains "Added module: test-inventory-local-groups" +assert_output_contains "Added module: test-inventory-local-users" # Adding modules together with their dependencies should not display skipping messages (CFE-3841): -! ( grep -F "Skipping already added" ./output.log ) +assert_output_not_contains "Skipping already added" +test_finish diff --git a/tests/shell/042_update_from_url.sh b/tests/shell/042_update_from_url.sh index 79982aac..385421ee 100644 --- a/tests/shell/042_update_from_url.sh +++ b/tests/shell/042_update_from_url.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init rm -rf delete-files cp ../shell/042_update_from_url/example-cfbs.json cfbs.json @@ -13,5 +8,7 @@ cfbs validate cp -r ../shell/042_update_from_url/delete-files . cfbs --loglevel=debug --non-interactive update -grep 'Specify another file you want deleted on your hosts?' cfbs.json -grep 'Why should this file be deleted?' cfbs.json +assert_file_contains cfbs.json 'Specify another file you want deleted on your hosts?' +assert_file_contains cfbs.json 'Why should this file be deleted?' + +test_finish diff --git a/tests/shell/043_replace_version.sh b/tests/shell/043_replace_version.sh index f78c87e5..78f19a26 100644 --- a/tests/shell/043_replace_version.sh +++ b/tests/shell/043_replace_version.sh @@ -1,8 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ +source "$(dirname "$0")/testlib.sh" +test_init # Set up the project we will build: cp ../shell/043_replace_version/example-cfbs.json ./cfbs.json @@ -12,18 +9,20 @@ mkdir -p subdir cp ../shell/043_replace_version/subdir/example.py ./subdir/example.py # Before building, version number is 0.0.0: -grep 'print("Version: 0.0.0")' ./subdir/example.py -! grep 'print("Version: 1.2.3")' ./subdir/example.py +assert_file_contains ./subdir/example.py 'print("Version: 0.0.0")' +assert_file_not_contains ./subdir/example.py 'print("Version: 1.2.3")' cfbs build # After building, input and output should be different: -! diff ./subdir/example.py ./out/masterfiles/services/cfbs/subdir/example.py +assert_no_diff ./subdir/example.py ./out/masterfiles/services/cfbs/subdir/example.py # Check that version number is correct in output: -grep 'print("Version: 1.2.3")' ./out/masterfiles/services/cfbs/subdir/example.py -! grep 'print("Version: 0.0.0")' ./out/masterfiles/services/cfbs/subdir/example.py +assert_file_contains ./out/masterfiles/services/cfbs/subdir/example.py 'print("Version: 1.2.3")' +assert_file_not_contains ./out/masterfiles/services/cfbs/subdir/example.py 'print("Version: 0.0.0")' # Also check that the input was not modified: -grep 'print("Version: 0.0.0")' ./subdir/example.py -! grep 'print("Version: 1.2.3")' ./subdir/example.py +assert_file_contains ./subdir/example.py 'print("Version: 0.0.0")' +assert_file_not_contains ./subdir/example.py 'print("Version: 1.2.3")' + +test_finish diff --git a/tests/shell/044_replace.sh b/tests/shell/044_replace.sh index ca4eff18..089cc14f 100644 --- a/tests/shell/044_replace.sh +++ b/tests/shell/044_replace.sh @@ -1,8 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ +source "$(dirname "$0")/testlib.sh" +test_init # Set up the project we will build: cp ../shell/044_replace/example-cfbs.json ./cfbs.json @@ -14,10 +11,12 @@ cp ../shell/044_replace/subdir/example.expected.py ./subdir/example.expected.py cfbs build -ls out/masterfiles/services/cfbs/subdir/example.py +assert_file_exists out/masterfiles/services/cfbs/subdir/example.py # Replace should have changed it: -! diff ./subdir/example.py out/masterfiles/services/cfbs/subdir/example.py > /dev/null +assert_no_diff ./subdir/example.py out/masterfiles/services/cfbs/subdir/example.py # This is the expected content: -diff ./subdir/example.expected.py out/masterfiles/services/cfbs/subdir/example.py +assert_diff ./subdir/example.expected.py out/masterfiles/services/cfbs/subdir/example.py + +test_finish diff --git a/tests/shell/045_update_from_url_branch_uptodate.sh b/tests/shell/045_update_from_url_branch_uptodate.sh index f916f6e4..980ce3d8 100644 --- a/tests/shell/045_update_from_url_branch_uptodate.sh +++ b/tests/shell/045_update_from_url_branch_uptodate.sh @@ -1,17 +1,14 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init --masterfiles no cfbs --non-interactive add https://github.com/cfengine/test-cfbs-static-repo/@update-test-branch test-library-parsed-local-users # check that cfbs.json contains the right commit hash (ideally for testing, different than the default branch's commit hash): -grep '"commit": "2152eb5a39fbf9b051105b400639b436bd53ab87"' cfbs.json +assert_file_contains cfbs.json '"commit": "2152eb5a39fbf9b051105b400639b436bd53ab87"' # check that branch key is correctly set: -grep '"branch": "update-test-branch"' cfbs.json +assert_file_contains cfbs.json '"branch": "update-test-branch"' cfbs update test-library-parsed-local-users | grep "Module 'test-library-parsed-local-users' already up to date" + +test_finish diff --git a/tests/shell/046_update_from_url_branch.sh b/tests/shell/046_update_from_url_branch.sh index 20f22fce..3deb7fb7 100644 --- a/tests/shell/046_update_from_url_branch.sh +++ b/tests/shell/046_update_from_url_branch.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp/ -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cfbs --non-interactive init --masterfiles no cfbs --non-interactive add https://github.com/cfengine/test-cfbs-static-repo/@update-test-branch test-library-parsed-local-users @@ -13,4 +8,6 @@ cp ../shell/046_update_from_url_branch/cfbs.json . cfbs update test-library-parsed-local-users | grep "Updated module 'test-library-parsed-local-users' from url" # check that the commit hash changed: -grep '"commit": "2152eb5a39fbf9b051105b400639b436bd53ab87"' cfbs.json +assert_file_contains cfbs.json '"commit": "2152eb5a39fbf9b051105b400639b436bd53ab87"' + +test_finish diff --git a/tests/shell/047_absolute_path_modules.sh b/tests/shell/047_absolute_path_modules.sh index 0152b91c..5f539c96 100644 --- a/tests/shell/047_absolute_path_modules.sh +++ b/tests/shell/047_absolute_path_modules.sh @@ -1,10 +1,5 @@ -set -e -set -x -cd tests/ -mkdir -p ./tmp -cd ./tmp/ -touch cfbs.json && rm cfbs.json -rm -rf .git +source "$(dirname "$0")/testlib.sh" +test_init cleanup() { rm -rf /tmp/foo @@ -26,10 +21,10 @@ cd - # run cfbs cfbs --non-interactive init --masterfiles no -cfbs --non-interactive add /tmp/foo +cfbs --non-interactive add /tmp/foo cfbs build -grep "$head_commit" cfbs.json +assert_file_contains cfbs.json "$head_commit" # Add second commit cp ../sample/bar/baz/main.cf /tmp/foo/baz.cf @@ -43,4 +38,6 @@ cd - cfbs --non-interactive update cfbs build -grep "$head_commit" cfbs.json +assert_file_contains cfbs.json "$head_commit" + +test_finish diff --git a/tests/shell/all.sh b/tests/shell/all.sh index 7ff7f595..b30b58c8 100644 --- a/tests/shell/all.sh +++ b/tests/shell/all.sh @@ -1,55 +1,108 @@ +#!/usr/bin/env bash echo "Warning: These shell based tests use the cfbs you have installed" echo " If you haven't already, run: pip install ." set -e -set -x export CFBS_USER_AGENT=CI # this user agent will be excluded from the build modules statistics -bash tests/shell/001_init.sh -bash tests/shell/002_add.sh -bash tests/shell/003_download.sh -bash tests/shell/004_build.sh -bash tests/shell/005_alias.sh -bash tests/shell/006_search.sh -bash tests/shell/007_install.sh -bash tests/shell/008_remove.sh -bash tests/shell/009_clean.sh -bash tests/shell/010_local_add.sh -bash tests/shell/011_update.sh -bash tests/shell/012_prepend_dot_slash.sh -bash tests/shell/013_add_url_commit.sh -bash tests/shell/014_add_nonexistent.sh -bash tests/shell/015_add_version.sh -bash tests/shell/016_add_folders.sh -bash tests/shell/017_info.sh -bash tests/shell/018_update_input_one_variable.sh -bash tests/shell/019_update_input_two_variables.sh -bash tests/shell/020_update_input_list.sh -bash tests/shell/021_update_input_list_with_keys.sh -bash tests/shell/022_update_input_fail_variable.sh -bash tests/shell/023_update_input_fail_number.sh -bash tests/shell/024_update_input_fail_bundle.sh -bash tests/shell/025_add_input_remove.sh -bash tests/shell/026_init_no_masterfiles.sh -bash tests/shell/027_init_masterfiles_version_master.sh -bash tests/shell/028_init_masterfiles_version_3.18.2.sh -bash tests/shell/029_init_masterfiles_version_3.18.1-1.sh -bash tests/shell/030_get_set_input.sh -bash tests/shell/031_get_set_input_pipe.sh -bash tests/shell/032_set_input_unordered.sh -bash tests/shell/033_add_commits_local_files.sh -bash tests/shell/034_git_user_name_git_user_email.sh -bash tests/shell/035_cfbs_build_compatibility_1.sh -bash tests/shell/036_cfbs_build_compatibility_2.sh -bash tests/shell/037_cfbs_validate.sh -bash tests/shell/038_global_dir.sh -bash tests/shell/039_add_added_by_field_update_1.sh -bash tests/shell/040_add_added_by_field_update_2.sh -bash tests/shell/041_add_multidep.sh -bash tests/shell/042_update_from_url.sh -bash tests/shell/043_replace_version.sh -bash tests/shell/044_replace.sh -bash tests/shell/045_update_from_url_branch_uptodate.sh -bash tests/shell/046_update_from_url_branch.sh -bash tests/shell/047_absolute_path_modules.sh +_passed=0 +_failed=0 +_skipped=0 +_total=0 +_failures="" +_suite_start=$(date +%s) +run_test() { + local test_script="$1" + local test_name + test_name=$(basename "$test_script" .sh) + _total=$((_total + 1)) + + local start end elapsed + start=$(date +%s) + + local output + local exit_code=0 + output=$(bash "$test_script" 2>&1) || exit_code=$? + + end=$(date +%s) + elapsed=$((end - start)) + + if [ $exit_code -eq 0 ]; then + if echo "$output" | grep -q "^--- SKIP:"; then + _skipped=$((_skipped + 1)) + echo "--- SKIP: $test_name (${elapsed}s)" + else + _passed=$((_passed + 1)) + echo "--- PASS: $test_name (${elapsed}s)" + fi + else + _failed=$((_failed + 1)) + _failures="${_failures} ${test_name}\n" + echo "--- FAIL: $test_name (${elapsed}s)" + echo "$output" + echo "---" + fi +} + +run_test tests/shell/001_init.sh +run_test tests/shell/002_add.sh +run_test tests/shell/003_download.sh +run_test tests/shell/004_build.sh +run_test tests/shell/005_alias.sh +run_test tests/shell/006_search.sh +run_test tests/shell/007_install.sh +run_test tests/shell/008_remove.sh +run_test tests/shell/009_clean.sh +run_test tests/shell/010_local_add.sh +run_test tests/shell/011_update.sh +run_test tests/shell/012_prepend_dot_slash.sh +run_test tests/shell/013_add_url_commit.sh +run_test tests/shell/014_add_nonexistent.sh +run_test tests/shell/015_add_version.sh +run_test tests/shell/016_add_folders.sh +run_test tests/shell/017_info.sh +run_test tests/shell/018_update_input_one_variable.sh +run_test tests/shell/019_update_input_two_variables.sh +run_test tests/shell/020_update_input_list.sh +run_test tests/shell/021_update_input_list_with_keys.sh +run_test tests/shell/022_update_input_fail_variable.sh +run_test tests/shell/023_update_input_fail_number.sh +run_test tests/shell/024_update_input_fail_bundle.sh +run_test tests/shell/025_add_input_remove.sh +run_test tests/shell/026_init_no_masterfiles.sh +run_test tests/shell/027_init_masterfiles_version_master.sh +run_test tests/shell/028_init_masterfiles_version_3.18.2.sh +run_test tests/shell/029_init_masterfiles_version_3.18.1-1.sh +run_test tests/shell/030_get_set_input.sh +run_test tests/shell/031_get_set_input_pipe.sh +run_test tests/shell/032_set_input_unordered.sh +run_test tests/shell/033_add_commits_local_files.sh +run_test tests/shell/034_git_user_name_git_user_email.sh +run_test tests/shell/035_cfbs_build_compatibility_1.sh +run_test tests/shell/036_cfbs_build_compatibility_2.sh +run_test tests/shell/037_cfbs_validate.sh +run_test tests/shell/038_global_dir.sh +run_test tests/shell/039_add_added_by_field_update_1.sh +run_test tests/shell/040_add_added_by_field_update_2.sh +run_test tests/shell/041_add_multidep.sh +run_test tests/shell/042_update_from_url.sh +run_test tests/shell/043_replace_version.sh +run_test tests/shell/044_replace.sh +run_test tests/shell/045_update_from_url_branch_uptodate.sh +run_test tests/shell/046_update_from_url_branch.sh +run_test tests/shell/047_absolute_path_modules.sh + +# Summary +_suite_end=$(date +%s) +_suite_elapsed=$((_suite_end - _suite_start)) +echo "" +echo "==============================" +echo "Test Results: $_passed passed, $_failed failed, $_skipped skipped (total: $_total, ${_suite_elapsed}s)" +if [ $_failed -gt 0 ]; then + echo "" + echo "Failed tests:" + echo -e "$_failures" + exit 1 +fi +echo "==============================" echo "All cfbs shell tests completed successfully!" diff --git a/tests/shell/common.sh b/tests/shell/common.sh deleted file mode 100644 index da86d38b..00000000 --- a/tests/shell/common.sh +++ /dev/null @@ -1,13 +0,0 @@ -git-must-track () { - # $1: Path / filename - # Error if the file has never been added: - git ls-files --error-unmatch $1 -} - -git-no-diffs () { - # Error if there are staged changes (added, not yet commited): - git diff --exit-code --staged - - # Error if there are uncommited changes (to tracked files): - git diff --exit-code -} diff --git a/tests/shell/testlib.sh b/tests/shell/testlib.sh new file mode 100644 index 00000000..d6d796f2 --- /dev/null +++ b/tests/shell/testlib.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# testlib.sh - Minimal test framework for cfbs shell tests +# +# Usage in test files: +# source "$(dirname "$0")/testlib.sh" +# test_init +# ... test body ... +# test_finish + +# --- Internal state --- +_TEST_NAME="" +_TEST_START_TIME="" +_LAST_OUTPUT="" +_LAST_EXIT_CODE=0 +_TESTLIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# --- Internal helpers --- + +_test_name_from_path() { + basename "$1" .sh +} + +_test_fail() { + set +x + echo "FAIL: $*" >&2 + return 1 +} + +# --- Lifecycle --- + +skip_unless_unsafe() { + if [ "${UNSAFE_TESTS:-}" != 1 ]; then + local name + name=$(_test_name_from_path "${BASH_SOURCE[1]:-$0}") + echo "--- SKIP: $name (unsafe tests disabled) ---" + exit 0 + fi +} + +test_init() { + set -e + set -x + + _TEST_NAME=$(_test_name_from_path "${BASH_SOURCE[1]:-$0}") + + rm -rf ./tests/tmp/ + mkdir -p ./tests/tmp/ + cd ./tests/tmp/ + + _TEST_START_TIME=$(date +%s) + echo "--- START: $_TEST_NAME ---" >&2 +} + +test_finish() { + local end elapsed + end=$(date +%s) + elapsed=$(( end - _TEST_START_TIME )) + set +x + echo "--- PASS: $_TEST_NAME (${elapsed}s) ---" >&2 +} + +# --- Git assertions --- + +assert_git_tracks() { + # $1: Path / filename + # Error if the file has never been added: + git ls-files --error-unmatch "$1" || _test_fail "Expected git to track: $1" +} + +assert_git_no_diffs() { + # Error if there are staged changes (added, not yet commited): + git diff --exit-code --staged || _test_fail "Unexpected staged changes" + + # Error if there are uncommited changes (to tracked files): + git diff --exit-code || _test_fail "Unexpected uncommited changes" +} + +# --- Assertions --- + +assert_file_exists() { + [ -e "$1" ] || _test_fail "Expected file to exist: $1" +} + +assert_file_not_exists() { + [ ! -e "$1" ] || _test_fail "Expected file to not exist: $1" +} + +assert_file_contains() { + grep -qF "$2" "$1" || _test_fail "Expected file '$1' to contain: $2" +} + +assert_file_not_contains() { + ! grep -qF "$2" "$1" || _test_fail "Expected file '$1' to NOT contain: $2" +} + +assert_file_matches_regex() { + grep -qE "$2" "$1" || _test_fail "Expected file '$1' to match regex: $2" +} + +assert_output_contains() { + echo "$_LAST_OUTPUT" | grep -qF "$1" \ + || _test_fail "Expected output to contain: $1" +} + +assert_output_not_contains() { + ! echo "$_LAST_OUTPUT" | grep -qF "$1" \ + || _test_fail "Expected output to NOT contain: $1" +} + +assert_success() { + "$@" || _test_fail "Expected command to succeed: $*" +} + +assert_failure() { + local rc=0 + "$@" || rc=$? + [ $rc -ne 0 ] || _test_fail "Expected command to fail: $*" +} + +assert_equal() { + [ "$1" = "$2" ] || _test_fail "Expected '$2' but got '$1'" +} + +assert_not_equal() { + [ "$1" != "$2" ] || _test_fail "Expected values to differ but both are: '$1'" +} + +assert_diff() { + diff "$1" "$2" || _test_fail "Files differ: $1 vs $2" +} + +assert_no_diff() { + ! diff -q "$1" "$2" > /dev/null 2>&1 \ + || _test_fail "Expected files to differ but they are identical: $1 vs $2" +} + +assert_num_string_occurrences() { + local expected="$1" file="$2" pattern="$3" + local actual + actual=$(grep -cF "$pattern" "$file" || true) + [ "$actual" -eq "$expected" ] \ + || _test_fail "Expected $expected occurrences of '$pattern' in '$file' but found $actual" +} + +# --- Run / Capture --- + +run() { + local rc=0 + _LAST_OUTPUT=$("$@" 2>&1) || rc=$? + _LAST_EXIT_CODE=$rc + if [ $rc -ne 0 ]; then + _test_fail "Command failed (exit $rc): $*" + fi +} + +run_expect_failure() { + local rc=0 + _LAST_OUTPUT=$("$@" 2>&1) || rc=$? + _LAST_EXIT_CODE=$rc + if [ $rc -eq 0 ]; then + _test_fail "Expected command to fail: $*" + fi +}