#!/usr/bin/env bash set -Eeuo pipefail EXPECTED_EXTERNAL_TESTS="${EXPECTED_EXTERNAL_TESTS:-10}" WORK_DIR="$(mktemp -d)" POSTGRES_CONTAINER="" REDIS_CONTAINER="" MINIO_PID="" cleanup() { local status=$? trap - EXIT INT TERM set +e if [[ -n "$MINIO_PID" ]]; then kill "$MINIO_PID" 2>/dev/null wait "$MINIO_PID" 2>/dev/null fi if [[ -n "$POSTGRES_CONTAINER" ]]; then docker rm -f "$POSTGRES_CONTAINER" >/dev/null 2>&1 fi if [[ -n "$REDIS_CONTAINER" ]]; then docker rm -f "$REDIS_CONTAINER" >/dev/null 2>&1 fi rm -rf "$WORK_DIR" exit "$status" } trap cleanup EXIT INT TERM wait_for_command() { local description=$1 shift for _ in $(seq 1 60); do if "$@" >/dev/null 2>&1; then return 0 fi sleep 1 done echo "Timed out waiting for ${description}" >&2 return 1 } if [[ -z "${IMAGEFORGE_TEST_DATABASE_URL:-}" || -z "${IMAGEFORGE_TEST_REDIS_URL:-}" ]]; then if ! command -v docker >/dev/null 2>&1; then echo "Set IMAGEFORGE_TEST_DATABASE_URL and IMAGEFORGE_TEST_REDIS_URL, or install Docker." >&2 exit 1 fi suffix="$$-${RANDOM}" postgres_port="${IMAGEFORGE_TEST_POSTGRES_PORT:-25432}" redis_port="${IMAGEFORGE_TEST_REDIS_PORT:-26379}" POSTGRES_CONTAINER="imageforge-test-postgres-${suffix}" REDIS_CONTAINER="imageforge-test-redis-${suffix}" docker run -d \ --label imageforge.external-tests=true \ --name "$POSTGRES_CONTAINER" \ -e POSTGRES_PASSWORD=codex_test \ -e POSTGRES_DB=imageforge_test \ -p "127.0.0.1:${postgres_port}:5432" \ postgres:16-alpine >/dev/null docker run -d \ --label imageforge.external-tests=true \ --name "$REDIS_CONTAINER" \ -p "127.0.0.1:${redis_port}:6379" \ redis:7-alpine >/dev/null wait_for_command PostgreSQL docker exec "$POSTGRES_CONTAINER" \ pg_isready -U postgres -d imageforge_test wait_for_command Redis docker exec "$REDIS_CONTAINER" redis-cli ping export IMAGEFORGE_TEST_DATABASE_URL="postgres://postgres:codex_test@127.0.0.1:${postgres_port}/imageforge_test" export IMAGEFORGE_TEST_REDIS_URL="redis://127.0.0.1:${redis_port}/" fi arch="$(uname -m)" case "$arch" in x86_64) minio_arch=amd64 ;; aarch64|arm64) minio_arch=arm64 ;; *) echo "Unsupported MinIO architecture: ${arch}" >&2; exit 1 ;; esac default_minio_bin="${WORK_DIR}/minio" default_mc_bin="${WORK_DIR}/mc" if command -v minio >/dev/null 2>&1; then default_minio_bin="$(command -v minio)" fi if command -v mc >/dev/null 2>&1; then default_mc_bin="$(command -v mc)" fi MINIO_BIN="${MINIO_BIN:-${default_minio_bin}}" MC_BIN="${MC_BIN:-${default_mc_bin}}" download_tool() { local url=$1 local output=$2 local partial="${output}.part" curl --fail --silent --show-error --location \ --retry 10 --retry-all-errors --retry-delay 2 --connect-timeout 20 \ --continue-at - --output "$partial" "$url" mv "$partial" "$output" chmod +x "$output" } if [[ ! -x "$MINIO_BIN" ]]; then download_tool \ "https://dl.min.io/server/minio/release/linux-${minio_arch}/minio" \ "$MINIO_BIN" fi if [[ ! -x "$MC_BIN" ]]; then download_tool \ "https://dl.min.io/client/mc/release/linux-${minio_arch}/mc" \ "$MC_BIN" fi export IMAGEFORGE_TEST_S3_ENDPOINT="${IMAGEFORGE_TEST_S3_ENDPOINT:-http://127.0.0.1:19000}" export IMAGEFORGE_TEST_S3_BUCKET="${IMAGEFORGE_TEST_S3_BUCKET:-imageforge-test}" export IMAGEFORGE_TEST_S3_ACCESS_KEY="${IMAGEFORGE_TEST_S3_ACCESS_KEY:-codexminio}" export IMAGEFORGE_TEST_S3_SECRET_KEY="${IMAGEFORGE_TEST_S3_SECRET_KEY:-codexminio123}" if [[ "$IMAGEFORGE_TEST_S3_ENDPOINT" == "http://127.0.0.1:19000" ]]; then MINIO_ROOT_USER="$IMAGEFORGE_TEST_S3_ACCESS_KEY" \ MINIO_ROOT_PASSWORD="$IMAGEFORGE_TEST_S3_SECRET_KEY" \ "$MINIO_BIN" server "${WORK_DIR}/minio-data" \ --address 127.0.0.1:19000 >"${WORK_DIR}/minio.log" 2>&1 & MINIO_PID=$! wait_for_command MinIO curl --fail --silent \ "${IMAGEFORGE_TEST_S3_ENDPOINT}/minio/health/ready" fi "$MC_BIN" alias set imageforge-test \ "$IMAGEFORGE_TEST_S3_ENDPOINT" \ "$IMAGEFORGE_TEST_S3_ACCESS_KEY" \ "$IMAGEFORGE_TEST_S3_SECRET_KEY" >/dev/null "$MC_BIN" mb --ignore-existing \ "imageforge-test/${IMAGEFORGE_TEST_S3_BUCKET}" >/dev/null export DATABASE_URL="$IMAGEFORGE_TEST_DATABASE_URL" export REDIS_URL="$IMAGEFORGE_TEST_REDIS_URL" export JWT_SECRET="${JWT_SECRET:-imageforge-external-test-jwt-secret}" export API_KEY_PEPPER="${API_KEY_PEPPER:-imageforge-external-test-api-key-pepper}" test_log="${WORK_DIR}/external-tests.log" cargo test --all-targets --locked -- \ --ignored --test-threads=1 --nocapture 2>&1 | tee "$test_log" expected_summary="test result: ok. ${EXPECTED_EXTERNAL_TESTS} passed; 0 failed; 0 ignored;" if ! grep --fixed-strings --quiet "$expected_summary" "$test_log"; then echo "External-state test count mismatch; expected: ${expected_summary}" >&2 exit 1 fi echo "External-state gate passed: ${EXPECTED_EXTERNAL_TESTS} passed, 0 failed, 0 ignored."