- Migrate Paper loader from sunset v2 API to v3 (fill.papermc.io) - Fix NeoForge: fall back to beta versions when no stable exists (set -e + grep) - Fix setEula/setperms ordering (was writing eula.txt before /data was writable) - Fix setEula/serverInfoWrite to use absolute paths (/data/server/) - Add VOLUME directive removal (was shadowing bind mounts) - Add comprehensive API test suite (110 tests across 6 suites) - Move test.sh to tests/integration.sh, switch to named volumes - Add Docker build cache to CI workflows - Add .editorconfig, expand .gitignore, improve README docs
272 lines
9.3 KiB
Bash
Executable File
272 lines
9.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Tests for PaperMC Downloads Service API (v3)
|
|
# Covers: API reachability, build resolution, download URL, checksums
|
|
# Usage: ./tests/test_paper.sh [mc_version]
|
|
|
|
source "$(dirname "$0")/test_utils.sh"
|
|
|
|
MC_VERSION="${1:-$(get_latest_mc_version)}"
|
|
|
|
if [[ -z "$MC_VERSION" ]]; then
|
|
echo "Could not determine MC version"
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "\n${BOLD}PaperMC API Tests (MC ${MC_VERSION})${NC}"
|
|
echo ""
|
|
|
|
PAPER_API="https://fill.papermc.io/v3"
|
|
PAPER_UA="dockercraft-test/1.0"
|
|
|
|
paper_api_get() {
|
|
curl -sf --connect-timeout 10 --max-time 30 \
|
|
-H "User-Agent: ${PAPER_UA}" "$1" 2>/dev/null || true
|
|
}
|
|
|
|
# ---------- Project info endpoint ------------------------------------------
|
|
|
|
test_project_api_reachable() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper")
|
|
assert_not_empty "$body" "Project API body should not be empty"
|
|
assert_json_valid "$body" "Project API should return valid JSON"
|
|
}
|
|
|
|
test_project_has_versions() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper")
|
|
assert_json_field "$body" '.versions' "Should have .versions field"
|
|
}
|
|
|
|
test_project_versions_is_object() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper")
|
|
local type
|
|
type=$(echo "$body" | jq -r '.versions | type')
|
|
assert_equals "$type" "object" ".versions should be a JSON object"
|
|
}
|
|
|
|
test_project_has_version_groups() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper")
|
|
local count
|
|
count=$(echo "$body" | jq '.versions | length')
|
|
if [[ "$count" -gt 0 ]]; then
|
|
return 0
|
|
else
|
|
echo "ASSERT FAILED: .versions should have groups, got ${count}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------- Builds endpoint -------------------------------------------------
|
|
|
|
test_builds_api_reachable() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
assert_not_empty "$body" "Builds API body should not be empty"
|
|
}
|
|
|
|
test_builds_is_array() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local type
|
|
type=$(echo "$body" | jq -r 'type')
|
|
assert_equals "$type" "array" "Response should be a JSON array"
|
|
}
|
|
|
|
test_builds_has_entries() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local count
|
|
count=$(echo "$body" | jq 'length')
|
|
if [[ "$count" -gt 0 ]]; then
|
|
return 0
|
|
else
|
|
echo "ASSERT FAILED: builds array should have entries, got ${count}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_build_entry_has_id() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
assert_json_field "$body" '.[0].id' "Build entry should have .id field"
|
|
}
|
|
|
|
test_build_id_is_numeric() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local id
|
|
id=$(echo "$body" | jq -r '.[0].id')
|
|
assert_matches "$id" "^[0-9]+$" "Build id '${id}' should be numeric"
|
|
}
|
|
|
|
test_build_entry_has_channel() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
assert_json_field "$body" '.[0].channel' "Build entry should have .channel field"
|
|
}
|
|
|
|
test_build_channel_is_valid() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local channel
|
|
channel=$(echo "$body" | jq -r '.[0].channel')
|
|
assert_matches "$channel" "^(STABLE|EXPERIMENTAL)$" \
|
|
"Build channel '${channel}' should be STABLE or EXPERIMENTAL"
|
|
}
|
|
|
|
test_build_entry_has_downloads() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
assert_json_field "$body" '.[0].downloads."server:default"' \
|
|
"Build entry should have .downloads.\"server:default\""
|
|
}
|
|
|
|
# ---------- Stable builds --------------------------------------------------
|
|
|
|
test_stable_builds_exist() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local stable_count
|
|
stable_count=$(echo "$body" | jq '[.[] | select(.channel == "STABLE")] | length')
|
|
if [[ "$stable_count" -gt 0 ]]; then
|
|
return 0
|
|
else
|
|
echo "ASSERT FAILED: No stable builds found for MC ${MC_VERSION}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_latest_stable_build_id() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local id
|
|
id=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE")) | .id')
|
|
|
|
assert_not_empty "$id" "Latest stable build id should not be empty"
|
|
assert_matches "$id" "^[0-9]+$" "Build id should be numeric"
|
|
}
|
|
|
|
# ---------- Download URL ---------------------------------------------------
|
|
|
|
test_download_url_present() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local url
|
|
url=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".url) // empty')
|
|
|
|
assert_not_empty "$url" "Stable build download URL should not be empty"
|
|
}
|
|
|
|
test_download_url_format() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local url
|
|
url=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".url) // empty')
|
|
|
|
assert_matches "$url" ".*paper-.*\.jar$" \
|
|
"Download URL should end with a paper jar filename"
|
|
}
|
|
|
|
test_download_url_reachable() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local url
|
|
url=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".url) // empty')
|
|
|
|
if [[ -z "$url" ]]; then
|
|
echo "No download URL found"
|
|
return 1
|
|
fi
|
|
|
|
assert_http_ok "$url" "Download URL should return HTTP 200"
|
|
}
|
|
|
|
test_download_name_matches_pattern() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local name
|
|
name=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".name) // empty')
|
|
|
|
assert_matches "$name" "^paper-${MC_VERSION}-[0-9]+\.jar$" \
|
|
"Download name '${name}' should match paper-{version}-{build}.jar"
|
|
}
|
|
|
|
# ---------- Checksums ------------------------------------------------------
|
|
|
|
test_download_has_sha256() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local sha
|
|
sha=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".checksums.sha256) // empty')
|
|
|
|
assert_matches "$sha" "^[a-f0-9]{64}$" \
|
|
"SHA256 '${sha}' should be a 64-char hex string"
|
|
}
|
|
|
|
test_download_has_size() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local size
|
|
size=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".size) // empty')
|
|
|
|
assert_matches "$size" "^[0-9]+$" "Download size '${size}' should be numeric"
|
|
}
|
|
|
|
test_download_size_reasonable() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local size
|
|
size=$(echo "$body" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".size)')
|
|
|
|
# Paper jar should be between 1MB and 100MB
|
|
if [[ "$size" -gt 1000000 && "$size" -lt 100000000 ]]; then
|
|
return 0
|
|
else
|
|
echo "ASSERT FAILED: Download size ${size} bytes is outside expected range (1MB-100MB)"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ---------- Build ordering -------------------------------------------------
|
|
|
|
test_builds_are_ordered() {
|
|
local body
|
|
body=$(paper_api_get "${PAPER_API}/projects/paper/versions/${MC_VERSION}/builds")
|
|
local first_id last_id
|
|
first_id=$(echo "$body" | jq '.[0].id')
|
|
last_id=$(echo "$body" | jq '.[-1].id')
|
|
|
|
# v3 API returns builds in descending order (newest first)
|
|
assert_version_gte "$first_id" "$last_id" "Build ids should be in descending order"
|
|
}
|
|
|
|
# ---------- Run all tests --------------------------------------------------
|
|
|
|
run_test "Project API is reachable and valid JSON" test_project_api_reachable
|
|
run_test "Project has .versions field" test_project_has_versions
|
|
run_test ".versions is a JSON object" test_project_versions_is_object
|
|
run_test "Project has version groups" test_project_has_version_groups
|
|
run_test "Builds API is reachable" test_builds_api_reachable
|
|
run_test "Response is a JSON array" test_builds_is_array
|
|
run_test "Builds array has entries" test_builds_has_entries
|
|
run_test "Build entry has .id field" test_build_entry_has_id
|
|
run_test "Build id is numeric" test_build_id_is_numeric
|
|
run_test "Build entry has .channel field" test_build_entry_has_channel
|
|
run_test "Build channel is STABLE or EXPERIMENTAL" test_build_channel_is_valid
|
|
run_test "Build entry has downloads" test_build_entry_has_downloads
|
|
run_test "Stable builds exist for MC ${MC_VERSION}" test_stable_builds_exist
|
|
run_test "Latest stable build id is numeric" test_latest_stable_build_id
|
|
run_test "Download URL is present" test_download_url_present
|
|
run_test "Download URL has correct format" test_download_url_format
|
|
run_test "Download URL returns HTTP 200" test_download_url_reachable
|
|
run_test "Download name matches paper-{ver}-{build}.jar" test_download_name_matches_pattern
|
|
run_test "Download has SHA256 checksum" test_download_has_sha256
|
|
run_test "Download has file size" test_download_has_size
|
|
run_test "Download size is reasonable (1MB-100MB)" test_download_size_reasonable
|
|
run_test "Build ids are in descending order (newest first)" test_builds_are_ordered
|
|
|
|
print_summary
|