#!/bin/bash
# Copyright 2024 RustFS Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -euo pipefail

# Disable proxy for localhost requests to avoid interference
export NO_PROXY="127.0.0.1,localhost,::1"
export no_proxy="${NO_PROXY}"
# Keep proxy variables for external downloads; NO_PROXY bypasses localhost.

# Ensure user-level Python scripts are discoverable (awscurl/tox installed via --user)
PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null || echo "3.14")
export PATH="$HOME/Library/Python/${PYTHON_VERSION}/bin:$HOME/.local/bin:$PATH"
if command -v uv >/dev/null 2>&1; then
    UV_TOOL_BIN="$(uv tool dir --bin 2>/dev/null || true)"
    if [ -n "${UV_TOOL_BIN}" ]; then
        export PATH="${UV_TOOL_BIN}:$PATH"
    fi
fi

# Configuration
S3_ACCESS_KEY="${S3_ACCESS_KEY:-rustfsadmin}"
S3_SECRET_KEY="${S3_SECRET_KEY:-rustfsadmin}"
S3_ALT_ACCESS_KEY="${S3_ALT_ACCESS_KEY:-rustfsalt}"
S3_ALT_SECRET_KEY="${S3_ALT_SECRET_KEY:-rustfsalt}"
S3_REGION="${S3_REGION:-us-east-1}"
S3_HOST="${S3_HOST:-127.0.0.1}"
S3_PORT="${S3_PORT:-9000}"

# Keep the compatibility harness focused on foreground S3 API behavior.
# The background scanner can race short-lived test buckets and add avoidable
# metacache/listing pressure on small CI runners.
export RUSTFS_SCANNER_ENABLED="${RUSTFS_SCANNER_ENABLED:-false}"
export RUSTFS_SCANNER_START_DELAY_SECS="${RUSTFS_SCANNER_START_DELAY_SECS:-3600}"
export RUSTFS_SCANNER_CYCLE="${RUSTFS_SCANNER_CYCLE:-3600}"

# Test parameters
TEST_MODE="${TEST_MODE:-single}"
MAXFAIL="${MAXFAIL:-1}"
XDIST="${XDIST:-0}"
# Test scope:
#   "implemented" (default) - run only the implemented_tests.txt whitelist (PR gate)
#   "all"                   - run the entire upstream suite (scheduled full sweep)
TEST_SCOPE="${TEST_SCOPE:-implemented}"
if [[ "${TEST_SCOPE}" != "implemented" && "${TEST_SCOPE}" != "all" ]]; then
    echo "[ERROR] Invalid TEST_SCOPE: ${TEST_SCOPE} (must be \"implemented\" or \"all\")" >&2
    exit 1
fi
S3_SHARD_COUNT="${S3_SHARD_COUNT:-1}"
S3_SHARD_INDEX="${S3_SHARD_INDEX:-0}"
TEST_TIMEOUT="${TEST_TIMEOUT:-300}"
if [[ ! "${S3_SHARD_COUNT}" =~ ^[1-9][0-9]*$ ]] \
    || [[ ! "${S3_SHARD_INDEX}" =~ ^[0-9]+$ ]] \
    || (( S3_SHARD_INDEX >= S3_SHARD_COUNT )); then
    echo "[ERROR] Invalid S3 shard ${S3_SHARD_INDEX}/${S3_SHARD_COUNT}" >&2
    exit 1
fi
if [[ ! "${TEST_TIMEOUT}" =~ ^[1-9][0-9]*$ ]]; then
    echo "[ERROR] Invalid TEST_TIMEOUT: ${TEST_TIMEOUT}" >&2
    exit 1
fi

# Upstream ceph/s3-tests suite, pinned for reproducible runs.
# Bump S3TESTS_REV deliberately: upstream changes can rename tests or change
# assertions, so a bump usually requires reclassifying the test list files.
S3TESTS_REPO="${S3TESTS_REPO:-https://github.com/ceph/s3-tests.git}"
S3TESTS_REV="${S3TESTS_REV:-5522d1c351f75bc00ae0f64f742f3f095f5939d9}"

# Compatibility default for the s3-tests harness:
# this script provisions multiple local export directories on the same physical disk.
# Prefer the canonical bypass knob by default, and only honor the legacy CI alias
# when it is already provided by the environment.
if [ -z "${RUSTFS_UNSAFE_BYPASS_DISK_CHECK+x}" ] && [ -z "${MINIO_CI+x}" ]; then
    export RUSTFS_UNSAFE_BYPASS_DISK_CHECK="true"
fi

# Directories (define early for use in test list loading)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

log_info() {
    echo -e "${GREEN}[INFO]${NC} $*"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $*"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $*"
}

# =============================================================================
# Test Classification Files
# =============================================================================
# Tests are classified into three categories stored in text files:
#   - implemented_tests.txt:   Tests that should pass on RustFS
#   - unimplemented_tests.txt: Standard S3 features planned but not yet implemented
#   - excluded_tests.txt:      Tests intentionally excluded from RustFS gating
#
# By default, only tests listed in implemented_tests.txt are run.
# Use TESTEXPR env var to override and run custom test selection.
# =============================================================================

# Test list files location
TEST_LISTS_DIR="${SCRIPT_DIR}"
# IMPLEMENTED_TESTS_FILE may be overridden to run an alternate exact-node-id
# whitelist (e.g. lifecycle_behavior_tests.txt for the dedicated lifecycle
# behavior lane, which needs RUSTFS_ILM_DEBUG_DAY_SECS + the scanner enabled).
IMPLEMENTED_TESTS_FILE="${IMPLEMENTED_TESTS_FILE:-${TEST_LISTS_DIR}/implemented_tests.txt}"
UNIMPLEMENTED_TESTS_FILE="${TEST_LISTS_DIR}/unimplemented_tests.txt"
EXCLUDED_TESTS_FILE="${TEST_LISTS_DIR}/excluded_tests.txt"
S3_TEST_FILE="s3tests/functional/test_s3.py"

# =============================================================================
# load_testnodes_from_file: Read test names and build exact pytest node ids
# =============================================================================
# Pytest -k matches substrings, so similar test names can accidentally include or
# exclude each other. The default file-based path uses exact node ids instead.
# =============================================================================
TEST_NODE_ARGS=()
USE_FILE_TEST_NODES=false

load_testnodes_from_file() {
    local file="$1"
    local line=""

    if [[ ! -f "${file}" ]]; then
        log_error "Test list file not found: ${file}"
        return 1
    fi

    while IFS= read -r line || [[ -n "$line" ]]; do
        # Skip empty lines and comments
        [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
        # Trim whitespace
        line=$(echo "$line" | xargs)
        [[ -z "$line" ]] && continue

        TEST_NODE_ARGS+=("${S3_TEST_FILE}::${line}")
    done < "${file}"
}

# =============================================================================
# MARKEXPR: pytest marker expression
# =============================================================================
# File-based test selection is authoritative. Keep the default marker expression
# non-restrictive so implemented tests that carry upstream compatibility markers
# are still run when they are listed in implemented_tests.txt.
# =============================================================================
if [[ -z "${MARKEXPR:-}" ]]; then
    # Valid pytest -m expression that does not match any real marker.
    MARKEXPR="not rustfs_never_marker"
fi

# =============================================================================
# TESTEXPR: optional pytest -k expression to select specific tests
# =============================================================================
# By default, loads exact pytest node ids from implemented_tests.txt.
# Set TESTEXPR to override this with a custom pytest -k expression.
#
# The file-based approach provides:
#   1. Clear visibility of which tests are run
#   2. Easy maintenance - edit txt files to add/remove tests
#   3. Separation of concerns - test classification vs test execution
# =============================================================================
TESTEXPR="${TESTEXPR:-}"
if [[ -n "${TESTEXPR}" ]]; then
    log_info "Using custom TESTEXPR selection: ${TESTEXPR}"
elif [[ "${TEST_SCOPE}" == "all" ]]; then
    log_info "TEST_SCOPE=all: running the entire upstream test suite (no whitelist)"
else
    if [[ -f "${IMPLEMENTED_TESTS_FILE}" ]]; then
        log_info "Loading test list from: ${IMPLEMENTED_TESTS_FILE}"
        load_testnodes_from_file "${IMPLEMENTED_TESTS_FILE}"
        TEST_COUNT="${#TEST_NODE_ARGS[@]}"
        if [[ "${TEST_COUNT}" -eq 0 ]]; then
            log_error "No tests found in ${IMPLEMENTED_TESTS_FILE}"
            exit 1
        fi
        USE_FILE_TEST_NODES=true
        log_info "Loaded ${TEST_COUNT} exact test nodes from implemented_tests.txt"
    else
        log_warn "Test list file not found: ${IMPLEMENTED_TESTS_FILE}"
        log_warn "Falling back to exclusion-based filtering"
        EXCLUDED_TESTS=(
            "test_post_object"
            "test_bucket_list_objects_anonymous"
            "test_bucket_listv2_objects_anonymous"
            "test_bucket_concurrent_set_canned_acl"
            "test_bucket_acl"
            "test_object_acl"
            "test_access_bucket"
            "test_100_continue"
            "test_cors"
            "test_object_raw"
            "test_versioning"
            "test_versioned"
        )
        TESTEXPR=""
        for pattern in "${EXCLUDED_TESTS[@]}"; do
            if [[ -n "${TESTEXPR}" ]]; then
                TESTEXPR+=" and "
            fi
            TESTEXPR+="not ${pattern}"
        done
    fi
fi

# Configuration file paths
S3TESTS_CONF_TEMPLATE="${S3TESTS_CONF_TEMPLATE:-.github/s3tests/s3tests.conf}"
S3TESTS_CONF="${S3TESTS_CONF:-s3tests.conf}"

# Service deployment mode: "build", "binary", "docker", or "existing"
# - "build": Compile with cargo build --release and run (default)
# - "binary": Use pre-compiled binary (RUSTFS_BINARY path or default)
# - "docker": Build Docker image and run in container
# - "existing": Use already running service (skip start, use S3_HOST and S3_PORT)
DEPLOY_MODE="${DEPLOY_MODE:-build}"
RUSTFS_BINARY="${RUSTFS_BINARY:-}"
NO_CACHE="${NO_CACHE:-false}"
S3TESTS_LOCAL_SSE_MASTER_KEY_DEFAULT="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY="

# Additional directories (SCRIPT_DIR and PROJECT_ROOT defined earlier)
ARTIFACTS_DIR="${PROJECT_ROOT}/artifacts/s3tests-${TEST_MODE}"
CONTAINER_NAME="rustfs-${TEST_MODE}"
NETWORK_NAME="rustfs-net"
DATA_ROOT="${DATA_ROOT:-target}"
if [[ "${DATA_ROOT}" = /* ]]; then
    DATA_BASE="${DATA_ROOT}"
else
    DATA_BASE="${PROJECT_ROOT}/${DATA_ROOT}"
fi
DATA_DIR="${DATA_BASE}/test-data/${CONTAINER_NAME}"
RUSTFS_PID=""

if [ "${DEPLOY_MODE}" != "existing" ] && [ -z "${RUSTFS_SSE_S3_MASTER_KEY:-}" ]; then
    export RUSTFS_SSE_S3_MASTER_KEY="${S3TESTS_LOCAL_SSE_MASTER_KEY_DEFAULT}"
    log_info "Using deterministic local SSE-S3 master key for the s3-tests harness"
fi

show_usage() {
    cat << EOF
Usage: $0 [OPTIONS]

Options:
  -h, --help     Show this help message
  --no-cache     Force rebuild even if binary exists and is recent (for build mode)

Deployment Modes (via DEPLOY_MODE environment variable):
  1. build      - Compile with cargo build --release and run (default)
  2. binary     - Use pre-compiled binary (use RUSTFS_BINARY or default: ./target/release/rustfs)
  3. docker     - Build Docker image and run in container
  4. existing   - Use already running service (specify S3_HOST and S3_PORT)

Environment Variables:
  DEPLOY_MODE            - Deployment mode: "build", "binary", "docker", or "existing" (default: "build")
  RUSTFS_BINARY          - Path to RustFS binary (for binary mode, default: ./target/release/rustfs)
  S3_HOST                - S3 service host (default: 127.0.0.1)
  S3_PORT                - S3 service port (default: 9000)
  S3_ACCESS_KEY          - Main user access key (default: rustfsadmin)
  S3_SECRET_KEY          - Main user secret key (default: rustfsadmin)
  S3_ALT_ACCESS_KEY      - Alt user access key (default: rustfsalt)
  S3_ALT_SECRET_KEY      - Alt user secret key (default: rustfsalt)
  RUSTFS_SSE_S3_MASTER_KEY - Optional base64 32-byte key for local managed SSE fallback
  RUSTFS_SCANNER_ENABLED - Enable background scanner for harness service (default: false)
  MAXFAIL                - Stop after N failures, 0 = never stop (default: 1)
  XDIST                  - Enable parallel execution with N workers (default: 0)
  TEST_SCOPE             - "implemented" (whitelist, default) or "all" (entire upstream suite)
  S3_SHARD_COUNT         - Number of deterministic exact-node-ID shards (default: 1)
  S3_SHARD_INDEX         - Zero-based shard index (default: 0)
  TEST_TIMEOUT           - Per-test timeout in seconds (default: 300)
  S3TESTS_REPO           - s3-tests repository URL (default: https://github.com/ceph/s3-tests.git)
  S3TESTS_REV            - Pinned s3-tests commit; bump deliberately and reclassify test lists
  MARKEXPR               - pytest marker expression (default: no marker filtering)
  TESTEXPR               - pytest -k expression (overrides implemented_tests.txt node list)
  S3TESTS_CONF_TEMPLATE  - Path to s3tests config template (default: .github/s3tests/s3tests.conf)
  S3TESTS_CONF           - Path to generated s3tests config (default: s3tests.conf)
  DATA_ROOT              - Root directory for test data storage (default: target)
                            Final path: \${DATA_ROOT}/test-data/\${CONTAINER_NAME}

Test Classification Files (in scripts/s3-tests/):
  implemented_tests.txt    - Implemented tests (run by default)
  unimplemented_tests.txt  - Standard S3 tests planned but not implemented
  excluded_tests.txt       - Tests intentionally excluded from RustFS gating

Notes:
  - Tests are loaded from implemented_tests.txt by default
  - Set TESTEXPR to override with custom test selection
  - In build mode, if the binary exists and was compiled less than 30 minutes ago,
    compilation will be skipped unless --no-cache is specified.

Examples:
  # Use Docker (default)
  $0

  # Use pre-compiled binary
  DEPLOY_MODE=binary RUSTFS_BINARY=./target/release/rustfs $0

  # Use Docker
  DEPLOY_MODE=docker $0

  # Use existing service
  DEPLOY_MODE=existing S3_HOST=192.168.1.100 S3_PORT=9000 $0

  # Force rebuild in build mode
  $0 --no-cache

EOF
}

cleanup() {
    log_info "Cleaning up..."

    if [ "${DEPLOY_MODE}" = "docker" ]; then
        docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
        docker network rm "${NETWORK_NAME}" >/dev/null 2>&1 || true
    elif [ "${DEPLOY_MODE}" = "build" ] || [ "${DEPLOY_MODE}" = "binary" ]; then
        if [ -n "${RUSTFS_PID}" ]; then
            log_info "Stopping RustFS process (PID: ${RUSTFS_PID})..."
            kill "${RUSTFS_PID}" 2>/dev/null || true
            wait "${RUSTFS_PID}" 2>/dev/null || true
        fi
    fi
}

trap cleanup EXIT

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_usage
            exit 0
            ;;
        --no-cache)
            NO_CACHE="true"
            ;;
        *)
            log_error "Unknown option: $1"
            show_usage
            exit 1
            ;;
    esac
    shift
done

cd "${PROJECT_ROOT}"

# Check port availability (except for existing mode)
if [ "${DEPLOY_MODE}" != "existing" ]; then
    if nc -z "${S3_HOST}" "${S3_PORT}" 2>/dev/null || timeout 1 bash -c "cat < /dev/null > /dev/tcp/${S3_HOST}/${S3_PORT}" 2>/dev/null; then
        log_error "Port ${S3_PORT} is already in use on ${S3_HOST}"
        log_error "Please stop the service using this port or use DEPLOY_MODE=existing to connect to an existing service"
        exit 1
    fi
fi

# Start RustFS based on deployment mode
if [ "${DEPLOY_MODE}" = "existing" ]; then
    log_info "Using existing RustFS service at ${S3_HOST}:${S3_PORT}"
    log_info "Skipping service startup..."
elif [ "${DEPLOY_MODE}" = "binary" ]; then
    # Determine binary path
    if [ -z "${RUSTFS_BINARY}" ]; then
        RUSTFS_BINARY="${PROJECT_ROOT}/target/release/rustfs"
    fi

    if [ ! -f "${RUSTFS_BINARY}" ]; then
        log_error "RustFS binary not found at: ${RUSTFS_BINARY}"
        log_info "Please compile the binary first:"
        log_info "  cargo build --release"
        log_info "Or specify the path: RUSTFS_BINARY=/path/to/rustfs $0"
        exit 1
    fi

    log_info "Using pre-compiled binary: ${RUSTFS_BINARY}"

    # Prepare data and artifacts directories
    mkdir -p "${DATA_DIR}"
    mkdir -p "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}"
    for i in {0..3}; do
        mkdir -p "${DATA_DIR}/rustfs${i}"
    done

    # Start RustFS binary
    log_info "Starting RustFS binary..."
    RUST_LOG="${RUST_LOG:-info}" "${RUSTFS_BINARY}" \
        --address "${S3_HOST}:${S3_PORT}" \
        --access-key "${S3_ACCESS_KEY}" \
        --secret-key "${S3_SECRET_KEY}" \
        "${DATA_DIR}/rustfs0" "${DATA_DIR}/rustfs1" "${DATA_DIR}/rustfs2" "${DATA_DIR}/rustfs3" \
        > "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" 2>&1 &

    RUSTFS_PID=$!
    log_info "RustFS started with PID: ${RUSTFS_PID}"

elif [ "${DEPLOY_MODE}" = "build" ]; then
    RUSTFS_BINARY="${PROJECT_ROOT}/target/release/rustfs"

    # Check if we should skip compilation
    SHOULD_BUILD=true
    if [ -f "${RUSTFS_BINARY}" ]; then
        # Get file modification time in seconds since epoch
        # Try Linux format first, fallback to macOS format
        FILE_MTIME=$(stat -c %Y "${RUSTFS_BINARY}" 2>/dev/null || stat -f %m "${RUSTFS_BINARY}" 2>/dev/null)

        if [ -n "${FILE_MTIME}" ]; then
            CURRENT_TIME=$(date +%s)
            AGE_SECONDS=$((CURRENT_TIME - FILE_MTIME))
            AGE_MINUTES=$((AGE_SECONDS / 60))

            if [ "${AGE_MINUTES}" -lt 30 ] && [ "${NO_CACHE}" != "true" ]; then
                log_info "Binary exists and is recent (${AGE_MINUTES} minutes old), skipping compilation."
                log_info "Use --no-cache to force rebuild."
                SHOULD_BUILD=false
            fi
        fi
    fi

    if [ "${SHOULD_BUILD}" = "true" ]; then
        if [ "${NO_CACHE}" = "true" ]; then
            log_info "Building RustFS with cargo build --release (--no-cache forced)..."
        else
            log_info "Building RustFS with cargo build --release..."
        fi
        cargo build --release || {
            log_error "Failed to build RustFS"
            exit 1
        }

        if [ ! -f "${RUSTFS_BINARY}" ]; then
            log_error "RustFS binary not found at: ${RUSTFS_BINARY}"
            log_error "Build completed but binary not found"
            exit 1
        fi

        log_info "Build successful, using binary: ${RUSTFS_BINARY}"
    else
        log_info "Using existing binary: ${RUSTFS_BINARY}"
    fi

    # Prepare data and artifacts directories
    mkdir -p "${DATA_DIR}"
    mkdir -p "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}"
    for i in {0..3}; do
        mkdir -p "${DATA_DIR}/rustfs${i}"
    done

    # Start RustFS binary
    log_info "Starting RustFS binary..."
    RUST_LOG="${RUST_LOG:-info}" "${RUSTFS_BINARY}" \
        --address "${S3_HOST}:${S3_PORT}" \
        --access-key "${S3_ACCESS_KEY}" \
        --secret-key "${S3_SECRET_KEY}" \
        "${DATA_DIR}/rustfs0" "${DATA_DIR}/rustfs1" "${DATA_DIR}/rustfs2" "${DATA_DIR}/rustfs3" \
        > "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" 2>&1 &

    RUSTFS_PID=$!
    log_info "RustFS started with PID: ${RUSTFS_PID}"

elif [ "${DEPLOY_MODE}" = "docker" ]; then
    # Build Docker image and run in container
    log_info "Building RustFS Docker image..."
    DOCKER_BUILDKIT=1 docker build \
        --platform linux/amd64 \
        -t rustfs-ci \
        -f Dockerfile.source . || {
        log_error "Failed to build Docker image"
        exit 1
    }

    # Create network
    log_info "Creating Docker network..."
    docker network inspect "${NETWORK_NAME}" >/dev/null 2>&1 || docker network create "${NETWORK_NAME}"

    # Remove existing container
    log_info "Removing existing container (if any)..."
    docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true

    # Start RustFS container
    log_info "Starting RustFS container..."
    docker run -d --name "${CONTAINER_NAME}" \
        --network "${NETWORK_NAME}" \
        -p "${S3_PORT}:9000" \
        -e RUSTFS_ADDRESS=0.0.0.0:9000 \
        -e RUSTFS_ACCESS_KEY="${S3_ACCESS_KEY}" \
        -e RUSTFS_SECRET_KEY="${S3_SECRET_KEY}" \
        -e RUSTFS_SSE_S3_MASTER_KEY="${RUSTFS_SSE_S3_MASTER_KEY}" \
        -e RUSTFS_SCANNER_ENABLED="${RUSTFS_SCANNER_ENABLED}" \
        -e RUSTFS_SCANNER_START_DELAY_SECS="${RUSTFS_SCANNER_START_DELAY_SECS}" \
        -e RUSTFS_SCANNER_CYCLE="${RUSTFS_SCANNER_CYCLE}" \
        -e RUSTFS_VOLUMES="/data/rustfs0 /data/rustfs1 /data/rustfs2 /data/rustfs3" \
        -v "/tmp/${CONTAINER_NAME}:/data" \
        rustfs-ci || {
        log_error "Failed to start container"
        docker logs "${CONTAINER_NAME}" || true
        exit 1
    }
else
    log_error "Invalid DEPLOY_MODE: ${DEPLOY_MODE}"
    log_error "Must be one of: build, binary, docker, existing"
    show_usage
    exit 1
fi

# Step 5: Wait for RustFS ready (improved health check)
log_info "Waiting for RustFS to be ready..."

if [ "${DEPLOY_MODE}" = "docker" ]; then
    log_info "Step 1: Waiting for container to start..."
    for i in {1..30}; do
        if [ "$(docker inspect -f '{{.State.Running}}' "${CONTAINER_NAME}" 2>/dev/null)" == "true" ]; then
            break
        fi
        sleep 1
    done

    if [ "$(docker inspect -f '{{.State.Running}}' "${CONTAINER_NAME}" 2>/dev/null)" != "true" ]; then
        log_error "Container failed to start"
        docker logs "${CONTAINER_NAME}" || true
        exit 1
    fi
elif [ "${DEPLOY_MODE}" = "build" ] || [ "${DEPLOY_MODE}" = "binary" ]; then
    log_info "Step 1: Waiting for process to start..."
    for i in {1..10}; do
        if kill -0 "${RUSTFS_PID}" 2>/dev/null; then
            break
        fi
        sleep 1
    done

    if ! kill -0 "${RUSTFS_PID}" 2>/dev/null; then
        log_error "RustFS process failed to start"
        if [ -f "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" ]; then
            tail -50 "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log"
        fi
        exit 1
    fi
elif [ "${DEPLOY_MODE}" = "existing" ]; then
    log_info "Step 1: Checking existing service..."
    # Skip container/process checks for existing service
fi

log_info "Step 2: Waiting for port ${S3_PORT} to be listening..."
for i in {1..30}; do
    if nc -z "${S3_HOST}" "${S3_PORT}" 2>/dev/null || timeout 1 bash -c "cat < /dev/null > /dev/tcp/${S3_HOST}/${S3_PORT}" 2>/dev/null; then
        log_info "Port ${S3_PORT} is listening"
        break
    fi
    sleep 1
done

log_info "Step 3: Waiting for service to fully initialize..."

# Check if log file indicates server is started (most reliable method)
check_server_ready_from_log() {
    if [ -f "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" ]; then
        if grep -q "server started successfully" "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" 2>/dev/null; then
            return 0
        fi
    fi
    return 1
}

# Test S3 API readiness
test_s3_api_ready() {
    # Step 1: Require the dependency-aware readiness endpoint.
    READY_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
        -X GET \
        "http://${S3_HOST}:${S3_PORT}/health/ready" \
        --max-time 5 2>/dev/null || echo "000")

    if [ "${READY_CODE}" = "000" ]; then
        # Connection failed - server might not be running or not listening yet
        return 1
    elif [ "${READY_CODE}" != "200" ]; then
        # The service is live but its storage/IAM dependencies are not ready.
        return 1
    fi

    # Step 2: Test S3 API with signed request (awscurl) if available
    # This tests if S3 API is actually ready and can process requests
    if command -v awscurl >/dev/null 2>&1; then
        export PATH="$HOME/.local/bin:$PATH"
        RESPONSE=$(awscurl --service s3 --region "${S3_REGION}" \
            --access_key "${S3_ACCESS_KEY}" \
            --secret_key "${S3_SECRET_KEY}" \
            -X GET "http://${S3_HOST}:${S3_PORT}/" 2>&1)

        if echo "${RESPONSE}" | grep -q "<ListAllMyBucketsResult"; then
            # S3 API is ready and responding correctly
            return 0
        fi
        # If awscurl failed, check if it's a 503 (Service Unavailable) which means not ready
        if echo "${RESPONSE}" | grep -q "503\|Service not ready"; then
            return 1  # Not ready yet (readiness gate is blocking S3 API)
        fi
        # Authentication, server, and transport failures are not readiness.
        return 1
    fi

    # /health/ready is authoritative when the optional signed probe is absent.
    return 0
}

# First, wait for server to log "server started successfully"
log_info "Waiting for server startup completion..."
for i in {1..30}; do
    if check_server_ready_from_log; then
        log_info "Server startup complete detected in log"
        # Give it a moment for FullReady to be set (happens just before the log message)
        sleep 2
        break
    fi
    if [ $i -eq 30 ]; then
        log_warn "Server startup message not found in log after 30 attempts, continuing with API check..."
    fi
    sleep 1
done

# Now verify S3 API is actually responding
log_info "Verifying S3 API readiness..."
for i in {1..20}; do
    if test_s3_api_ready; then
        log_info "RustFS is fully ready (S3 API responding)"
        break
    fi

    if [ $i -eq 20 ]; then
        log_error "RustFS S3 API readiness check timed out"
        log_error "Checking service status..."

        # Check if server is still running
        if [ "${DEPLOY_MODE}" = "build" ] || [ "${DEPLOY_MODE}" = "binary" ]; then
            if [ -n "${RUSTFS_PID}" ] && ! kill -0 "${RUSTFS_PID}" 2>/dev/null; then
                log_error "RustFS process is not running (PID: ${RUSTFS_PID})"
                if [ -f "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" ]; then
                    log_error "Last 50 lines of RustFS log:"
                    tail -50 "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log"
                fi
                exit 1
            fi
        fi

        # Show last test attempt with detailed diagnostics
        log_error "Last S3 API readiness test diagnostics:"

        # Test /health endpoint (probe path, bypasses readiness gate)
        log_error "Step 1: Testing /health endpoint (probe path):"
        HEALTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
            -X GET \
            "http://${S3_HOST}:${S3_PORT}/health" \
            --max-time 5 2>&1 || echo "000")
        log_error "  /health HTTP status code: ${HEALTH_CODE}"
        if [ "${HEALTH_CODE}" != "200" ]; then
            log_error "  /health endpoint response:"
            curl -s "http://${S3_HOST}:${S3_PORT}/health" 2>&1 | head -5 || true
        fi

        # Test S3 API with signed request if awscurl is available
        if command -v awscurl >/dev/null 2>&1; then
            export PATH="$HOME/.local/bin:$PATH"
            log_error "Step 2: Testing S3 API with awscurl (signed request):"
            awscurl --service s3 --region "${S3_REGION}" \
                --access_key "${S3_ACCESS_KEY}" \
                --secret_key "${S3_SECRET_KEY}" \
                -X GET "http://${S3_HOST}:${S3_PORT}/" 2>&1 | head -20
        else
            log_error "Step 2: Testing S3 API root path (unsigned HEAD request):"
            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
                -X HEAD \
                "http://${S3_HOST}:${S3_PORT}/" \
                --max-time 5 2>&1 || echo "000")
            log_error "  Root path HTTP status code: ${HTTP_CODE}"
            if [ "${HTTP_CODE}" = "503" ]; then
                log_error "  Note: 503 indicates readiness gate is blocking (service not ready)"
            elif [ "${HTTP_CODE}" = "000" ]; then
                log_error "  Note: 000 indicates connection failure"
            fi
        fi

        # Output logs based on deployment mode
        if [ "${DEPLOY_MODE}" = "docker" ]; then
            docker logs "${CONTAINER_NAME}" 2>&1 | tail -50
        elif [ "${DEPLOY_MODE}" = "build" ] || [ "${DEPLOY_MODE}" = "binary" ]; then
            if [ -f "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" ]; then
                log_error "Last 50 lines of RustFS log:"
                tail -50 "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log"
            fi
        fi
        exit 1
    fi

    sleep 1
done

# Step 6: Generate s3tests config
log_info "Generating s3tests config..."
mkdir -p "${ARTIFACTS_DIR}"

# Resolve template and output paths (relative to PROJECT_ROOT)
if [[ "${S3TESTS_CONF_TEMPLATE}" = /* ]]; then
    TEMPLATE_PATH="${S3TESTS_CONF_TEMPLATE}"
else
    TEMPLATE_PATH="${PROJECT_ROOT}/${S3TESTS_CONF_TEMPLATE}"
fi
if [[ "${S3TESTS_CONF}" = /* ]]; then
    CONF_OUTPUT_PATH="${S3TESTS_CONF}"
else
    CONF_OUTPUT_PATH="${PROJECT_ROOT}/${S3TESTS_CONF}"
fi

# Check if template exists
if [ ! -f "${TEMPLATE_PATH}" ]; then
    log_error "S3tests config template not found: ${TEMPLATE_PATH}"
    log_error "Please specify S3TESTS_CONF_TEMPLATE environment variable"
    exit 1
fi

# Install gettext for envsubst if not available
if ! command -v envsubst >/dev/null 2>&1; then
    log_info "Installing gettext-base for envsubst..."
    if command -v apt-get >/dev/null 2>&1; then
        sudo apt-get update && sudo apt-get install -y gettext-base || {
            log_warn "Failed to install gettext-base, trying alternative method"
        }
    elif command -v brew >/dev/null 2>&1; then
        brew install gettext || {
            log_warn "Failed to install gettext via brew"
        }
    fi
fi

log_info "Using template: ${TEMPLATE_PATH}"
log_info "Generating config: ${CONF_OUTPUT_PATH}"
mkdir -p "$(dirname "${CONF_OUTPUT_PATH}")"

# Export all required variables for envsubst
export S3_HOST S3_PORT S3_ACCESS_KEY S3_SECRET_KEY S3_ALT_ACCESS_KEY S3_ALT_SECRET_KEY
envsubst < "${TEMPLATE_PATH}" > "${CONF_OUTPUT_PATH}" || {
    log_error "Failed to generate s3tests config"
    exit 1
}

# Step 7: Provision s3-tests alt user
# Note: Main user (rustfsadmin) is a system user and doesn't need to be created via API
log_info "Provisioning s3-tests alt user..."

# Helper function to install Python packages with fallback for externally-managed environments
ensure_python_pip() {
    if python3 -m pip --version >/dev/null 2>&1; then
        return 0
    fi

    log_info "Installing pip for Python package setup..."
    if python3 -m ensurepip --upgrade >/dev/null 2>&1; then
        return 0
    fi

    if command -v apt-get >/dev/null 2>&1; then
        sudo apt-get update && sudo apt-get install -y python3-pip || {
            log_warn "Failed to install python3-pip via apt-get"
        }
    elif command -v brew >/dev/null 2>&1; then
        brew install python || {
            log_warn "Failed to install Python via brew"
        }
    fi

    python3 -m pip --version >/dev/null 2>&1
}

install_python_package() {
    local package=$1
    local error_output

    if command -v uv >/dev/null 2>&1; then
        if uv tool install --upgrade "${package}"; then
            UV_TOOL_BIN="$(uv tool dir --bin 2>/dev/null || true)"
            if [ -n "${UV_TOOL_BIN}" ]; then
                export PATH="${UV_TOOL_BIN}:$PATH"
            fi
            return 0
        fi
        log_warn "Failed to install ${package} with uv, falling back to python3 -m pip"
    fi

    ensure_python_pip || {
        log_error "python3 -m pip is unavailable"
        return 1
    }

    # Try --user first (works on most Linux systems)
    error_output=$(python3 -m pip install --user --upgrade pip "${package}" 2>&1)
    if [ $? -eq 0 ]; then
        return 0
    fi

    # If that fails with externally-managed-environment error, try with --break-system-packages
    if echo "${error_output}" | grep -q "externally-managed-environment"; then
        log_warn "Detected externally-managed Python environment, using --break-system-packages flag"
        python3 -m pip install --user --break-system-packages --upgrade pip "${package}" || {
            log_error "Failed to install ${package} even with --break-system-packages"
            return 1
        }
        return 0
    fi

    # Other errors - show the error output
    log_error "Failed to install ${package}: ${error_output}"
    return 1
}

if ! command -v awscurl >/dev/null 2>&1; then
    install_python_package awscurl || {
        log_error "Failed to install awscurl"
        exit 1
    }
    # Add common Python user bin directories to PATH
    # macOS: ~/Library/Python/X.Y/bin
    # Linux: ~/.local/bin
    PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null || echo "3.14")
    export PATH="$HOME/Library/Python/${PYTHON_VERSION}/bin:$HOME/.local/bin:$PATH"
    # Verify awscurl is now available
    if ! command -v awscurl >/dev/null 2>&1; then
        log_error "awscurl installed but not found in PATH. Tried:"
        log_error "  - $HOME/Library/Python/${PYTHON_VERSION}/bin"
        log_error "  - $HOME/.local/bin"
        log_error "Please ensure awscurl is in your PATH"
        exit 1
    fi
fi

# Provision alt user (required by suite)
log_info "Provisioning alt user (${S3_ALT_ACCESS_KEY})..."
awscurl \
    --service s3 \
    --region "${S3_REGION}" \
    --access_key "${S3_ACCESS_KEY}" \
    --secret_key "${S3_SECRET_KEY}" \
    -X PUT \
    -H 'Content-Type: application/json' \
    -d "{\"secretKey\":\"${S3_ALT_SECRET_KEY}\",\"status\":\"enabled\",\"policy\":\"readwrite\"}" \
    "http://${S3_HOST}:${S3_PORT}/rustfs/admin/v3/add-user?accessKey=${S3_ALT_ACCESS_KEY}" || {
    log_error "Failed to add alt user"
    exit 1
}

# Explicitly attach built-in policy via policy mapping
awscurl \
    --service s3 \
    --region "${S3_REGION}" \
    --access_key "${S3_ACCESS_KEY}" \
    --secret_key "${S3_SECRET_KEY}" \
    -X PUT \
    "http://${S3_HOST}:${S3_PORT}/rustfs/admin/v3/set-user-or-group-policy?policyName=readwrite&userOrGroup=${S3_ALT_ACCESS_KEY}&isGroup=false" || {
    log_error "Failed to set user policy"
    exit 1
}

# Sanity check: alt user can list buckets
awscurl \
    --service s3 \
    --region "${S3_REGION}" \
    --access_key "${S3_ALT_ACCESS_KEY}" \
    --secret_key "${S3_ALT_SECRET_KEY}" \
    -X GET \
    "http://${S3_HOST}:${S3_PORT}/" >/dev/null || {
    log_error "Alt user cannot list buckets"
    exit 1
}

log_info "Alt user provisioned successfully"

# Step 8: Prepare s3-tests, pinned to S3TESTS_REV for reproducible runs
log_info "Preparing s3-tests at ${S3TESTS_REV}..."
if [ ! -d "${PROJECT_ROOT}/s3-tests/.git" ]; then
    git init -q "${PROJECT_ROOT}/s3-tests"
    git -C "${PROJECT_ROOT}/s3-tests" remote add origin "${S3TESTS_REPO}"
fi
if ! git -C "${PROJECT_ROOT}/s3-tests" cat-file -e "${S3TESTS_REV}^{commit}" 2>/dev/null; then
    git -C "${PROJECT_ROOT}/s3-tests" fetch --depth 1 origin "${S3TESTS_REV}" || {
        log_error "Failed to fetch s3-tests revision ${S3TESTS_REV} from ${S3TESTS_REPO}"
        exit 1
    }
fi
# -f discards local edits left by previous runs (e.g. pytest-xdist appended to requirements.txt)
git -C "${PROJECT_ROOT}/s3-tests" checkout -qf --detach "${S3TESTS_REV}" || {
    log_error "Failed to checkout s3-tests revision ${S3TESTS_REV}"
    exit 1
}

S3TESTS_PATCH_DIR="${SCRIPT_DIR}/patches"
if [ -d "${S3TESTS_PATCH_DIR}" ]; then
    for patch_file in "${S3TESTS_PATCH_DIR}"/*.patch; do
        [ -e "${patch_file}" ] || continue
        log_info "Applying s3-tests patch: ${patch_file##*/}"
        git -C "${PROJECT_ROOT}/s3-tests" apply "${patch_file}" || {
            log_error "Failed to apply s3-tests patch: ${patch_file}"
            exit 1
        }
    done
fi

cd "${PROJECT_ROOT}/s3-tests"

# Install tox if not available
if ! command -v tox >/dev/null 2>&1; then
    install_python_package tox || {
        log_error "Failed to install tox"
        exit 1
    }
    # Add common Python user bin directories to PATH (same as awscurl)
    PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null || echo "3.14")
    export PATH="$HOME/Library/Python/${PYTHON_VERSION}/bin:$HOME/.local/bin:$PATH"
fi

# Step 9: Run ceph s3-tests
log_info "Running ceph s3-tests..."
mkdir -p "${ARTIFACTS_DIR}"

XDIST_ARGS=""
if [ "${XDIST}" != "0" ]; then
    # Add pytest-xdist to requirements.txt so tox installs it inside its virtualenv
    grep -qxF "pytest-xdist" requirements.txt || echo "pytest-xdist" >> requirements.txt
    XDIST_ARGS="-n ${XDIST} --dist=loadgroup"
fi
grep -qxF "pytest-timeout" requirements.txt || echo "pytest-timeout" >> requirements.txt

# Resolve config path (absolute path for tox)
if [[ "${S3TESTS_CONF}" = /* ]]; then
    CONF_OUTPUT_PATH="${S3TESTS_CONF}"
else
    CONF_OUTPUT_PATH="${PROJECT_ROOT}/${S3TESTS_CONF}"
fi

PYTEST_SELECTION_ARGS=()
if [[ "${USE_FILE_TEST_NODES}" == "true" ]]; then
    PYTEST_SELECTION_ARGS=("${TEST_NODE_ARGS[@]}")
elif [[ -n "${TESTEXPR}" ]]; then
    PYTEST_SELECTION_ARGS=("${S3_TEST_FILE}" -k "${TESTEXPR}")
else
    # TEST_SCOPE=all: the whole upstream suite
    PYTEST_SELECTION_ARGS=("${S3_TEST_FILE}")
fi

collect_nodeids() {
    local output_path="$1"
    shift
    local collect_log="${output_path%.txt}.log"
    local collect_rc=0
    local node_prefix="${S3_TEST_FILE//./\\.}::"

    set +e
    S3TEST_CONF="${CONF_OUTPUT_PATH}" tox -- -q --collect-only "$@" 2>&1 | tee "${collect_log}"
    collect_rc=${PIPESTATUS[0]}
    set -e
    if [ "${collect_rc}" -ne 0 ]; then
        log_error "pytest collection failed with exit code ${collect_rc}"
        return "${collect_rc}"
    fi
    grep -E "^${node_prefix}" "${collect_log}" > "${output_path}" || true
    if [ ! -s "${output_path}" ]; then
        log_error "pytest collection produced no S3 test node IDs"
        return 1
    fi
}

ALL_COLLECTED_NODEIDS="${ARTIFACTS_DIR}/all-collected-nodeids.txt"
UNSHARDED_SELECTED_NODEIDS="${ARTIFACTS_DIR}/unsharded-selected-nodeids.txt"
SELECTED_NODEIDS="${ARTIFACTS_DIR}/selected-nodeids.txt"
collect_nodeids "${ALL_COLLECTED_NODEIDS}" "${S3_TEST_FILE}" -m "not rustfs_never_marker"
python3 "${SCRIPT_DIR}/report_compat.py" \
    --lists-dir "${SCRIPT_DIR}" \
    --collected-nodeids "${ALL_COLLECTED_NODEIDS}" \
    --check-classifications-only || {
    log_error "S3 test classifications do not match pinned revision ${S3TESTS_REV}"
    exit 1
}
if [[ "${TEST_SCOPE}" == "all" && -z "${TESTEXPR}" && "${MARKEXPR}" == "not rustfs_never_marker" ]]; then
    cp "${ALL_COLLECTED_NODEIDS}" "${UNSHARDED_SELECTED_NODEIDS}"
else
    collect_nodeids "${UNSHARDED_SELECTED_NODEIDS}" "${PYTEST_SELECTION_ARGS[@]}" -m "${MARKEXPR}"
fi

if (( S3_SHARD_COUNT > 1 )); then
    awk -v count="${S3_SHARD_COUNT}" -v shard_index="${S3_SHARD_INDEX}" \
        '((NR - 1) % count) == shard_index' \
        "${UNSHARDED_SELECTED_NODEIDS}" > "${SELECTED_NODEIDS}"
    if [[ ! -s "${SELECTED_NODEIDS}" ]]; then
        log_error "Shard ${S3_SHARD_INDEX}/${S3_SHARD_COUNT} selected no tests"
        exit 1
    fi
    PYTEST_SELECTION_ARGS=()
    while IFS= read -r nodeid; do
        PYTEST_SELECTION_ARGS+=("${nodeid}")
    done < "${SELECTED_NODEIDS}"
    log_info "Selected shard ${S3_SHARD_INDEX}/${S3_SHARD_COUNT}: ${#PYTEST_SELECTION_ARGS[@]} exact cases"
else
    cp "${UNSHARDED_SELECTED_NODEIDS}" "${SELECTED_NODEIDS}"
fi

# Run tests from s3tests/functional
# Failure locals can contain multi-MiB request bodies; keep tracebacks without expanding local values.
set +e
S3TEST_CONF="${CONF_OUTPUT_PATH}" \
    tox -- \
    -vv -ra --tb=long \
    --maxfail="${MAXFAIL}" \
    --timeout="${TEST_TIMEOUT}" \
    --junitxml="${ARTIFACTS_DIR}/junit.xml" \
    ${XDIST_ARGS} \
    "${PYTEST_SELECTION_ARGS[@]}" \
    -m "${MARKEXPR}" \
    2>&1 | tee "${ARTIFACTS_DIR}/pytest.log"

TEST_EXIT_CODE=${PIPESTATUS[0]}
set -e

# Step 10: Collect RustFS logs
log_info "Collecting RustFS logs..."
mkdir -p "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}"

if [ "${DEPLOY_MODE}" = "docker" ]; then
    docker logs "${CONTAINER_NAME}" > "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log" 2>&1 || true
    docker inspect "${CONTAINER_NAME}" > "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/inspect.json" || true
elif [ "${DEPLOY_MODE}" = "build" ] || [ "${DEPLOY_MODE}" = "binary" ]; then
    # Logs are already being written to file, just copy metadata
    echo "{\"pid\": ${RUSTFS_PID}, \"binary\": \"${RUSTFS_BINARY}\", \"mode\": \"binary\"}" > "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/inspect.json" || true
elif [ "${DEPLOY_MODE}" = "existing" ]; then
    log_info "Skipping log collection for existing service"
    echo "{\"host\": \"${S3_HOST}\", \"port\": ${S3_PORT}, \"mode\": \"existing\"}" > "${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/inspect.json" || true
fi

# Step 11: Classification report and gate
REPORT_SCRIPT="${SCRIPT_DIR}/report_compat.py"
REPORT_ARGS=(
    --junit "${ARTIFACTS_DIR}/junit.xml"
    --lists-dir "${SCRIPT_DIR}"
    --collected-nodeids "${SELECTED_NODEIDS}"
    --output "${ARTIFACTS_DIR}/compat-report.md"
    --fail-on-regression
)
if [[ "${TEST_SCOPE}" == "all" ]]; then
    REPORT_ARGS+=(--fail-on-unclassified)
fi
set +e
python3 "${REPORT_SCRIPT}" "${REPORT_ARGS[@]}"
REPORT_EXIT_CODE=$?
set -e

# Summary
if [ ${TEST_EXIT_CODE} -eq 0 ]; then
    log_info "Tests completed successfully!"
    log_info "Results: ${ARTIFACTS_DIR}/junit.xml"
    log_info "Logs: ${ARTIFACTS_DIR}/pytest.log"
else
    log_error "Tests failed with exit code ${TEST_EXIT_CODE}"
    log_info "Check results: ${ARTIFACTS_DIR}/junit.xml"
    log_info "Check logs: ${ARTIFACTS_DIR}/pytest.log"
    log_info "Check RustFS logs: ${ARTIFACTS_DIR}/rustfs-${TEST_MODE}/rustfs.log"
fi

if [[ "${TEST_EXIT_CODE}" -ne 0 && "${TEST_EXIT_CODE}" -ne 1 ]]; then
    exit "${TEST_EXIT_CODE}"
fi
if [[ "${TEST_SCOPE}" == "implemented" && "${TEST_EXIT_CODE}" -ne 0 ]]; then
    exit "${TEST_EXIT_CODE}"
fi
exit "${REPORT_EXIT_CODE}"
