diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md index 686d550..3f1a225 100644 --- a/.gitea/PULL_REQUEST_TEMPLATE.md +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -23,7 +23,7 @@ ### Applicable issues - - 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 diff --git a/.gitea/workflows/test-pr.yml b/.gitea/workflows/test-pr.yml index eef9eb4..fa9be5b 100644 --- a/.gitea/workflows/test-pr.yml +++ b/.gitea/workflows/test-pr.yml @@ -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: | diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4f2bd05 --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/.helmignore b/.helmignore index c0341ca..152e97a 100644 --- a/.helmignore +++ b/.helmignore @@ -5,6 +5,7 @@ # Common VCS dirs .git/ .gitignore +.gitmodules .bzr/ .bzrignore .hg/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a216a96..9ae1a2e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ "yzhang.markdown-all-in-one", "DavidAnson.vscode-markdownlint", "Tim-Koehler.helm-intellisense", - "esbenp.prettier-vscode" + "esbenp.prettier-vscode", + "jetmartin.bats" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 9a5337b..edfd3fc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,9 @@ ] }, "yaml.schemaStore.enable": true, + "[bats]": { + "editor.tabSize": 2 + }, "[shellscript]": { "files.eol": "\n", "editor.tabSize": 2 diff --git a/.yamllint b/.yamllint index 90128be..51306d2 100644 --- a/.yamllint +++ b/.yamllint @@ -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 \ No newline at end of file + max-spaces-inside: 2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 075cae0..94f45b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/Makefile b/Makefile index bca7ad7..3fc00c2 100644 --- a/Makefile +++ b/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 diff --git a/renovate.json5 b/renovate.json5 index 7605fa7..c27d373 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -10,6 +10,9 @@ 'kind/dependency', ], automergeStrategy: 'squash', + 'git-submodules': { + 'enabled': true + }, customManagers: [ { description: 'Gitea-version of https://docs.renovatebot.com/presets-regexManagers/#regexmanagersgithubactionsversions', diff --git a/scripts/act_runner/token.sh b/scripts/act_runner/token.sh old mode 100644 new mode 100755 diff --git a/scripts/init-containers/config/config_environment.sh b/scripts/init-containers/config/config_environment.sh old mode 100644 new mode 100755 diff --git a/scripts/init-containers/init/configure_gpg_environment.sh b/scripts/init-containers/init/configure_gpg_environment.sh old mode 100644 new mode 100755 diff --git a/unittests/bash/bats b/unittests/bash/bats new file mode 160000 index 0000000..b640ec3 --- /dev/null +++ b/unittests/bash/bats @@ -0,0 +1 @@ +Subproject commit b640ec3cf2c7c9cfc9e6351479261186f76eeec8 diff --git a/unittests/bash/test_helper/bats-assert b/unittests/bash/test_helper/bats-assert new file mode 160000 index 0000000..e2d855b --- /dev/null +++ b/unittests/bash/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit e2d855bc78619ee15b0c702b5c30fb074101159f diff --git a/unittests/bash/test_helper/bats-mock b/unittests/bash/test_helper/bats-mock new file mode 160000 index 0000000..93e0128 --- /dev/null +++ b/unittests/bash/test_helper/bats-mock @@ -0,0 +1 @@ +Subproject commit 93e0128b8787db05a2632c70501cd667dd35d253 diff --git a/unittests/bash/test_helper/bats-support b/unittests/bash/test_helper/bats-support new file mode 160000 index 0000000..9bf10e8 --- /dev/null +++ b/unittests/bash/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit 9bf10e876dd6b624fe44423f0b35e064225f7556 diff --git a/unittests/bash/test_helper/common-setup.bash b/unittests/bash/test_helper/common-setup.bash new file mode 100644 index 0000000..187abf5 --- /dev/null +++ b/unittests/bash/test_helper/common-setup.bash @@ -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" +} diff --git a/unittests/bash/tests/init-containers/config/config_environment.bats b/unittests/bash/tests/init-containers/config/config_environment.bats new file mode 100644 index 0000000..62364a6 --- /dev/null +++ b/unittests/bash/tests/init-containers/config/config_environment.bats @@ -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 +}