#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "$SCRIPT_DIR/common.sh"
CALLER=test_all.sh
usage() {
cat <<EOF
Usage: ./tests/test_all.sh [OPTIONS]
Run the project test suite from the repository root.
Options:
--no-cargo Skip Cargo tests
--no-e2e Skip e2e/interface tests
--suite NAME Limit to a component (repeatable). NAME is one of:
rbs, rbc, tools — Cargo + e2e for that area
rbs-e2e, rbc-e2e, tools-e2e — e2e only (no Cargo)
-suite NAME Same as --suite
--testcase NAME E2e only: test-name substring token(s), comma-separated OR. Requires --suite.
-h, --help Show this help and exit
Examples:
./tests/test_all.sh
./tests/test_all.sh --no-cargo
./tests/test_all.sh --suite rbs
./tests/test_all.sh -suite rbs-e2e --testcase version,other
Environment variables (default: both enabled):
ENABLE_CARGO_TESTS=\${ENABLE_CARGO_TESTS:-1}
ENABLE_E2E_TESTS=\${ENABLE_E2E_TESTS:-1}
E2E_AUTO_SETUP=\${E2E_AUTO_SETUP:-1} # initialize Python e2e dependencies automatically
E2E_VENV_DIR=\${E2E_VENV_DIR:-tests/.venv} # auto-created Python environment
EOF
}
assert_openapi_yaml_matches_build() {
echo ""
echo "=== OpenAPI: docs/proto/rbs_rest_api.yaml matches rbs build output ==="
local before_yaml
before_yaml="$(mktemp)"
cp "$REPO_ROOT/docs/proto/rbs_rest_api.yaml" "$before_yaml"
cargo build -p rbs --features rest -q
if ! cmp -s "$before_yaml" "$REPO_ROOT/docs/proto/rbs_rest_api.yaml"; then
echo "error: docs/proto/rbs_rest_api.yaml differs from \`cargo build -p rbs --features rest\`." >&2
echo "Regenerate: cargo build -p rbs --features rest" >&2
diff -u "$before_yaml" "$REPO_ROOT/docs/proto/rbs_rest_api.yaml" >&2 || true
rm -f "$before_yaml"
exit 1
fi
rm -f "$before_yaml"
echo "OpenAPI YAML is in sync with the rbs crate build."
}
apply_suite_token() {
local token=$1
case "$token" in
rbs)
array_push_unique cargo_packages rbs
array_push_unique cargo_packages rbs-core
array_push_unique cargo_packages rbs-rest
array_push_unique cargo_packages rbs-api-types
array_push_unique e2e_suites rbs
;;
rbc)
array_push_unique cargo_packages rbc
array_push_unique e2e_suites rbc
;;
tools)
array_push_unique cargo_packages rbs-cli
array_push_unique cargo_packages rbs-admin-client
array_push_unique e2e_suites tools
;;
rbs-e2e) array_push_unique e2e_suites rbs ;;
rbc-e2e) array_push_unique e2e_suites rbc ;;
tools-e2e) array_push_unique e2e_suites tools ;;
*)
echo "test_all.sh: unknown suite '$token' (expected rbs, rbc, tools, rbs-e2e, rbc-e2e, tools-e2e)" >&2
exit 1
;;
esac
}
run_cargo_section() {
local label=$1
local openapi_check=$2
shift 2
local -a cargo_args=("$@")
if [[ "$ENABLE_CARGO_TESTS" != "1" ]]; then
echo "=== Cargo tests SKIPPED (ENABLE_CARGO_TESTS=$ENABLE_CARGO_TESTS) ==="
if [[ "$openapi_check" -eq 1 ]]; then
echo "(OpenAPI YAML drift check also skipped; requires Cargo build)"
fi
return 0
fi
if [[ ${#cargo_args[@]} -eq 0 ]]; then
echo "=== Cargo tests SKIPPED (no Cargo packages for selected suite(s)) ==="
return 0
fi
if [[ "$label" == "workspace" ]]; then
echo "=== Cargo tests (workspace) ==="
else
echo "=== Cargo tests (selected packages) ==="
fi
cargo test "${cargo_args[@]}"
if [[ "$openapi_check" -eq 1 ]]; then
assert_openapi_yaml_matches_build
fi
}
run_e2e_section() {
local suites_name=$1 patterns_name=$2 empty_policy=${3:-skip}
if [[ "$ENABLE_E2E_TESTS" != "1" ]]; then
echo "=== E2e / interface tests SKIPPED (ENABLE_E2E_TESTS=$ENABLE_E2E_TESTS) ==="
return 0
fi
local -n _suites=$suites_name
if [[ ${#_suites[@]} -eq 0 && "$empty_policy" == "fail" ]]; then
echo "=== E2e / interface tests SKIPPED (no e2e suite in selection) ==="
return 0
fi
echo ""
run_e2e_pytest "$suites_name" "$patterns_name" "$empty_policy"
}
main() {
cd "$REPO_ROOT"
ENABLE_CARGO_TESTS="${ENABLE_CARGO_TESTS:-1}"
ENABLE_E2E_TESTS="${ENABLE_E2E_TESTS:-1}"
local suites=() e2e_patterns=()
local cargo_packages=() e2e_suites=()
while [[ "${1-}" != "" ]]; do
case "$1" in
--no-cargo) ENABLE_CARGO_TESTS="0" ;;
--no-e2e) ENABLE_E2E_TESTS="0" ;;
--suite|-suite)
shift
[[ -n "${1-}" ]] || { echo "test_all.sh: missing value for --suite" >&2; usage >&2; exit 1; }
suites+=("$1")
;;
--testcase)
shift
[[ -n "${1-}" ]] || { echo "test_all.sh: missing value for --testcase" >&2; usage >&2; exit 1; }
append_e2e_patterns_from_csv e2e_patterns "$1"
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 1
;;
esac
shift
done
if [[ ${#e2e_patterns[@]} -gt 0 && ${#suites[@]} -eq 0 ]]; then
echo "test_all.sh: --testcase requires at least one --suite" >&2
exit 1
fi
if [[ "$ENABLE_CARGO_TESTS" != "1" && "$ENABLE_E2E_TESTS" != "1" ]]; then
echo "No test sections enabled; nothing to run."
exit 0
fi
if [[ ${#suites[@]} -eq 0 ]]; then
run_cargo_section workspace 1 --workspace
run_e2e_section e2e_suites e2e_patterns skip
else
local s
for s in "${suites[@]}"; do
apply_suite_token "$s"
done
local -a cargo_args=()
local p openapi_check=0
for p in "${cargo_packages[@]}"; do
cargo_args+=(-p "$p")
done
if e2e_suites_include_rbs "${suites[@]}"; then
openapi_check=1
fi
run_cargo_section selected "$openapi_check" "${cargo_args[@]}"
local e2e_empty_policy
e2e_empty_policy="$(resolve_test_all_e2e_empty_policy ${#e2e_patterns[@]} "${suites[@]}")"
run_e2e_section e2e_suites e2e_patterns "$e2e_empty_policy"
fi
echo ""
echo "=== test_all.sh completed successfully ==="
}
main "$@"