#!/usr/bin/env bash
# Run the whole test suite and report pass/fail. Exit 0 only if every test passes.
#
# The tests are standalone scripts (not pytest): each loads the `epsiloneridani` program and exits 0/1.
# This runner knows how each one wants to be invoked, and which one to skip in a plain CI box:
#   - most are stdlib-only         -> `uv run python <file>`
#   - parity_selectors needs a snapshot, not the network -> pass the bundled fixture as its arg
#   - dashboard has a PEP 723 header (rich/textual)      -> `uv run --script`
#   - docker_entrypoint/lifecycle are bash; lifecycle resolves its own python3
#   - egress needs the bubble sandbox/proxy              -> skipped here
# The Python version is taken from $UV_PYTHON when set (uv honours it), so CI can matrix over versions.
#
# Usage: tests/run-all [--fail-fast]
set -uo pipefail
cd "$(dirname "$0")/.." || exit 1

FAIL_FAST=0
[ "${1:-}" = "--fail-fast" ] && FAIL_FAST=1

# stdlib-only python tests, run with the uv-provided interpreter
RUN_STDLIB=(
  agent_cmds.py
  agent_transcript.py
  auto_stage_priority.py
  authoring_profiles.py
  bubble_cache.py
  bubble_version.py
  branch_claim_repo.py
  build_status_rollup.py
  claude_config_dir.py
  claude_keychain.py
  claims_repo.py
  codex_account.py
  codex_fallback.py
  contest_claim.py
  dispatch_revalidation.py
  fix_diagnostic.py
  fork_authoring.py
  github_failure_reason.py
  github_ratelimit.py
  host_agent_preflight.py
  ignore_quota_hardblock.py
  infra_failure_budget.py
  inflight_review.py
  isolate_home_macos.py
  isolate_home_paths.py
  kiro.py
  ledger_blocking.py
  loop_sigterm.py
  mirror_creds.py
  mirror_creds_macos.py
  oauth_refresh_loop.py
  open_pr_pages.py
  pace_curve.py
  pr_targeting.py
  preflight_review_lake.py
  progress_tool_failure_log.py
  prompt_wrapper_paths.py
  provider_usage.py
  quota_auto_refresh.py
  quota_cache_fp.py
  quota_loop_refresh.py
  rebase_handoff.py
  review_sync_failure.py
  quota_claude_bootstrap.py
  quota_claude_windows.py
  quota_launch_stage.py
  quota_codex_windows.py
  quota_reason.py
  review_codex_model.py
  review_daily_cap.py
  review_diagnostics.py
  review_provider_down.py
  review_scheduling.py
  review_state_freshness.py
  review_throttles.py
  roadmap_backpressure.py
  roadmap_claims.py
  round_file_changes.py
  round_group_sweep.py
  rubric_bundle.py
  safe_exists.py
  scoreboard_trust.py
  shared_build_caches.py
  source_argument.py
  spread_candidates.py
  ssl_cert_file.py
  status_labels.py
  tend_bot_permissions.py
  worker_claude_config.py
  worker_manager.py
)
# special-cased python tests (handled explicitly below)
RUN_SPECIAL=(parity_selectors.py dashboard.py)
# bash tests
RUN_SH=(docker_entrypoint.sh lifecycle.sh)
# deliberately not run here (needs the bubble sandbox/proxy)
SKIP=(egress.sh)

pass=0; fail=0; failed=()

# run <label> <cmd...> : run a test, tally the result, honour --fail-fast
run() {
  local label="$1"; shift
  echo "== $label =="
  if "$@"; then
    echo "  [PASS] $label"; pass=$((pass + 1))
  else
    echo "  [FAIL] $label (rc=$?)"; fail=$((fail + 1)); failed+=("$label")
    [ "$FAIL_FAST" = 1 ] && { summary; exit 1; }
  fi
  echo
}

summary() {
  echo "================================================================"
  echo "run-all: $pass passed, $fail failed"
  ((${#failed[@]})) && printf '  failed: %s\n' "${failed[@]}"
  ((${#SKIP[@]}))   && printf '  skipped (needs sandbox): %s\n' "${SKIP[@]}"
}

# --- inventory guard: every tests/*.py and tests/*.sh must be classified --------------------------
# Catches a newly-added test that nobody wired in here: better a loud CI failure than a silent skip.
known=(" ${RUN_STDLIB[*]} ${RUN_SPECIAL[*]} ${RUN_SH[*]} ${SKIP[*]} ")
unclassified=()
for f in tests/*.py tests/*.sh; do
  base=$(basename "$f")
  [ "$base" = "run-all" ] && continue
  [[ "${known[*]}" == *" $base "* ]] || unclassified+=("$base")
done
if ((${#unclassified[@]})); then
  echo "ERROR: test file(s) not classified in tests/run-all (add to a RUN_* or SKIP list):" >&2
  printf '  - %s\n' "${unclassified[@]}" >&2
  exit 2
fi

# --- run ------------------------------------------------------------------------------------------
for t in "${RUN_STDLIB[@]}"; do
  run "$t" uv run python "tests/$t"
done

run parity_selectors.py uv run python tests/parity_selectors.py tests/fixtures/pr_selectors.json
run dashboard.py uv run --script tests/dashboard.py

# lifecycle.sh resolves its own python; hand it the uv-selected interpreter so the matrix
# actually covers it (otherwise it would always use the runner's default python3).
PY="$(uv run python -c 'import sys; print(sys.executable)')"
export PY
for t in "${RUN_SH[@]}"; do
  run "$t" bash "tests/$t"
done

summary
[ "$fail" -eq 0 ]
