#!/bin/bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TMP_DIR="$(mktemp -d)" trap 'rm -rf "${TMP_DIR}"' EXIT assert_eq() { local expected="$1" local actual="$2" if [[ "${expected}" != "${actual}" ]]; then printf 'Expected:\n%s\nActual:\n%s\n' "${expected}" "${actual}" >&2 exit 1 fi } expect_fail() { if ( "$@" ) >/dev/null 2>&1; then printf 'Expected failure: %s\n' "$*" >&2 exit 1 fi } write_mock_curl_pagination() { cat > "${TMP_DIR}/curl" <<'EOF' #!/bin/sh make_versions_json() { prefix="$1" start="$2" count="$3" i=0 printf '[' while [ "$i" -lt "$count" ]; do idx=$((start + i)) [ "$i" -gt 0 ] && printf ',' printf '{"name":"%s","version":"%s.%s.0","created_at":"2023-01-%02dT00:00:00Z"}' \ "$prefix" "$idx" "$idx" "$((idx + 1))" i=$((i + 1)) done printf ']' } method=GET out_file= headers_file= url= while [ "$#" -gt 0 ]; do case "$1" in -X) method=$2; shift 2 ;; -D) headers_file=$2; shift 2 ;; -o) out_file=$2; shift 2 ;; -w) shift 2 ;; -H|-sS) shift 1 ;; *) url=$1; shift ;; esac done body='[]' status='HTTP/1.1 200 OK' case "$url" in *'/api/v1/packages/test-owner/nuget/pkg-a?page=1&limit=100') body="$(make_versions_json pkg-a 0 100)" ;; *'/api/v1/packages/test-owner/nuget/pkg-a?page=2&limit=100') body="$(make_versions_json pkg-a 100 1)" ;; esac printf '%s' "$body" > "$out_file" { printf '%s\n' "$status" printf 'X-Request-Id: req-page\n' } > "$headers_file" printf '%s' 200 EOF chmod +x "${TMP_DIR}/curl" } write_mock_curl_collect() { cat > "${TMP_DIR}/curl" <<'EOF' #!/bin/sh method=GET out_file= headers_file= url= while [ "$#" -gt 0 ]; do case "$1" in -X) method=$2; shift 2 ;; -D) headers_file=$2; shift 2 ;; -o) out_file=$2; shift 2 ;; -w) shift 2 ;; -H|-sS) shift 1 ;; *) url=$1; shift ;; esac done body='[]' status='HTTP/1.1 200 OK' code='200' case "$url" in *'/api/v1/packages/test-owner/nuget/pkg-a?page=1&limit=100') body='[{"name":"pkg-a","version":"1.0.0","created_at":"2023-01-01T00:00:00Z"},{"name":"pkg-a","version":"1.1.0","created_at":"2023-01-02T00:00:00Z"},{"name":"pkg-a","version":"1.2.0","created_at":"2023-01-03T00:00:00Z"}]' ;; *'/api/v1/packages/test-owner/nuget/pkg-b?page=1&limit=100') code='404' status='HTTP/1.1 404 Not Found' body='' ;; esac printf '%s' "$body" > "$out_file" { printf '%s\n' "$status" printf 'X-Request-Id: req-collect\n' } > "$headers_file" printf '%s' "$code" EOF chmod +x "${TMP_DIR}/curl" } write_mock_curl_delete() { cat > "${TMP_DIR}/curl" <<'EOF' #!/bin/sh method=GET out_file= headers_file= url= while [ "$#" -gt 0 ]; do case "$1" in -X) method=$2; shift 2 ;; -D) headers_file=$2; shift 2 ;; -o) out_file=$2; shift 2 ;; -w) shift 2 ;; -H|-sS) shift 1 ;; *) url=$1; shift ;; esac done code='200' status='HTTP/1.1 200 OK' body='[]' case "$url" in *'/api/v1/packages/test-owner/nuget/pkg-a?page=1&limit=100') body='[{"name":"pkg-a","version":"1.0.0","created_at":"2023-01-01T00:00:00Z"},{"name":"pkg-a","version":"0.9.0","created_at":"2022-12-31T00:00:00Z"}]' ;; *'/api/v1/packages/test-owner/nuget/pkg-a/1.0.0') if [ "$method" = "DELETE" ]; then code='204' status='HTTP/1.1 204 No Content' body='' fi ;; *'/api/v1/packages/test-owner/nuget/pkg-a/0.9.0') if [ "$method" = "DELETE" ]; then code='404' status='HTTP/1.1 404 Not Found' body='' fi ;; esac printf '%s' "$body" > "$out_file" { printf '%s\n' "$status" printf 'X-Request-Id: req-delete\n' } > "$headers_file" if [ "$method" = "DELETE" ] && [ -n "${MOCK_DELETE_LOG:-}" ]; then printf '%s %s\n' "$method" "$url" >> "$MOCK_DELETE_LOG" fi printf '%s' "$code" EOF chmod +x "${TMP_DIR}/curl" } source "${ROOT_DIR}/entrypoint.sh" assert_eq "abc" "$(RUNNER_TOKEN=abc resolve_token)" expect_fail env -u RUNNER_TOKEN bash -lc "source '${ROOT_DIR}/entrypoint.sh'; resolve_token" assert_eq "5" "$(INPUT_KEEP_COUNT=5 resolve_keep_count)" expect_fail env INPUT_KEEP_COUNT=abc bash -lc "source '${ROOT_DIR}/entrypoint.sh'; resolve_keep_count" output="$(INPUT_PACKAGE_NAMES=$' pkg-a, pkg-b\npkg-a , pkg-c ' resolve_package_names)" assert_eq $'pkg-a\npkg-b\npkg-c' "${output}" expect_fail env INPUT_PACKAGE_NAMES=' , ' bash -lc "source '${ROOT_DIR}/entrypoint.sh'; resolve_package_names" IFS=$'\t' read -r owner repo <<< "$(parse_repo_context "owner/repo")" assert_eq "owner" "${owner}" assert_eq "repo" "${repo}" expect_fail bash -lc "source '${ROOT_DIR}/entrypoint.sh'; parse_repo_context 'repo'" write_mock_curl_pagination output="$(PATH="${TMP_DIR}:$PATH" GITEA_SERVER_URL="https://gitea.example.com" RESOLVED_GITEA_TOKEN="token" \ fetch_package_versions "test-owner" "pkg-a")" assert_eq "101" "$(jq 'length' <<<"${output}")" assert_eq "0.0.0" "$(jq -r '.[0].version' <<<"${output}")" assert_eq "100.100.0" "$(jq -r '.[100].version' <<<"${output}")" write_mock_curl_collect summary="$(PATH="${TMP_DIR}:$PATH" GITEA_SERVER_URL="https://gitea.example.com" RESOLVED_GITEA_TOKEN="token" \ collect_package_candidates "test-owner" "1" "${TMP_DIR}/candidates.tsv" "pkg-a" "pkg-b")" IFS=$'\t' read -r package_count total_version_count kept_count candidate_count <<< "${summary}" assert_eq "1" "${package_count}" assert_eq "3" "${total_version_count}" assert_eq "1" "${kept_count}" assert_eq "2" "${candidate_count}" assert_eq "2" "$(wc -l < "${TMP_DIR}/candidates.tsv" | tr -d ' ')" write_mock_curl_delete candidate_file="${TMP_DIR}/delete.tsv" cat > "${candidate_file}" <<'EOF' pkg-a 1.0.0 2023-01-01T00:00:00Z pkg-a 0.9.0 2022-12-31T00:00:00Z EOF output="$(MOCK_DELETE_LOG="${TMP_DIR}/delete.log" PATH="${TMP_DIR}:$PATH" GITEA_SERVER_URL="https://gitea.example.com" RESOLVED_GITEA_TOKEN="token" \ process_candidates "test-owner" "${candidate_file}" "1" "2" "1" "2" 2>&1)" assert_eq "1" "$(grep -c '^DELETE ' "${TMP_DIR}/delete.log")" assert_eq "Summary: packages=1 versions=2 kept=1 candidates=2 deleted=1 errors=1" "$(printf '%s' "${output}" | tail -n 1)"