|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Regression tests for the release verifiers. |
| 3 | +# |
| 4 | +# Generated data only — no network, no tracked fixtures, no assertions about |
| 5 | +# workflow YAML layout. Every negative case asserts the reason for the |
| 6 | +# rejection, so a verifier that breaks in an unrelated way (bad arity, missing |
| 7 | +# jq, a typo in a guard) cannot pass by merely exiting nonzero. |
| 8 | + |
| 9 | +set -euo pipefail |
| 10 | + |
| 11 | +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" |
| 12 | +verify_run="$repo_root/scripts/ci/verify_prepare_run.sh" |
| 13 | +verify_artifacts="$repo_root/scripts/ci/verify_release_artifacts.sh" |
| 14 | + |
| 15 | +work_dir="$(mktemp -d)" |
| 16 | +trap 'rm -rf "$work_dir"' EXIT |
| 17 | + |
| 18 | +version="0.2.0-beta.1" |
| 19 | +commit="0123456789abcdef0123456789abcdef01234567" |
| 20 | +other_commit="ffffffffffffffffffffffffffffffffffffffff" |
| 21 | +artifacts="$work_dir/artifacts" |
| 22 | +log="$work_dir/verifier.log" |
| 23 | +failures=0 |
| 24 | + |
| 25 | +report_log() { |
| 26 | + sed 's/^/ /' "$log" >&2 |
| 27 | +} |
| 28 | + |
| 29 | +expect_ok() { |
| 30 | + local label="$1" |
| 31 | + shift |
| 32 | + if "$@" >"$log" 2>&1; then |
| 33 | + printf 'ok %s\n' "$label" |
| 34 | + else |
| 35 | + printf 'FAIL %s: expected success\n' "$label" >&2 |
| 36 | + report_log |
| 37 | + failures=$((failures + 1)) |
| 38 | + fi |
| 39 | +} |
| 40 | + |
| 41 | +expect_failure() { |
| 42 | + local label="$1" want="$2" |
| 43 | + shift 2 |
| 44 | + if "$@" >"$log" 2>&1; then |
| 45 | + printf 'FAIL %s: expected failure, got success\n' "$label" >&2 |
| 46 | + failures=$((failures + 1)) |
| 47 | + elif grep -qF -- "$want" "$log"; then |
| 48 | + printf 'ok %s\n' "$label" |
| 49 | + else |
| 50 | + printf 'FAIL %s: expected message containing: %s\n' "$label" "$want" >&2 |
| 51 | + report_log |
| 52 | + failures=$((failures + 1)) |
| 53 | + fi |
| 54 | +} |
| 55 | + |
| 56 | +# A successful prepare run for $commit, optionally mutated by a jq filter. |
| 57 | +write_run() { |
| 58 | + local out="$1" filter="${2:-.}" |
| 59 | + jq -n --arg head_sha "$commit" \ |
| 60 | + '{head_sha: $head_sha, |
| 61 | + path: ".github/workflows/release-prepare.yml", |
| 62 | + event: "push", |
| 63 | + status: "completed", |
| 64 | + conclusion: "success"}' | |
| 65 | + jq "$filter" >"$out" |
| 66 | +} |
| 67 | + |
| 68 | +seed_artifacts() { |
| 69 | + rm -rf "$artifacts" |
| 70 | + mkdir -p "$artifacts" |
| 71 | + local label |
| 72 | + for label in linux-x64 linux-arm64 macos-arm64 macos-x64; do |
| 73 | + : >"$artifacts/pagedb-fsck-${version}-${label}.tar.gz" |
| 74 | + done |
| 75 | + : >"$artifacts/pagedb-fsck-${version}-windows-x64.zip" |
| 76 | +} |
| 77 | + |
| 78 | +# ── prepare-run provenance ────────────────────────────────────────────────── |
| 79 | + |
| 80 | +run_json="$work_dir/run.json" |
| 81 | +write_run "$run_json" |
| 82 | + |
| 83 | +expect_ok "prepare run: successful tag build" \ |
| 84 | + bash "$verify_run" "$run_json" "$commit" |
| 85 | + |
| 86 | +expect_failure "prepare run: different tag commit" "run head_sha is" \ |
| 87 | + bash "$verify_run" "$run_json" "$other_commit" |
| 88 | + |
| 89 | +write_run "$work_dir/failed.json" '.conclusion = "failure"' |
| 90 | +expect_failure "prepare run: unsuccessful conclusion" "run conclusion is 'failure'" \ |
| 91 | + bash "$verify_run" "$work_dir/failed.json" "$commit" |
| 92 | + |
| 93 | +write_run "$work_dir/in-progress.json" '.status = "in_progress" | .conclusion = null' |
| 94 | +expect_failure "prepare run: still running" "run status is 'in_progress'" \ |
| 95 | + bash "$verify_run" "$work_dir/in-progress.json" "$commit" |
| 96 | + |
| 97 | +write_run "$work_dir/other-workflow.json" '.path = ".github/workflows/test.yml"' |
| 98 | +expect_failure "prepare run: different workflow" "run path is" \ |
| 99 | + bash "$verify_run" "$work_dir/other-workflow.json" "$commit" |
| 100 | + |
| 101 | +write_run "$work_dir/dispatched.json" '.event = "workflow_dispatch"' |
| 102 | +expect_failure "prepare run: not a tag push" "run event is 'workflow_dispatch'" \ |
| 103 | + bash "$verify_run" "$work_dir/dispatched.json" "$commit" |
| 104 | + |
| 105 | +write_run "$work_dir/truncated.json" 'del(.conclusion)' |
| 106 | +expect_failure "prepare run: absent field" "run conclusion is '<absent>'" \ |
| 107 | + bash "$verify_run" "$work_dir/truncated.json" "$commit" |
| 108 | + |
| 109 | +printf 'not json' >"$work_dir/garbage.json" |
| 110 | +expect_failure "prepare run: malformed metadata" "not valid JSON" \ |
| 111 | + bash "$verify_run" "$work_dir/garbage.json" "$commit" |
| 112 | + |
| 113 | +expect_failure "prepare run: absent metadata file" "missing workflow-run metadata" \ |
| 114 | + bash "$verify_run" "$work_dir/nonexistent.json" "$commit" |
| 115 | + |
| 116 | +expect_failure "prepare run: malformed commit" "invalid release commit" \ |
| 117 | + bash "$verify_run" "$run_json" "HEAD" |
| 118 | + |
| 119 | +# ── release attachment set ────────────────────────────────────────────────── |
| 120 | + |
| 121 | +seed_artifacts |
| 122 | +expect_ok "artifacts: exact release set" \ |
| 123 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 124 | + |
| 125 | +seed_artifacts |
| 126 | +: >"$artifacts/unexpected.txt" |
| 127 | +expect_failure "artifacts: extra attachment" "unexpected release attachment: unexpected.txt" \ |
| 128 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 129 | + |
| 130 | +seed_artifacts |
| 131 | +: >"$artifacts/.hidden" |
| 132 | +expect_failure "artifacts: hidden attachment" "unexpected release attachment: .hidden" \ |
| 133 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 134 | + |
| 135 | +# Renamed rather than deleted: the attachment count stays at five, so this |
| 136 | +# reaches the by-name check instead of tripping a coarser cardinality guard. |
| 137 | +seed_artifacts |
| 138 | +mv "$artifacts/pagedb-fsck-${version}-windows-x64.zip" "$artifacts/pagedb-fsck-${version}-windows-x86.zip" |
| 139 | +expect_failure "artifacts: missing archive at full count" \ |
| 140 | + "missing release archive: pagedb-fsck-${version}-windows-x64.zip" \ |
| 141 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 142 | + |
| 143 | +seed_artifacts |
| 144 | +rm "$artifacts/pagedb-fsck-${version}-macos-x64.tar.gz" |
| 145 | +expect_failure "artifacts: absent archive" \ |
| 146 | + "missing release archive: pagedb-fsck-${version}-macos-x64.tar.gz" \ |
| 147 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 148 | + |
| 149 | +seed_artifacts |
| 150 | +rm "$artifacts/pagedb-fsck-${version}-linux-x64.tar.gz" |
| 151 | +ln -s /etc/hostname "$artifacts/pagedb-fsck-${version}-linux-x64.tar.gz" |
| 152 | +expect_failure "artifacts: symlinked archive" "is not a regular file" \ |
| 153 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 154 | + |
| 155 | +seed_artifacts |
| 156 | +rm "$artifacts/pagedb-fsck-${version}-linux-arm64.tar.gz" |
| 157 | +mkdir "$artifacts/pagedb-fsck-${version}-linux-arm64.tar.gz" |
| 158 | +expect_failure "artifacts: directory in place of archive" "is not a regular file" \ |
| 159 | + bash "$verify_artifacts" "$artifacts" "$version" |
| 160 | + |
| 161 | +seed_artifacts |
| 162 | +expect_failure "artifacts: version mismatch" "missing release archive" \ |
| 163 | + bash "$verify_artifacts" "$artifacts" "9.9.9" |
| 164 | + |
| 165 | +expect_failure "artifacts: malformed version" "invalid release version" \ |
| 166 | + bash "$verify_artifacts" "$artifacts" "not-a-version" |
| 167 | + |
| 168 | +expect_failure "artifacts: absent directory" "missing artifact directory" \ |
| 169 | + bash "$verify_artifacts" "$work_dir/nonexistent" "$version" |
| 170 | + |
| 171 | +# ──────────────────────────────────────────────────────────────────────────── |
| 172 | + |
| 173 | +if test "$failures" -ne 0; then |
| 174 | + printf 'release artifact verification: %d test(s) failed\n' "$failures" >&2 |
| 175 | + exit 1 |
| 176 | +fi |
| 177 | + |
| 178 | +printf 'release artifact verification: tests passed\n' |
0 commit comments