From 863576b29aa18b905524beb9990dbdd80a66d780 Mon Sep 17 00:00:00 2001 From: Mike Sterle-Contala <2691351+msterle@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:01:23 +0000 Subject: [PATCH] Drl 59/implement shellspec (#23) * add shellspec config and example tests * Add shellspec to github workflow * add pot mock, example tests --- .../{lint.yaml => lint-and-test.yaml} | 21 +++++- .shellspec | 12 ++++ README.md | 9 +++ spec/simple_spec.sh | 70 +++++++++++++++++++ spec/spec_helper.sh | 24 +++++++ spec/support/pot_mock.sh | 61 ++++++++++++++++ 6 files changed, 196 insertions(+), 1 deletion(-) rename .github/workflows/{lint.yaml => lint-and-test.yaml} (55%) create mode 100644 .shellspec create mode 100644 spec/simple_spec.sh create mode 100644 spec/spec_helper.sh create mode 100644 spec/support/pot_mock.sh diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint-and-test.yaml similarity index 55% rename from .github/workflows/lint.yaml rename to .github/workflows/lint-and-test.yaml index 17acf34..7d1713a 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint-and-test.yaml @@ -1,4 +1,4 @@ -name: lint +name: lint-and-test on: [push] @@ -22,3 +22,22 @@ jobs: else echo "No shell scripts with #!/bin/sh shebang found." fi + + test: + name: Run shellspec + runs-on: ubuntu-latest + + steps: + - name: Install shellspec + run: | + cd ~ + wget https://github.com/shellspec/shellspec/archive/0.28.1.tar.gz + tar xzvf 0.28.1.tar.gz + mkdir ~/.local/bin + ln -s ~/shellspec-0.28.1/bin/shellspec ~/.local/bin/ + + - name: Check out code + uses: actions/checkout@v4 + + - name: Run shellspec + run: shellspec diff --git a/.shellspec b/.shellspec new file mode 100644 index 0000000..d567ecf --- /dev/null +++ b/.shellspec @@ -0,0 +1,12 @@ +--require spec_helper + +## Default kcov (coverage) options +# --kcov-options "--include-path=. --path-strip-level=1" +# --kcov-options "--include-pattern=.sh" +# --kcov-options "--exclude-pattern=/.shellspec,/spec/,/coverage/,/report/" + +## Example: Include script "myprog" with no extension +# --kcov-options "--include-pattern=.sh,myprog" + +## Example: Only specified files/directories +# --kcov-options "--include-pattern=myprog,/lib/" diff --git a/README.md b/README.md index 75454f0..7b715b0 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,15 @@ To configure healthchecks via `crontab -e`, add the following in your editor of */2 * * * * /etc/cron.d/restart-actions.sh ``` +Development +----------- + +### Testing +This project uses [`shellspec`](https://github.com/shellspec/shellspec) as a test framework. To run tests locally, `shellspec` must first be installed on the local system according to the `shellspec` [installation guide](https://github.com/shellspec/shellspec?tab=readme-ov-file#installation). + +Tests are defined in the `spec` directory, and are all files matching the pattern `_spec.sh`. Mocks, custom matchers and other fixtures can be defined in the `spec/support` directory. + +The `pot` mock has already been defined and is imported into all tests. By default, all `pot` subcommands return truthy with no side effects, but these can be overloaded on a per-subcommand basis, as each subcommand has its own stub function. See [here](spec/simple_spec.sh#L47) for an example of overloading the `pot exec` subcommand, and [here](spec/support/pot_mock.sh#L5) for a list of all subcommand stubs that can be overloaded. Wrapper scripts to run GitHub actions in a jail on FreeBSD ---------------------------------------------------------- diff --git a/spec/simple_spec.sh b/spec/simple_spec.sh new file mode 100644 index 0000000..852d9c2 --- /dev/null +++ b/spec/simple_spec.sh @@ -0,0 +1,70 @@ +#shellcheck shell=bash + +Describe 'check-pots.sh' + Include ./check-pots.sh + prepare() { pot='test'; } + Before 'prepare' + + Describe 'check_tree()' + It 'Does not output a warning if the given tree exists' + tree_name='some_tree' + mkdir $tree_name + When call check_tree $tree_name + The output should not include 'was not found' + rm -fr $tree_name + End + + It 'Outputs a warning if the given tree does not exist' + tree_name='some_tree' + When call check_tree $tree_name + The output should include 'was not found' + End + + It 'Outputs a debug message if the given tree is empty' + tree_name='some_tree' + mkdir $tree_name + When call check_tree 'some_tree' + The output should include 'is empty' + rm -fr $tree_name + End + + It 'Has no output if the tree is populated' + tree_name='some_tree' + mkdir $tree_name + touch ${tree_name}/abc + When call check_tree 'some_tree' + The output should be blank + rm -fr $tree_name + End + End + + Describe 'check_pot()' + It 'Should emit a warning if sshd is disabled' + check_tree() { :; } + pot_exec() { + case $3 in + sysrc) echo 'NO';; + which) echo '/usr/sbin/pkg64';; + esac + } + When call check_pot + The lines of output should equal 1 + The output should include 'sshd is disabled on' + End + + It 'Should emit a warning if pkg binaries cannot be found' + check_tree() { :; } + pot_exec() { + case $3 in + sysrc) echo '';; + which) echo '';; + esac + } + When call check_pot + The lines of output should equal 3 + The line 1 of output should include "pkg64 on $pot was not found" + The line 2 of output should include "pkg64c on $pot was not found" + The line 3 of output should include "pk64cb on $pot was not found" + End + End +End diff --git a/spec/spec_helper.sh b/spec/spec_helper.sh new file mode 100644 index 0000000..ab8f10c --- /dev/null +++ b/spec/spec_helper.sh @@ -0,0 +1,24 @@ +# shellcheck shell=sh + +# Defining variables and functions here will affect all specfiles. +# Change shell options inside a function may cause different behavior, +# so it is better to set them here. +# set -eu + +# This callback function will be invoked only once before loading specfiles. +spec_helper_precheck() { + # Available functions: info, warn, error, abort, setenv, unsetenv + # Available variables: VERSION, SHELL_TYPE, SHELL_VERSION + : minimum_version "0.28.1" +} + +# This callback function will be invoked after a specfile has been loaded. +spec_helper_loaded() { + : +} + +# This callback function will be invoked after core modules has been loaded. +spec_helper_configure() { + # Available functions: import, before_each, after_each, before_all, after_all + import 'support/pot_mock' +} diff --git a/spec/support/pot_mock.sh b/spec/support/pot_mock.sh new file mode 100644 index 0000000..02dcdfc --- /dev/null +++ b/spec/support/pot_mock.sh @@ -0,0 +1,61 @@ +# shellcheck shell=sh + +pot() { + case $1 in + help) fn='pot_help';; + version) fn='pot_version';; + config) fn='pot_config';; + ls|list) fn='pot_list';; + show) fn='pot_show';; + info) fn='pot_info';; + top) fn='pot_top';; + ps) fn='pot_ps';; + init) fn='pot_init';; + de-init) fn='pot_de_init';; + vnet-start) fn='pot_vnet_start';; + create-base) fn='pot_create_base';; + create-fscomp) fn='pot_create_fscomp';; + create-private-bridge) fn='pot_create_private_bridge';; + create) fn='pot_create';; + clone) fn='pot_clone';; + clone-fscomp) fn='pot_clone_fscomp';; + rename) fn='pot_rename';; + destroy) fn='pot_destroy';; + prune) fn='pot_prune';; + copy-in) fn='pot_copy_in';; + copy-out) fn='pot_copy_out';; + mount-in) fn='pot_mount_in';; + mount-out) fn='pot_mount_out';; + add-dep) fn='pot_add_dep';; + set-rss) fn='pot_set_rss';; + get-rss) fn='pot_get_rss';; + set-cmd) fn='pot_set_cmd';; + set-env) fn='pot_set_env';; + set-hosts) fn='pot_set_hosts';; + set-hook) fn='pot_set_hook';; + set-attribute|set-attr) fn='pot_set_attribute';; + get-attribute|get-attr) fn='pot_get_attribute';; + export-ports) fn='pot_export_ports';; + start) fn='pot_start';; + stop) fn='pot_stop';; + term) fn='pot_term';; + run) fn='pot_run';; + snap|snapshot) fn='pot_snapshot';; + rollback|revert) fn='pot_revert';; + purge-snapshots) fn='pot_purge_snapshots';; + export) fn='pot_export';; + import) fn='pot_import';; + prepare) fn='pot_prepare';; + update-config) fn='pot_update_config';; + last-run-stats) fn='pot_last_run_stats';; + signal) fn='pot_signal';; + exec) fn='pot_exec';; + *) fn='pot_other';; + esac + shift 1 + if type "$fn" 2>/dev/null | grep -q 'function'; then + $fn "$@" + else + : + fi +}