Files
dockercraft/tests/test_utils.sh
T
jeremy 5688b30892 fix: migrate PaperMC to v3 API, fix NeoForge beta fallback, add test suite
- 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
2026-08-21 12:49:27 -04:00

282 lines
7.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Shared test utilities for Dockercraft API tests
# Source this file in individual test scripts: source "$(dirname "$0")/test_utils.sh"
set -euo pipefail
# ---------- colors ---------------------------------------------------------
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
else
RED='' GREEN='' YELLOW='' BLUE='' BOLD='' NC=''
fi
# ---------- counters -------------------------------------------------------
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
TESTS_SKIPPED=0
FAILED_TESTS=()
# ---------- test framework -------------------------------------------------
# Run a test function and track results
# Usage: run_test "test name" test_function_name
# Exit codes: 0=pass, 1=fail, 77=skip
# IMPORTANT: Test functions MUST use `exit 77` (not `return 77`) to signal skip.
# `return 77` does not propagate through command substitution with `set -e`.
run_test() {
local name="$1"
local func="$2"
local extra_args="${3:-}"
TESTS_RUN=$((TESTS_RUN + 1))
# Run the test function, capturing output
local output
local exit_code=0
if [[ -n "$extra_args" ]]; then
output=$("$func" "$extra_args" 2>&1) || exit_code=$?
else
output=$("$func" 2>&1) || exit_code=$?
fi
if [[ $exit_code -eq 0 ]]; then
TESTS_PASSED=$((TESTS_PASSED + 1))
echo -e " ${GREEN}${NC} ${name}"
if [[ -n "$output" ]]; then
echo -e " ${output}"
fi
elif [[ $exit_code -eq 77 ]]; then
TESTS_SKIPPED=$((TESTS_SKIPPED + 1))
echo -e " ${YELLOW}${NC} ${name} ${YELLOW}(skipped)${NC}"
if [[ -n "$output" ]]; then
echo -e " ${output}"
fi
else
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$name")
echo -e " ${RED}${NC} ${name}"
if [[ -n "$output" ]]; then
echo -e " ${output}"
fi
fi
}
# Assert that a string contains a substring
# Usage: assert_contains "$haystack" "$needle" ["message"]
assert_contains() {
local haystack="$1"
local needle="$2"
local message="${3:-Expected to contain '${needle}'}"
if [[ "$haystack" == *"$needle"* ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " Got: ${haystack:0:200}"
return 1
fi
}
# Assert that a string matches a regex
# Usage: assert_matches "$string" "$regex" ["message"]
assert_matches() {
local string="$1"
local regex="$2"
local message="${3:-Expected to match regex '${regex}'}"
if [[ "$string" =~ $regex ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " Got: ${string:0:200}"
return 1
fi
}
# Assert that two strings are equal
# Usage: assert_equals "$actual" "$expected" ["message"]
assert_equals() {
local actual="$1"
local expected="$2"
local message="${3:-Expected '${expected}', got '${actual}'}"
if [[ "$actual" == "$expected" ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " Expected: ${expected:0:200}"
echo " Got: ${actual:0:200}"
return 1
fi
}
# Assert that a string is not empty
# Usage: assert_not_empty "$value" ["message"]
assert_not_empty() {
local value="$1"
local message="${2:-Expected non-empty value}"
if [[ -n "$value" ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
return 1
fi
}
# Assert that a string is empty
# Usage: assert_empty "$value" ["message"]
assert_empty() {
local value="$1"
local message="${2:-Expected empty value}"
if [[ -z "$value" ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " Got: ${value:0:200}"
return 1
fi
}
# Assert that a JSON field exists and is non-null
# Usage: assert_json_field "$json" "$jq_expr" ["message"]
assert_json_field() {
local json="$1"
local jq_expr="$2"
local message="${3:-Expected JSON field '${jq_expr}' to exist}"
local value
value=$(echo "$json" | jq -r "$jq_expr" 2>/dev/null) || true
if [[ -n "$value" && "$value" != "null" && "$value" != "" ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " jq expression: ${jq_expr}"
echo " Result: ${value:-<empty>}"
return 1
fi
}
# Assert that a URL returns HTTP 200
# Usage: assert_http_ok "$url" ["message"]
assert_http_ok() {
local url="$1"
local message="${2:-Expected HTTP 200 from ${url}}"
local http_code
http_code=$(curl -sL -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 30 "$url") || true
if [[ "$http_code" == "200" ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " URL: ${url}"
echo " HTTP status: ${http_code}"
return 1
fi
}
# Assert that JSON parses successfully and has a given top-level key
# Usage: assert_json_valid "$json" ["message"]
assert_json_valid() {
local json="$1"
local message="${2:-Expected valid JSON}"
if echo "$json" | jq empty 2>/dev/null; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " First 200 chars: ${json:0:200}"
return 1
fi
}
# Version comparison: assert version A >= version B
# Usage: assert_version_gte "$version_a" "$version_b"
assert_version_gte() {
local v_a="$1"
local v_b="$2"
local message="${3:-Expected ${v_a} >= ${v_b}}"
local result
result=$(printf '%s\n' "$v_b" "$v_a" | sort -V | head -n1)
if [[ "$result" == "$v_b" ]]; then
return 0
else
echo "ASSERT FAILED: ${message}"
echo " ${v_a} is not >= ${v_b}"
return 1
fi
}
# Version comparison: assert version A <= version B
assert_version_lte() {
assert_version_gte "$2" "$1" "${3:-Expected ${1} <= ${2}}"
}
# ---------- API helpers ----------------------------------------------------
# Fetch a URL and return the body, or empty string on failure
# Usage: api_get "$url"
api_get() {
curl -sf --connect-timeout 10 --max-time 30 "$1" 2>/dev/null || true
}
# Fetch a URL with custom headers and return the body
# Usage: api_get_ua "$url" ["user_agent"]
api_get_ua() {
local ua="${2:-Mozilla/5.0 (X11; Linux x86_64)}"
curl -sL -A "$ua" --connect-timeout 10 --max-time 30 "$1" 2>/dev/null || true
}
# Get the latest MC version from Mojang manifest
# Usage: get_latest_mc_version
get_latest_mc_version() {
api_get "https://launchermeta.mojang.com/mc/game/version_manifest.json" |
jq -r '.latest.release'
}
# ---------- summary --------------------------------------------------------
print_summary() {
echo ""
echo -e "${BOLD}========================================${NC}"
echo -e "${BOLD} Test Results${NC}"
echo -e "${BOLD}========================================${NC}"
echo -e " Total: ${TESTS_RUN}"
echo -e " ${GREEN}Passed: ${TESTS_PASSED}${NC}"
if [[ $TESTS_FAILED -gt 0 ]]; then
echo -e " ${RED}Failed: ${TESTS_FAILED}${NC}"
else
echo -e " Failed: 0"
fi
if [[ $TESTS_SKIPPED -gt 0 ]]; then
echo -e " ${YELLOW}Skipped: ${TESTS_SKIPPED}${NC}"
fi
echo -e "${BOLD}========================================${NC}"
if [[ $TESTS_FAILED -gt 0 ]]; then
echo ""
echo -e "${RED}Failed tests:${NC}"
for t in "${FAILED_TESTS[@]}"; do
echo -e " ${RED}${t}${NC}"
done
return 1
fi
echo -e "\n${GREEN}All tests passed!${NC}"
return 0
}