Introduce bash scripting unittests (#724) All checks were successful check-and-test / check-and-test (push) Successful in 51s
All checks were successful
check-and-test / check-and-test (push) Successful in 51s
### Description of the change This adds an environment for unit testing our bash scripts, using [BATS](https://github.com/bats-core/bats-core). It implements first tests for `config_environment.sh`. ### Benefits Writing unit tests for bash scripts documents the expected behavior and allows it being a quality gate in our CI. ### Possible drawbacks Not everyone is familiar with this approach and unit testing framework. Me neither, it took me some hours to get into it. ### Applicable issues - Related to #691 where an issue in `config_environment.sh` was detected. It doesn't fixes it yet. This will be a dedicated Pull Request. ### Additional information I've verified that the changes for Renovate are indeed working. You may wonder why there is only one `run $PROJECT_ROOT/scripts/init-containers/config/config_environment.sh` and many `run execute_test_script` calls. Usually, testing a script itself would be executing `run $PROJECT_ROOT/scripts/init-containers/config/config_environment.sh`. You then can assert the exit code and other things. Since the `config_environment.sh` exports environment variables and we are not able to access them from outside a `run` execution, the function `execute_test_script` wraps our script execution between environment comparison. Doing so allows us capture environment variables that were added/removed during script execution. Reviewed-on: gitea/helm-chart#724 Reviewed-by: pat-s <pat-s@noreply.gitea.com> Co-authored-by: justusbunsi <sk.bunsenbrenner@gmail.com> Co-committed-by: justusbunsi <sk.bunsenbrenner@gmail.com>
This commit was merged in pull request #724.
This commit is contained in:
@@ -23,7 +23,7 @@ | ||||
### Applicable issues | ||||
| ||||
<!-- Enter any applicable Issues here (You can reference an issue using #). Please remove this section if there is no referenced issue. --> | ||||
- fixes # | ||||
- Fixes # | ||||
| ||||
### Additional information | ||||
| ||||
@@ -39,5 +39,6 @@ | ||||
| ||||
- [ ] Parameters are documented in the `values.yaml` and added to the `README.md` using [readme-generator-for-helm](https://github.com/bitnami-labs/readme-generator-for-helm) | ||||
- [ ] Breaking changes are documented in the `README.md` | ||||
- [ ] Templating unittests are added | ||||
- [ ] Helm templating unittests are added (required when changing anything in `templates` folder) | ||||
- [ ] Bash unittests are added (required when changing anything in `scripts` folder) | ||||
- [ ] All added template resources MUST render a namespace in metadata | ||||
| ||||
@@ -20,7 +20,7 @@ jobs: | ||||
- name: install tools | ||||
run: | | ||||
apk update | ||||
apk add --update make nodejs npm yamllint | ||||
apk add --update bash make nodejs npm yamllint ncurses | ||||
- uses: actions/checkout@v4 | ||||
- name: install chart dependencies | ||||
run: helm dependency build | ||||
@@ -28,9 +28,14 @@ jobs: | ||||
run: helm lint | ||||
- name: template | ||||
run: helm template --debug gitea-helm . | ||||
- name: unit tests | ||||
- name: prepare unit test environment | ||||
run: | | ||||
helm plugin install --version ${{ env.HELM_UNITTEST_VERSION }} https://github.com/helm-unittest/helm-unittest | ||||
git submodule update --init --recursive | ||||
- name: unit tests | ||||
env: | ||||
TERM: xterm | ||||
run: | | ||||
make unittests | ||||
- name: verify readme | ||||
run: | | ||||
| ||||
12 .gitmodules vendored Normal file
12
.gitmodules vendored Normal file @@ -0,0 +1,12 @@ | ||||
[submodule "unittests/bash/bats"] | ||||
path = unittests/bash/bats | ||||
url = https://github.com/bats-core/bats-core.git | ||||
[submodule "unittests/bash/test_helper/bats-support"] | ||||
path = unittests/bash/test_helper/bats-support | ||||
url = https://github.com/bats-core/bats-support.git | ||||
[submodule "unittests/bash/test_helper/bats-assert"] | ||||
path = unittests/bash/test_helper/bats-assert | ||||
url = https://github.com/bats-core/bats-assert.git | ||||
[submodule "unittests/bash/test_helper/bats-mock"] | ||||
path = unittests/bash/test_helper/bats-mock | ||||
url = https://github.com/jasonkarns/bats-mock.git | ||||
@@ -5,6 +5,7 @@ | ||||
# Common VCS dirs | ||||
.git/ | ||||
.gitignore | ||||
.gitmodules | ||||
.bzr/ | ||||
.bzrignore | ||||
.hg/ | ||||
| ||||
3 .vscode/extensions.json vendored
3
.vscode/extensions.json vendored @@ -3,6 +3,7 @@ | ||||
"yzhang.markdown-all-in-one", | ||||
"DavidAnson.vscode-markdownlint", | ||||
"Tim-Koehler.helm-intellisense", | ||||
"esbenp.prettier-vscode" | ||||
"esbenp.prettier-vscode", | ||||
"jetmartin.bats" | ||||
] | ||||
} | ||||
| ||||
3 .vscode/settings.json vendored
3
.vscode/settings.json vendored @@ -5,6 +5,9 @@ | ||||
] | ||||
}, | ||||
"yaml.schemaStore.enable": true, | ||||
"[bats]": { | ||||
"editor.tabSize": 2 | ||||
}, | ||||
"[shellscript]": { | ||||
"files.eol": "\n", | ||||
"editor.tabSize": 2 | ||||
| ||||
@@ -5,7 +5,7 @@ ignore: | | ||||
.yamllint | ||||
node_modules | ||||
templates | ||||
| ||||
unittests/bash | ||||
| ||||
rules: | ||||
truthy: | ||||
@@ -17,4 +17,4 @@ rules: | ||||
comments: | ||||
min-spaces-from-content: 1 | ||||
braces: | ||||
max-spaces-inside: 2 | ||||
max-spaces-inside: 2 | ||||
| ||||
@@ -48,16 +48,30 @@ default port-forward svc/gitea-http 3000:3000`. | ||||
| ||||
### Unit tests | ||||
| ||||
#### Helm templating tests | ||||
| ||||
```bash | ||||
# install the unittest plugin | ||||
$ helm plugin install https://github.com/helm-unittest/helm-unittest | ||||
| ||||
# run the unittests | ||||
make unittests | ||||
# run the Helm unittests | ||||
make unittests-helm | ||||
``` | ||||
| ||||
See [plugin documentation](https://github.com/helm-unittest/helm-unittest/blob/main/DOCUMENT.md) for usage instructions. | ||||
| ||||
#### Bash script tests | ||||
| ||||
```bash | ||||
# setup the environment | ||||
git submodule update --init --recursive | ||||
| ||||
# run the bash tests | ||||
make unittests-bash | ||||
``` | ||||
| ||||
See [bats documentation](https://bats-core.readthedocs.io/en/stable/) for usage instructions. | ||||
| ||||
## Release process | ||||
| ||||
1. Create a tag following the tagging schema | ||||
| ||||
11 Makefile
11
Makefile @@ -1,3 +1,5 @@ | ||||
SHELL := /usr/bin/env bash -O globstar | ||||
| ||||
.PHONY: prepare-environment | ||||
prepare-environment: | ||||
npm install | ||||
@@ -8,9 +10,16 @@ readme: prepare-environment | ||||
npm run readme:lint | ||||
| ||||
.PHONY: unittests | ||||
unittests: | ||||
unittests: unittests-helm unittests-bash | ||||
| ||||
.PHONY: unittests-helm | ||||
unittests-helm: | ||||
helm unittest --strict -f 'unittests/helm/**/*.yaml' -f 'unittests/helm/values-conflicting-checks.yaml' ./ | ||||
| ||||
.PHONY: unittests-bash | ||||
unittests-bash: | ||||
./unittests/bash/bats/bin/bats --pretty ./unittests/bash/tests/**/*.bats | ||||
| ||||
.PHONY: helm | ||||
update-helm-dependencies: | ||||
helm dependency update | ||||
| ||||
@@ -10,6 +10,9 @@ | ||||
'kind/dependency', | ||||
], | ||||
automergeStrategy: 'squash', | ||||
'git-submodules': { | ||||
'enabled': true | ||||
}, | ||||
customManagers: [ | ||||
{ | ||||
description: 'Gitea-version of https://docs.renovatebot.com/presets-regexManagers/#regexmanagersgithubactionsversions', | ||||
| ||||
0 scripts/act_runner/token.sh Normal file → Executable file
0
scripts/act_runner/token.sh Normal file → Executable file 0 scripts/init-containers/config/config_environment.sh Normal file → Executable file
0
scripts/init-containers/config/config_environment.sh Normal file → Executable file 0 scripts/init-containers/init/configure_gpg_environment.sh Normal file → Executable file
0
scripts/init-containers/init/configure_gpg_environment.sh Normal file → Executable file 1 unittests/bash/bats Submodule
1
unittests/bash/bats Submodule Submodule unittests/bash/bats added at b640ec3cf2
1 unittests/bash/test_helper/bats-assert Submodule
1
unittests/bash/test_helper/bats-assert Submodule Submodule unittests/bash/test_helper/bats-assert added at e2d855bc78
1 unittests/bash/test_helper/bats-mock Submodule
1
unittests/bash/test_helper/bats-mock Submodule Submodule unittests/bash/test_helper/bats-mock added at 93e0128b87
1 unittests/bash/test_helper/bats-support Submodule
1
unittests/bash/test_helper/bats-support Submodule Submodule unittests/bash/test_helper/bats-support added at 9bf10e876d
7 unittests/bash/test_helper/common-setup.bash Normal file
7
unittests/bash/test_helper/common-setup.bash Normal file @@ -0,0 +1,7 @@ | ||||
#!/usr/bin/env bash | ||||
| ||||
function common_setup() { | ||||
load "$TEST_ROOT/test_helper/bats-support/load" | ||||
load "$TEST_ROOT/test_helper/bats-assert/load" | ||||
load "$TEST_ROOT/test_helper/bats-mock/stub" | ||||
} | ||||
@@ -0,0 +1,204 @@ | ||||
#!/usr/bin/env bats | ||||
| ||||
function setup() { | ||||
PROJECT_ROOT="$(git rev-parse --show-toplevel)" | ||||
TEST_ROOT="$PROJECT_ROOT/unittests/bash" | ||||
load "$TEST_ROOT/test_helper/common-setup" | ||||
common_setup | ||||
| ||||
export GITEA_APP_INI="$BATS_TEST_TMPDIR/app.ini" | ||||
export TMP_EXISTING_ENVS_FILE="$BATS_TEST_TMPDIR/existing-envs" | ||||
export ENV_TO_INI_MOUNT_POINT="$BATS_TEST_TMPDIR/env-to-ini-mounts" | ||||
| ||||
stub gitea \ | ||||
"generate secret INTERNAL_TOKEN : echo 'mocked-internal-token'" \ | ||||
"generate secret SECRET_KEY : echo 'mocked-secret-key'" \ | ||||
"generate secret JWT_SECRET : echo 'mocked-jwt-secret'" \ | ||||
"generate secret LFS_JWT_SECRET : echo 'mocked-lfs-jwt-secret'" | ||||
} | ||||
| ||||
function teardown() { | ||||
unstub gitea | ||||
# This condition exists due to https://github.com/jasonkarns/bats-mock/pull/37 being still open | ||||
if [ $ENV_TO_INI_EXPECTED -eq 1 ]; then | ||||
unstub environment-to-ini | ||||
fi | ||||
} | ||||
| ||||
# This function exists due to https://github.com/jasonkarns/bats-mock/pull/37 being still open | ||||
function expect_environment_to_ini_call() { | ||||
export ENV_TO_INI_EXPECTED=1 | ||||
stub environment-to-ini \ | ||||
"-o $GITEA_APP_INI : echo 'Stubbed environment-to-ini was called!'" | ||||
} | ||||
| ||||
function execute_test_script() { | ||||
currentEnvsBefore=$(env | sort) | ||||
source $PROJECT_ROOT/scripts/init-containers/config/config_environment.sh | ||||
local exitCode=$? | ||||
currentEnvsAfter=$(env | sort) | ||||
| ||||
# diff as unified +/- output without context before/after | ||||
diff --unified=0 <(echo "$currentEnvsBefore") <(echo "$currentEnvsAfter") | ||||
| ||||
exit $exitCode | ||||
} | ||||
| ||||
function write_mounted_file() { | ||||
# either "inlines" or "additionals" | ||||
scope="${1}" | ||||
file="${2}" | ||||
content="${3}" | ||||
| ||||
mkdir -p "$ENV_TO_INI_MOUNT_POINT/$scope/..data/" | ||||
echo "${content}" > "$ENV_TO_INI_MOUNT_POINT/$scope/..data/$file" | ||||
ln -sf "$ENV_TO_INI_MOUNT_POINT/$scope/..data/$file" "$ENV_TO_INI_MOUNT_POINT/$scope/$file" | ||||
} | ||||
| ||||
@test "works as expected when nothing is configured" { | ||||
expect_environment_to_ini_call | ||||
run $PROJECT_ROOT/scripts/init-containers/config/config_environment.sh | ||||
| ||||
assert_success | ||||
assert_line '...Initial secrets generated' | ||||
assert_line 'Reloading preset envs...' | ||||
assert_line '=== All configuration sources loaded ===' | ||||
assert_line 'Stubbed environment-to-ini was called!' | ||||
} | ||||
| ||||
@test "exports initial secrets" { | ||||
expect_environment_to_ini_call | ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
assert_line '+GITEA__OAUTH2__JWT_SECRET=mocked-jwt-secret' | ||||
assert_line '+GITEA__SECURITY__INTERNAL_TOKEN=mocked-internal-token' | ||||
assert_line '+GITEA__SECURITY__SECRET_KEY=mocked-secret-key' | ||||
assert_line '+GITEA__SERVER__LFS_JWT_SECRET=mocked-lfs-jwt-secret' | ||||
} | ||||
| ||||
@test "does NOT export initial secrets when app.ini already exists" { | ||||
expect_environment_to_ini_call | ||||
touch $GITEA_APP_INI | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
assert_line --partial 'An app.ini file already exists.' | ||||
refute_line '+GITEA__OAUTH2__JWT_SECRET=mocked-jwt-secret' | ||||
refute_line '+GITEA__SECURITY__INTERNAL_TOKEN=mocked-internal-token' | ||||
refute_line '+GITEA__SECURITY__SECRET_KEY=mocked-secret-key' | ||||
refute_line '+GITEA__SERVER__LFS_JWT_SECRET=mocked-lfs-jwt-secret' | ||||
} | ||||
| ||||
@test "ensures that preset environment variables take precedence over auto-generated ones" { | ||||
expect_environment_to_ini_call | ||||
export GITEA__OAUTH2__JWT_SECRET="pre-defined-jwt-secret" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
refute_line '+GITEA__OAUTH2__JWT_SECRET=mocked-jwt-secret' | ||||
} | ||||
| ||||
@test "ensures that preset environment variables take precedence over mounted ones" { | ||||
expect_environment_to_ini_call | ||||
export GITEA__OAUTH2__JWT_SECRET="pre-defined-jwt-secret" | ||||
write_mounted_file "inlines" "oauth2" "$(cat << EOF | ||||
JWT_SECRET=inline-jwt-secret | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
refute_line '+GITEA__OAUTH2__JWT_SECRET=mocked-jwt-secret' | ||||
refute_line '+GITEA__OAUTH2__JWT_SECRET=inline-jwt-secret' | ||||
} | ||||
| ||||
@test "ensures that additionals take precedence over inlines" { | ||||
expect_environment_to_ini_call | ||||
write_mounted_file "inlines" "oauth2" "$(cat << EOF | ||||
JWT_SECRET=inline-jwt-secret | ||||
EOF | ||||
)" | ||||
write_mounted_file "additionals" "oauth2" "$(cat << EOF | ||||
JWT_SECRET=additional-jwt-secret | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
refute_line '+GITEA__OAUTH2__JWT_SECRET=mocked-jwt-secret' | ||||
refute_line '+GITEA__OAUTH2__JWT_SECRET=inline-jwt-secret' | ||||
assert_line '+GITEA__OAUTH2__JWT_SECRET=additional-jwt-secret' | ||||
} | ||||
| ||||
@test "ensures that dotted/dashed sections are properly masked" { | ||||
expect_environment_to_ini_call | ||||
write_mounted_file "inlines" "repository.pull-request" "$(cat << EOF | ||||
WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP] | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
assert_line '+GITEA__REPOSITORY_0X2E_PULL_0X2D_REQUEST__WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]' | ||||
} | ||||
| ||||
############################################################### | ||||
##### THIS IS A BUG, BUT I WANT IT TO BE COVERED BY TESTS ##### | ||||
############################################################### | ||||
@test "ensures uppercase section and setting names (🐞)" { | ||||
expect_environment_to_ini_call | ||||
export GITEA__oauth2__JwT_Secret="pre-defined-jwt-secret" | ||||
write_mounted_file "inlines" "repository.pull-request" "$(cat << EOF | ||||
WORK_IN_progress_PREFIXES=WIP:,[WIP] | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
assert_line '+GITEA__REPOSITORY_0X2E_PULL_0X2D_REQUEST__WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]' | ||||
assert_line '+GITEA__OAUTH2__JWT_SECRET=pre-defined-jwt-secret' | ||||
} | ||||
| ||||
@test "treats top-level configuration as section-less" { | ||||
expect_environment_to_ini_call | ||||
write_mounted_file "inlines" "_generals_" "$(cat << EOF | ||||
APP_NAME=Hello top-level configuration | ||||
RUN_MODE=dev | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_success | ||||
assert_line '+GITEA____APP_NAME=Hello top-level configuration' | ||||
assert_line '+GITEA____RUN_MODE=dev' | ||||
} | ||||
| ||||
@test "fails on invalid setting" { | ||||
write_mounted_file "inlines" "_generals_" "$(cat << EOF | ||||
some random invalid string | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_failure | ||||
} | ||||
| ||||
@test "treats empty setting name as invalid setting" { | ||||
write_mounted_file "inlines" "_generals_" "$(cat << EOF | ||||
=value | ||||
EOF | ||||
)" | ||||
| ||||
run execute_test_script | ||||
| ||||
assert_failure | ||||
} | ||||
Reference in New Issue
Block a user