From 7b99392dd7cdf9655bd21ceb5f2ee1ddcc045b22 Mon Sep 17 00:00:00 2001 From: moilanik Date: Sat, 13 Jun 2026 19:39:01 +0300 Subject: [PATCH] katetaan puuttuva testit --- .gitea/scripts/bats-coverage.sh | 12 ++- .gitea/workflows/build-feature.yml | 4 +- .gitea/workflows/ci.yml | 2 +- .../{feature-env.conf => gitea-env.conf} | 0 docs/adr/0006-directory-ownership.md | 4 +- docs/ci-pipeline-practices.md | 4 +- scripts/ci-validate.sh | 2 +- tests/ci-validate.bats | 79 +++++++++++++++ tests/dispatch-workflow.bats | 11 +++ tests/helpers/mock-api.sh | 4 +- tests/helpers/mock-server.py | 10 +- tests/publish-git-pages.bats | 99 +++++++++++++++++++ tests/publish.bats | 52 ++++++++++ 13 files changed, 270 insertions(+), 13 deletions(-) rename .gitea/workflows/{feature-env.conf => gitea-env.conf} (100%) create mode 100644 tests/ci-validate.bats create mode 100644 tests/publish-git-pages.bats create mode 100644 tests/publish.bats diff --git a/.gitea/scripts/bats-coverage.sh b/.gitea/scripts/bats-coverage.sh index 992d1c0..5334ae9 100755 --- a/.gitea/scripts/bats-coverage.sh +++ b/.gitea/scripts/bats-coverage.sh @@ -8,9 +8,17 @@ REPORT_DIR="${2:-}" [ -n "$REPORT_DIR" ] || { echo "ERROR: report directory required" >&2; exit 1; } HAS_COVERAGE=false -if docker run --rm -v "$WORKSPACE_VOLUME":/data alpine sh -c '[ -d /data/coverage ] && ls -A /data/coverage | grep -q .' 2>/dev/null; then +COVERAGE_SRC="" +for candidate in /data/scripts/coverage /data/coverage; do + if docker run --rm -v "$WORKSPACE_VOLUME":/data alpine sh -c "[ -d \"$candidate\" ] && ls -A \"$candidate\" | grep -q ." 2>/dev/null; then + COVERAGE_SRC="$candidate" + break + fi +done + +if [ -n "$COVERAGE_SRC" ]; then mkdir -p "$REPORT_DIR/coverage" - docker run --rm -v "$WORKSPACE_VOLUME":/data alpine tar c -C /data/coverage . | tar x -C "$REPORT_DIR/coverage" + docker run --rm -v "$WORKSPACE_VOLUME":/data alpine tar c -C "$COVERAGE_SRC" . | tar x -C "$REPORT_DIR/coverage" HAS_COVERAGE=true fi diff --git a/.gitea/workflows/build-feature.yml b/.gitea/workflows/build-feature.yml index 909cec9..236b17e 100644 --- a/.gitea/workflows/build-feature.yml +++ b/.gitea/workflows/build-feature.yml @@ -58,8 +58,8 @@ jobs: -v bats-workspace:/data \ --entrypoint bash ${{ inputs.bats-image }} \ -c 'apk add -q lsof python3 jq curl ruby && cd /data && \ - gem install bashcov 2>&1 | tail -1 && \ - bashcov --include scripts/ -- bats tests/' \ + gem install bashcov -v 3.3.0 2>&1 | tail -1 && \ + bashcov --root /data/scripts/ -- bats tests/' \ > "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1 BATS_EXIT=$? bash .ci/.gitea/scripts/bats-coverage.sh bats-workspace "reports/${GITHUB_SHA:0:8}/bats" diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e551a05..1c56c19 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: load-config: uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@feature/pipeline-cleanup with: - config_path: .gitea/workflows/feature-env.conf + config_path: .gitea/workflows/gitea-env.conf feature: if: github.ref != 'refs/heads/main' diff --git a/.gitea/workflows/feature-env.conf b/.gitea/workflows/gitea-env.conf similarity index 100% rename from .gitea/workflows/feature-env.conf rename to .gitea/workflows/gitea-env.conf diff --git a/docs/adr/0006-directory-ownership.md b/docs/adr/0006-directory-ownership.md index e228388..c755542 100644 --- a/docs/adr/0006-directory-ownership.md +++ b/docs/adr/0006-directory-ownership.md @@ -7,7 +7,7 @@ Provider-repossa (`gitea-ci-library`) kansioiden omistajuus on seuraava: | Kansio / Tiedosto | Omistaja | Tyyppi | |-------------------|----------|--------| | `.gitea/workflows/` | Sekoitettu | Providerin reusable workflowt + consumerin pipeline | -| `.gitea/workflows/feature-env.conf` | Consumer | KEY=VALUE config | +| `.gitea/workflows/gitea-env.conf` | Consumer | KEY=VALUE config | | `.gitea/scripts/` | Consumer | Consumer-skriptit | | `scripts/` | Provider | Providerin sisäiset työkalut | @@ -52,7 +52,7 @@ Consumerin omat skriptit, osana consumerin pipeline-logiikkaa. Kutsutaan consumerin workflowista ilman tupla checkouttia: `.gitea/scripts/bats-report.sh`. -## Consumerin `.gitea/workflows/feature-env.conf` +## Consumerin `.gitea/workflows/gitea-env.conf` Consumerin konfiguraatiotiedosto. Providerin `config-provider.yml` lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön. diff --git a/docs/ci-pipeline-practices.md b/docs/ci-pipeline-practices.md index da710f0..7ab8230 100644 --- a/docs/ci-pipeline-practices.md +++ b/docs/ci-pipeline-practices.md @@ -64,7 +64,7 @@ mekanismin ketjuttamista: Ketju toimii näin: ``` -feature-env.conf → config-provider.yml → env_json (yksi JSON-string) +gitea-env.conf → config-provider.yml → env_json (yksi JSON-string) (1) (2) ↓ ci.yml with: env_json @@ -80,7 +80,7 @@ feature-env.conf → config-provider.yml → env_json (yksi JSON-string) ``` Vaiheet: -1. Consumer määrittelee arvot `feature-env.conf`:ssä (KEY=VALUE) +1. Consumer määrittelee arvot `gitea-env.conf`:ssä (KEY=VALUE) 2. `config-provider.yml` lukee confin ja tuottaa yhden JSON-stringin outputina 3. `ci.yml` välittää JSONin `needs` + `with:` -ketjulla 4. `build-feature.yml` purkaa arvot workflow `env:`-tasolle `fromJson()`:lla diff --git a/scripts/ci-validate.sh b/scripts/ci-validate.sh index cf5cb06..f79cbe0 100644 --- a/scripts/ci-validate.sh +++ b/scripts/ci-validate.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -CONF_FILE=".gitea/workflows/feature-env.conf" +CONF_FILE="${CI_CONF_FILE:-.gitea/workflows/gitea-env.conf}" ERRORS=0 [ -f "$CONF_FILE" ] || { echo "ERROR: $CONF_FILE not found — checkout missing?" >&2; exit 1; } diff --git a/tests/ci-validate.bats b/tests/ci-validate.bats new file mode 100644 index 0000000..46e8f9c --- /dev/null +++ b/tests/ci-validate.bats @@ -0,0 +1,79 @@ +#!/usr/bin/env bats + +setup() { + export CONF_FILE=$(mktemp) + export CI_CONF_FILE="$CONF_FILE" +} + +teardown() { + rm -f "$CONF_FILE" +} + +@test "missing config file → exit 1" { + export CI_CONF_FILE="/nonexistent/path/$(date +%s).conf" + run bash scripts/ci-validate.sh + [ "$status" -eq 1 ] + [[ "$output" == *"ERROR"* ]] +} + +@test "empty value in config → exit 1" { + cat > "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" < "$CONF_FILE" </dev/null) || true [ -n "$pids" ] && kill -9 $pids 2>/dev/null || true - sleep 0.3 + sleep 0.5 } _wait_port_free() { local i=0 - while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 30 ]; do + while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 50 ]; do sleep 0.1 i=$((i + 1)) done diff --git a/tests/helpers/mock-server.py b/tests/helpers/mock-server.py index 2b4cbb1..57249d6 100644 --- a/tests/helpers/mock-server.py +++ b/tests/helpers/mock-server.py @@ -46,7 +46,7 @@ class H(http.server.BaseHTTPRequestHandler): def _log_request(self, method): path = self.path content_len = int(self.headers.get('Content-Length', 0)) - body = self.rfile.read(content_len).decode() if content_len else '' + body = self.rfile.read(content_len).decode(errors='replace') if content_len else '' line = f'{method} {path}\n{body}\n' with open(REQ_FILE, 'a') as f: f.write(line) @@ -67,6 +67,14 @@ class H(http.server.BaseHTTPRequestHandler): self.end_headers() self.wfile.write(body.encode()) + def do_PATCH(self): + self._log_request('PATCH') + code, body = self._get_response() + self.send_response(code) + self.send_header('Content-Type', 'application/json') + self.end_headers() + self.wfile.write(body.encode()) + def log_message(self, format, *args): pass diff --git a/tests/publish-git-pages.bats b/tests/publish-git-pages.bats new file mode 100644 index 0000000..b99f8f5 --- /dev/null +++ b/tests/publish-git-pages.bats @@ -0,0 +1,99 @@ +#!/usr/bin/env bats + +setup() { + source tests/helpers/mock-api.sh + export GITEA_API_URL="http://localhost:18080" + export PAGES_HOST="reports.example.com" + export GIT_PAGES_PUBLISH_URL="http://localhost:18080/" + export GIT_PAGES_PUBLISH_TOKEN="publish-token-abc" + export GITHUB_REPOSITORY="test-owner/test-repo" + export GITHUB_SHA="abc123def456789012345678901234567890abcd" + + REPORT_DIR="reports/abc123de/unit-tests" + mkdir -p "$REPORT_DIR" + echo "test" > "$REPORT_DIR/index.html" +} + +teardown() { + mock_stop + rm -rf "reports/abc123de" +} + +@test "missing suite_path argument → exit 1" { + run bash scripts/publish-git-pages.sh "" + [ "$status" -eq 1 ] + [[ "$output" == *"ERROR"* ]] +} + +@test "missing GITEA_API_URL → exit 1" { + unset GITEA_API_URL + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GITEA_API_URL"* ]] +} + +@test "missing PAGES_HOST → exit 1" { + unset PAGES_HOST + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"PAGES_HOST"* ]] +} + +@test "missing GIT_PAGES_PUBLISH_URL → exit 1" { + unset GIT_PAGES_PUBLISH_URL + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GIT_PAGES_PUBLISH_URL"* ]] +} + +@test "missing GIT_PAGES_PUBLISH_TOKEN → exit 1" { + unset GIT_PAGES_PUBLISH_TOKEN + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]] +} + +@test "missing GITHUB_REPOSITORY → exit 1" { + unset GITHUB_REPOSITORY + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GITHUB_REPOSITORY"* ]] +} + +@test "suite path is not a directory → exit 1" { + run bash scripts/publish-git-pages.sh "nonexistent" + [ "$status" -eq 1 ] + [[ "$output" == *"not a directory"* ]] +} + +@test "valid publish returns report base URL" { + mock_set_sequence '[ + {"code":200,"body":"published"} + ]' + mock_start + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 0 ] + [[ "$output" == "https://reports.example.com/test-owner/test-repo/reports/abc123de" ]] +} + +@test "publish with suite subpath" { + mkdir -p "reports/abc123de/sub/suite" + echo "sub" > "reports/abc123de/sub/suite/result.html" + mock_set_sequence '[ + {"code":200,"body":"published"} + ]' + mock_start + run bash scripts/publish-git-pages.sh "sub/suite" + [ "$status" -eq 0 ] + [[ "$output" == "https://reports.example.com/test-owner/test-repo/reports/abc123de" ]] +} + +@test "git-pages returns HTTP 500 → exit 1" { + mock_set_sequence '[ + {"code":500,"body":"internal error"} + ]' + mock_start + run bash scripts/publish-git-pages.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"500"* ]] +} diff --git a/tests/publish.bats b/tests/publish.bats new file mode 100644 index 0000000..f725e7a --- /dev/null +++ b/tests/publish.bats @@ -0,0 +1,52 @@ +#!/usr/bin/env bats + +setup() { + export GITEA_API_URL="http://localhost:18080" + export GITEA_TOKEN="test-token-abc" + export PAGES_HOST="reports.example.com" + export GIT_PAGES_PUBLISH_URL="http://localhost:18080/" + export GIT_PAGES_PUBLISH_TOKEN="publish-token-abc" + export GITHUB_REPOSITORY="test-owner/test-repo" + export GITHUB_SHA="abc123def456789012345678901234567890abcd" +} + +@test "missing suite_path argument → exit 1" { + run bash scripts/publish.sh "" + [ "$status" -eq 1 ] + [[ "$output" == *"ERROR"* ]] +} + +@test "missing GITEA_API_URL → exit 1" { + unset GITEA_API_URL + run bash scripts/publish.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GITEA_API_URL"* ]] +} + +@test "missing GITEA_TOKEN → exit 1" { + unset GITEA_TOKEN + run bash scripts/publish.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GITEA_TOKEN"* ]] +} + +@test "missing PAGES_HOST → exit 1" { + unset PAGES_HOST + run bash scripts/publish.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"PAGES_HOST"* ]] +} + +@test "missing GIT_PAGES_PUBLISH_URL → exit 1" { + unset GIT_PAGES_PUBLISH_URL + run bash scripts/publish.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GIT_PAGES_PUBLISH_URL"* ]] +} + +@test "missing GIT_PAGES_PUBLISH_TOKEN → exit 1" { + unset GIT_PAGES_PUBLISH_TOKEN + run bash scripts/publish.sh "unit-tests" + [ "$status" -eq 1 ] + [[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]] +}