diff --git a/tests/entrypoint_pagination.sh b/tests/entrypoint_pagination.sh index a13ebf3..fe042cc 100644 --- a/tests/entrypoint_pagination.sh +++ b/tests/entrypoint_pagination.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -Eeuo pipefail +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" + tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' EXIT @@ -22,14 +25,20 @@ if [[ " $* " == *" -X DELETE "* ]]; then exit 0 fi +# tags 端點沒有需要清理的 tag +if [[ "$last_arg" == */tags?* ]]; then + printf '%s' '[]' + exit 0 +fi + case "$last_arg" in - *page=1) + */releases?*page=1) printf '%s' '[{"id":4,"tag_name":"v4","name":"release-4","created_at":"2024-04-01T00:00:00Z"},{"id":3,"tag_name":"v3","name":"release-3","created_at":"2024-03-01T00:00:00Z"}]' ;; - *page=2) + */releases?*page=2) printf '%s' '[{"id":2,"tag_name":"v2","name":"release-2","created_at":"2024-02-01T00:00:00Z"},{"id":1,"tag_name":"v1","name":"release-1","created_at":"2024-01-01T00:00:00Z"}]' ;; - *page=3) + */releases?*page=3) printf '%s' '[]' ;; *) @@ -40,80 +49,7 @@ esac EOF chmod +x "$bin_dir/curl" -cat >"$bin_dir/jq.py" <<'PY' -#!/usr/bin/env python3 -import json -import re -import sys - -expr = "" -flags = set() -args = sys.argv[1:] - -while args: - current = args.pop(0) - if current in {"-e", "-c", "-r", "-s"}: - flags.add(current) - continue - if current == "--": - if not args: - raise SystemExit("missing jq expression") - expr = args.pop(0) - break - if current.startswith("-"): - flags.add(current) - continue - expr = current - break - -raw = sys.stdin.read() - -def dump(value): - if "-r" in flags and isinstance(value, (str, int, float)) and not isinstance(value, bool): - sys.stdout.write(str(value)) - else: - sys.stdout.write(json.dumps(value, separators=(",", ":"))) - -if expr == "length": - data = json.loads(raw or "null") - print(len(data)) - raise SystemExit(0) - -if expr == "add": - arrays = [json.loads(line) for line in raw.splitlines() if line.strip()] - merged = [] - for item in arrays: - merged.extend(item) - dump(merged) - raise SystemExit(0) - -if expr == "sort_by(.created_at) | reverse": - data = json.loads(raw or "[]") - dump(sorted(data, key=lambda item: item["created_at"], reverse=True)) - raise SystemExit(0) - -match = re.fullmatch(r"\.\[(\d+):\]", expr) -if match: - data = json.loads(raw or "[]") - dump(data[int(match.group(1)):]) - raise SystemExit(0) - -if expr == ".[]": - data = json.loads(raw or "[]") - for item in data: - dump(item) - sys.stdout.write("\n") - raise SystemExit(0) - -match = re.fullmatch(r"\.(id|tag_name|name)", expr) -if match: - data = json.loads(raw or "null") - value = data.get(match.group(1)) - dump(value) - raise SystemExit(0) - -raise SystemExit(f"unsupported jq expression: {expr}") -PY +cp "$script_dir/lib/jq_mock.py" "$bin_dir/jq.py" chmod +x "$bin_dir/jq.py" cat >"$bin_dir/jq" <<'EOF' @@ -129,7 +65,7 @@ GITEA_SERVER_URL="https://gitea.example.test" \ GITEA_REPOSITORY="owner/repo" \ KEEP_COUNT="2" \ RUNNER_TOKEN="" \ -bash ./entrypoint.sh >/dev/null +bash "$repo_root/entrypoint.sh" >/dev/null grep -q 'page=1' "$curl_log" grep -q 'page=2' "$curl_log" @@ -138,6 +74,11 @@ grep -q '/releases/2' "$curl_log" grep -q '/releases/1' "$curl_log" if grep -q '/releases/3' "$curl_log" || grep -q '/releases/4' "$curl_log"; then - printf 'unexpected deletion request in %s\n' "$curl_log" >&2 + printf 'unexpected release deletion request in %s\n' "$curl_log" >&2 + exit 1 +fi + +if grep -q '/tags/' "$curl_log"; then + printf 'unexpected tag deletion request in %s\n' "$curl_log" >&2 exit 1 fi diff --git a/tests/entrypoint_tag_cleanup.sh b/tests/entrypoint_tag_cleanup.sh new file mode 100644 index 0000000..3d3ad68 --- /dev/null +++ b/tests/entrypoint_tag_cleanup.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" + +tmpdir="$(mktemp -d)" +trap 'rm -rf "$tmpdir"' EXIT + +curl_log="$tmpdir/curl.log" +bin_dir="$tmpdir/bin" +mkdir -p "$bin_dir" + +cat >"$bin_dir/curl" <<'EOF' +#!/usr/bin/env bash +set -Eeuo pipefail + +log_file="${CURL_LOG:?}" +printf '%s\n' "$*" >>"$log_file" + +last_arg="${!#}" + +if [[ " $* " == *" -X DELETE "* ]]; then + printf '204' + exit 0 +fi + +# 倉庫所有 tag:v1~v4 加上兩個未指定 release 的孤兒 tag +if [[ "$last_arg" == */tags?* ]]; then + case "$last_arg" in + *page=1) + printf '%s' '[{"name":"v4"},{"name":"v3"},{"name":"v2"},{"name":"v1"},{"name":"tmp-build"},{"name":"v0"}]' + ;; + *) + printf '%s' '[]' + ;; + esac + exit 0 +fi + +# /releases GET:刪除舊版本後重新取得時,只回傳仍保留的 release +if grep -Eq -- '-X DELETE .*/releases/[0-9]' "$log_file"; then + case "$last_arg" in + *page=1) + printf '%s' '[{"id":4,"tag_name":"v4","name":"release-4","created_at":"2024-04-01T00:00:00Z"},{"id":3,"tag_name":"v3","name":"release-3","created_at":"2024-03-01T00:00:00Z"}]' + ;; + *) + printf '%s' '[]' + ;; + esac + exit 0 +fi + +case "$last_arg" in + *page=1) + printf '%s' '[{"id":4,"tag_name":"v4","name":"release-4","created_at":"2024-04-01T00:00:00Z"},{"id":3,"tag_name":"v3","name":"release-3","created_at":"2024-03-01T00:00:00Z"}]' + ;; + *page=2) + printf '%s' '[{"id":2,"tag_name":"v2","name":"release-2","created_at":"2024-02-01T00:00:00Z"},{"id":1,"tag_name":"v1","name":"release-1","created_at":"2024-01-01T00:00:00Z"}]' + ;; + *) + printf '%s' '[]' + ;; +esac +EOF +chmod +x "$bin_dir/curl" + +cp "$script_dir/lib/jq_mock.py" "$bin_dir/jq.py" +chmod +x "$bin_dir/jq.py" + +cat >"$bin_dir/jq" <<'EOF' +#!/usr/bin/env bash +set -Eeuo pipefail +exec python3 "$0.py" "$@" +EOF +chmod +x "$bin_dir/jq" + +PATH="$bin_dir:$PATH" \ +CURL_LOG="$curl_log" \ +GITEA_SERVER_URL="https://gitea.example.test" \ +GITEA_REPOSITORY="owner/repo" \ +KEEP_COUNT="2" \ +RUNNER_TOKEN="" \ +bash "$repo_root/entrypoint.sh" >/dev/null + +# 舊版本 release 仍被刪除 +grep -q '/releases/2' "$curl_log" +grep -q '/releases/1' "$curl_log" + +# 未指定 release 的 tag(含被刪除 release 留下的 tag 與其他孤兒 tag)被刪除 +for tag in v2 v1 tmp-build v0; do + if ! grep -q "/tags/$tag" "$curl_log"; then + printf 'expected tag deletion /tags/%s not found in %s\n' "$tag" "$curl_log" >&2 + exit 1 + fi +done + +# 仍指定 release 的 tag 不可被刪除 +for tag in v4 v3; do + if grep -q "/tags/$tag" "$curl_log"; then + printf 'unexpected deletion of release-assigned tag /tags/%s in %s\n' "$tag" "$curl_log" >&2 + exit 1 + fi +done diff --git a/tests/lib/jq_mock.py b/tests/lib/jq_mock.py new file mode 100644 index 0000000..0f05284 --- /dev/null +++ b/tests/lib/jq_mock.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Minimal jq stand-in for entrypoint.sh tests. + +Only the jq expressions used by entrypoint.sh are implemented. Unknown +expressions fail loudly so new jq usage is noticed instead of silently +producing wrong results. +""" +import json +import re +import sys + +expr = "" +flags = set() +named_args = {} +args = sys.argv[1:] + +while args: + current = args.pop(0) + if current in {"-e", "-c", "-r", "-s"}: + flags.add(current) + continue + if current == "--arg": + key = args.pop(0) + value = args.pop(0) + named_args[key] = value + continue + if current == "--": + if not args: + raise SystemExit("missing jq expression") + expr = args.pop(0) + break + if current.startswith("-"): + flags.add(current) + continue + expr = current + break + +raw = sys.stdin.read() + + +def dump(value): + if "-r" in flags and isinstance(value, (str, int, float)) and not isinstance(value, bool): + sys.stdout.write(str(value)) + else: + sys.stdout.write(json.dumps(value, separators=(",", ":"))) + + +if expr == "length": + data = json.loads(raw or "null") + print(len(data)) + raise SystemExit(0) + +if expr == "add": + arrays = [json.loads(line) for line in raw.splitlines() if line.strip()] + merged = [] + for item in arrays: + merged.extend(item) + dump(merged) + raise SystemExit(0) + +if expr == "sort_by(.created_at) | reverse": + data = json.loads(raw or "[]") + dump(sorted(data, key=lambda item: item["created_at"], reverse=True)) + raise SystemExit(0) + +if expr == "[.[].tag_name]": + data = json.loads(raw or "[]") + dump([item["tag_name"] for item in data]) + raise SystemExit(0) + +match = re.fullmatch(r"any\(\.\[\]; \. == \$(\w+)\)", expr) +if match: + data = json.loads(raw or "[]") + needle = named_args[match.group(1)] + sys.stdout.write("true" if any(item == needle for item in data) else "false") + raise SystemExit(0) + +match = re.fullmatch(r"\.\[(\d+):\]", expr) +if match: + data = json.loads(raw or "[]") + dump(data[int(match.group(1)):]) + raise SystemExit(0) + +if expr == ".[]": + data = json.loads(raw or "[]") + for item in data: + dump(item) + sys.stdout.write("\n") + raise SystemExit(0) + +match = re.fullmatch(r"\.(id|tag_name|name)", expr) +if match: + data = json.loads(raw or "null") + value = data.get(match.group(1)) + dump(value) + raise SystemExit(0) + +raise SystemExit(f"unsupported jq expression: {expr}")