Files
tdc114plus/scripts/test_runtime_helpers.sh
T

442 lines
14 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
TMP_ROOT="$(mktemp -d)"
trap 'rm -rf "$TMP_ROOT"' EXIT
PASS_COUNT=0
say() {
printf '%s\n' "$*"
}
fail() {
printf 'FAIL %s\n' "$*" >&2
exit 1
}
assert_file_contains() {
local file="$1"
local pattern="$2"
if ! grep -Fq "$pattern" "$file"; then
fail "expected '$pattern' in $file"
fi
}
assert_not_exists_or_empty() {
local file="$1"
if [ -e "$file" ] && [ -s "$file" ]; then
fail "expected empty or missing file: $file"
fi
}
new_fixture() {
local name="$1"
local dir="$TMP_ROOT/$name"
mkdir -p "$dir/bin" "$dir/baron/config/.generated/ory/kratos" "$dir/baron/config/.generated/ory/oathkeeper" "$dir/logs" "$dir/state"
touch "$dir/baron/Makefile"
cat > "$dir/baron/config/.generated/ory/kratos/kratos.yml" <<'EOF'
selfservice:
allowed_return_urls:
- http://localhost:5000
- []
EOF
cat > "$dir/baron/config/.generated/ory/oathkeeper/entrypoint.sh" <<'EOF'
#!/usr/bin/env sh
exit 0
EOF
cat > "$dir/bin/docker" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
log_file="${TEST_CMD_LOG:?}"
printf 'docker %s\n' "$*" >> "$log_file"
case "$*" in
"compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml up -d")
if [ "${TEST_DOCKER_UP_FAIL_ONCE:-0}" = "1" ] && [ ! -f "${TEST_STATE_DIR:?}/docker-up.failed" ]; then
touch "${TEST_STATE_DIR}/docker-up.failed"
echo "name conflict"
exit 1
fi
if [ "${TEST_DOCKER_UP_HEALTH_FAILURE:-0}" = "1" ] && [ ! -f "${TEST_STATE_DIR:?}/docker-up.health-failed" ]; then
touch "${TEST_STATE_DIR}/docker-up.health-failed"
echo "dependency failed to start: container baron_backend is unhealthy"
exit 1
fi
echo "compose up ok"
;;
"compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml down")
echo "compose down ok"
;;
"compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml logs --no-color")
echo "fake compose logs"
;;
"ps --format {{.Names}} {{.Status}}")
printf 'baron_backend Up (healthy)\nory_kratos Up\n'
;;
"ps -a --format {{.Names}}")
printf 'old_baron_backend\nold_ory_kratos\n'
;;
"ps --format {{.Names}}")
printf 'baron_backend\nbaron_gateway\n'
;;
"rm -f old_baron_backend old_ory_kratos")
echo "removed stale containers"
;;
*)
;;
esac
EOF
cat > "$dir/bin/make" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'make %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
cat > "$dir/bin/adb" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'adb %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
cat > "$dir/bin/pkill" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'pkill %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
cat > "$dir/bin/chmod" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'chmod %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
cat > "$dir/bin/chown" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'chown %s\n' "$*" >> "${TEST_CMD_LOG:?}"
EOF
cat > "$dir/check.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
state_dir="${TEST_STATE_DIR:?}"
attempt_file="$state_dir/check-attempts"
count=0
if [ -f "$attempt_file" ]; then
count="$(cat "$attempt_file")"
fi
count=$((count + 1))
printf '%s' "$count" > "$attempt_file"
mode="${CHECK_MODE:-pass}"
case "$mode" in
pass)
echo "RESULT ready-ish: 0 failure(s), 0 warning(s)"
;;
warn_then_pass)
if [ "$count" -eq 1 ]; then
echo "RESULT ready-ish: 0 failure(s), 1 warning(s)"
else
echo "RESULT ready-ish: 0 failure(s), 0 warning(s)"
fi
;;
fail)
echo "RESULT blocked: 1 failure(s), 0 warning(s)"
exit 1
;;
*)
echo "unknown CHECK_MODE=$mode" >&2
exit 2
;;
esac
EOF
cat > "$dir/api-smoke.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
state_dir="${TEST_STATE_DIR:?}"
attempt_file="$state_dir/api-attempts"
count=0
if [ -f "$attempt_file" ]; then
count="$(cat "$attempt_file")"
fi
count=$((count + 1))
printf '%s' "$count" > "$attempt_file"
mode="${API_MODE:-pass}"
case "$mode" in
pass)
echo "PASS api smoke"
;;
login_failed)
if [ "${TDC114_SKIP_AUTH_SMOKE:-false}" = "true" ]; then
echo "SKIP authenticated smoke: TDC114_SKIP_AUTH_SMOKE=true"
exit 0
fi
echo 'FAIL phone-login success: HTTP 401' >&2
echo 'Response body:' >&2
echo '{"error":"Login failed","code":"login_failed"}' >&2
exit 1
;;
fail)
echo "FAIL api smoke" >&2
exit 1
;;
*)
echo "unknown API_MODE=$mode" >&2
exit 2
;;
esac
EOF
cat > "$dir/wsl-maintenance.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
mode="${1:-status}"
case "$mode" in
status)
echo "${TEST_WSL_MAINTENANCE_OUTPUT:-WSL maintenance reminder: OK}"
;;
*)
echo "unsupported mode: $mode" >&2
exit 2
;;
esac
EOF
chmod +x "$dir/bin/docker" "$dir/bin/make" "$dir/bin/adb" "$dir/bin/pkill" \
"$dir/bin/chmod" "$dir/bin/chown" "$dir/check.sh" "$dir/api-smoke.sh" \
"$dir/wsl-maintenance.sh" "$dir/baron/config/.generated/ory/oathkeeper/entrypoint.sh"
printf '%s\n' "$dir"
}
run_case() {
local name="$1"
shift
say "CASE $name"
"$@"
PASS_COUNT=$((PASS_COUNT + 1))
}
test_startup_dry_run() {
local fixture
fixture="$(new_fixture startup-dry-run)"
local cmd_log="$fixture/commands.log"
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_CHECK_SCRIPT="$fixture/check.sh" \
TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
TDC114_START_AUTH_SERVER=false \
DOCKER_BIN="$fixture/bin/docker" \
MAKE_BIN="$fixture/bin/make" \
bash "$ROOT_DIR/scripts/startup.sh" --dry-run --wait=0 --skip-android-check
assert_not_exists_or_empty "$cmd_log"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "WSL maintenance reminder: OK"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "Dry-run mode: no actions were actually performed"
}
test_startup_auto_retry_success() {
local fixture
fixture="$(new_fixture startup-auto-success)"
local cmd_log="$fixture/commands.log"
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
TEST_DOCKER_UP_FAIL_ONCE=1 \
CHECK_MODE=warn_then_pass \
API_MODE=pass \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_CHECK_SCRIPT="$fixture/check.sh" \
TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
TDC114_START_AUTH_SERVER=false \
DOCKER_BIN="$fixture/bin/docker" \
MAKE_BIN="$fixture/bin/make" \
bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check
assert_file_contains "$cmd_log" "docker rm -f old_baron_backend old_ory_kratos"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "check-baron-api-env.sh reported warnings on attempt 1"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "check-baron-api-env.sh passed with zero warnings"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke.sh passed"
if grep -Fq " - []" "$fixture/baron/config/.generated/ory/kratos/kratos.yml"; then
fail "expected startup to sanitize invalid empty allowed_return_urls entry"
fi
}
test_startup_auto_health_failure_tolerated() {
local fixture
fixture="$(new_fixture startup-auto-health-failure)"
local cmd_log="$fixture/commands.log"
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
TEST_DOCKER_UP_HEALTH_FAILURE=1 \
CHECK_MODE=pass \
API_MODE=pass \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_CHECK_SCRIPT="$fixture/check.sh" \
TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
TDC114_START_AUTH_SERVER=false \
DOCKER_BIN="$fixture/bin/docker" \
MAKE_BIN="$fixture/bin/make" \
bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "compose up reported dependency or health issues; continuing to stabilization checks before declaring failure"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke.sh passed"
}
test_startup_login_failed_falls_back_to_base_smoke() {
local fixture
fixture="$(new_fixture startup-login-fallback)"
local cmd_log="$fixture/commands.log"
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
CHECK_MODE=pass \
API_MODE=login_failed \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_CHECK_SCRIPT="$fixture/check.sh" \
TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
TDC114_START_AUTH_SERVER=false \
DOCKER_BIN="$fixture/bin/docker" \
MAKE_BIN="$fixture/bin/make" \
bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke authenticated login failed; retrying in base-smoke-only mode because TDC114_REQUIRE_AUTH_SMOKE is false"
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "api-smoke base-smoke-only mode passed"
}
test_startup_auto_failure() {
local fixture
fixture="$(new_fixture startup-auto-failure)"
local cmd_log="$fixture/commands.log"
set +e
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
CHECK_MODE=fail \
API_MODE=pass \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_CHECK_SCRIPT="$fixture/check.sh" \
TDC114_API_SMOKE_SCRIPT="$fixture/api-smoke.sh" \
TDC114_WSL_MAINTENANCE_SCRIPT="$fixture/wsl-maintenance.sh" \
TDC114_START_AUTH_SERVER=false \
DOCKER_BIN="$fixture/bin/docker" \
MAKE_BIN="$fixture/bin/make" \
bash "$ROOT_DIR/scripts/startup.sh" --auto --wait=0 --skip-android-check
local rc=$?
set -e
if [ "$rc" -eq 0 ]; then
fail "startup auto failure case should exit non-zero"
fi
assert_file_contains "$fixture/logs/$(date +%F)/startup.log" "Startup failed: health checks did not pass"
}
test_wsl_maintenance_schedule() {
local fixture
fixture="$(new_fixture wsl-maintenance-schedule)"
local state_dir="$fixture/state"
TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
TDC114_WSL_MAINTENANCE_ANCHOR_DATE=2026-07-16 \
TDC114_WSL_MAINTENANCE_TODAY=2026-07-20 \
bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" status > "$fixture/status-not-due.txt"
assert_file_contains "$fixture/status-not-due.txt" "WSL maintenance reminder: OK"
assert_file_contains "$fixture/status-not-due.txt" "Next due date: 2026-07-30"
TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
TDC114_WSL_MAINTENANCE_ANCHOR_DATE=2026-07-16 \
TDC114_WSL_MAINTENANCE_TODAY=2026-07-30 \
bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" status > "$fixture/status-due.txt"
assert_file_contains "$fixture/status-due.txt" "WSL maintenance reminder: DUE"
assert_file_contains "$fixture/status-due.txt" "./scripts/check-wsl-maintenance.sh record"
TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
TDC114_WSL_MAINTENANCE_TODAY=2026-07-30 \
bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" record > "$fixture/record.txt"
assert_file_contains "$fixture/record.txt" "Recorded WSL maintenance date: 2026-07-30"
TDC114_WSL_MAINTENANCE_STATE_DIR="$state_dir" \
TDC114_WSL_MAINTENANCE_ANCHOR_DATE=2026-07-16 \
TDC114_WSL_MAINTENANCE_TODAY=2026-08-05 \
bash "$ROOT_DIR/scripts/check-wsl-maintenance.sh" status > "$fixture/status-after-record.txt"
assert_file_contains "$fixture/status-after-record.txt" "WSL maintenance reminder: OK"
assert_file_contains "$fixture/status-after-record.txt" "Base date: 2026-07-30"
}
test_shutdown_dry_run() {
local fixture
fixture="$(new_fixture shutdown-dry-run)"
local cmd_log="$fixture/commands.log"
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_ADB_CONNECT_ADDRESS=172.21.128.1:5562 \
DOCKER_BIN="$fixture/bin/docker" \
ADB_BIN="$fixture/bin/adb" \
PKILL_BIN="$fixture/bin/pkill" \
CHMOD_BIN="$fixture/bin/chmod" \
CHOWN_BIN="$fixture/bin/chown" \
bash "$ROOT_DIR/scripts/shutdown.sh" --dry-run
assert_not_exists_or_empty "$cmd_log"
assert_file_contains "$fixture/logs/$(date +%F)/shutdown.log" "Dry-run mode: no shutdown actions were executed"
}
test_shutdown_auto_order_and_scope() {
local fixture
fixture="$(new_fixture shutdown-auto)"
local cmd_log="$fixture/commands.log"
TEST_CMD_LOG="$cmd_log" \
TEST_STATE_DIR="$fixture/state" \
BARON_SSO_WORKTREE="$fixture/baron" \
TDC114_LOG_BASE="$fixture/logs" \
TDC114_ADB_CONNECT_ADDRESS=172.21.128.1:5562 \
DOCKER_BIN="$fixture/bin/docker" \
ADB_BIN="$fixture/bin/adb" \
PKILL_BIN="$fixture/bin/pkill" \
CHMOD_BIN="$fixture/bin/chmod" \
CHOWN_BIN="$fixture/bin/chown" \
bash "$ROOT_DIR/scripts/shutdown.sh" --auto
assert_file_contains "$cmd_log" "pkill -f $ROOT_DIR/scripts/integration_tests.sh"
assert_file_contains "$cmd_log" "pkill -f $ROOT_DIR/scripts/flutter-docker.sh"
assert_file_contains "$cmd_log" "adb disconnect 172.21.128.1:5562"
assert_file_contains "$cmd_log" "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml logs --no-color"
assert_file_contains "$cmd_log" "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml down"
assert_file_contains "$cmd_log" "docker rm -f old_baron_backend old_ory_kratos"
assert_file_contains "$cmd_log" "chmod -R u+w config/.generated/"
assert_file_contains "$cmd_log" "chown -R"
local logs_line
local down_line
logs_line="$(grep -n "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml logs --no-color" "$cmd_log" | head -n1 | cut -d: -f1)"
down_line="$(grep -n "docker compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml down" "$cmd_log" | head -n1 | cut -d: -f1)"
if [ -z "$logs_line" ] || [ -z "$down_line" ] || [ "$logs_line" -ge "$down_line" ]; then
fail "expected compose logs collection before compose down"
fi
}
run_case "startup dry-run" test_startup_dry_run
run_case "startup auto retry success" test_startup_auto_retry_success
run_case "startup auto health failure tolerated" test_startup_auto_health_failure_tolerated
run_case "startup login failure falls back" test_startup_login_failed_falls_back_to_base_smoke
run_case "startup auto failure" test_startup_auto_failure
run_case "wsl maintenance schedule" test_wsl_maintenance_schedule
run_case "shutdown dry-run" test_shutdown_dry_run
run_case "shutdown auto order and scope" test_shutdown_auto_order_and_scope
say "PASS $PASS_COUNT case(s)"