#!/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:-}" 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 }