mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-06 01:17:46 +08:00
Add dashboard and overview analytics, health monitoring, provider expense tracking, and announcement updates across the gateway and frontend. Keep schema migrations free of historical backfills while preserving automatic backfill execution. Bound migration deadlines, run schema preparation before Compose replacement, and anonymize deleted dashboard users. Include the current documentation cleanup and regression coverage.
234 lines
9.1 KiB
Bash
Executable File
234 lines
9.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
|
TEST_ROOT="$(mktemp -d)"
|
|
trap 'rm -rf -- "${TEST_ROOT}"' EXIT
|
|
|
|
fail_test() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
make_fixture() {
|
|
local fixture="$1"
|
|
mkdir -p "${fixture}/bin" "${fixture}/compose"
|
|
: >"${fixture}/compose/docker-compose.yml"
|
|
: >"${fixture}/calls"
|
|
cp "${REPO_ROOT}/update.sh" "${fixture}/compose/update.sh"
|
|
chmod 0755 "${fixture}/compose/update.sh"
|
|
}
|
|
|
|
make_migration_docker_stub() {
|
|
local fixture="$1"
|
|
cat >"${fixture}/bin/docker" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
printf '%s\n' "$*" >>"${AETHER_TEST_CALLS}"
|
|
case "$*" in
|
|
"compose version"|"info") exit 0 ;;
|
|
*"config --services") printf 'app\n' ;;
|
|
*"pull app") exit 0 ;;
|
|
*"run --rm --no-deps -T app --migrate") exit "${AETHER_TEST_MIGRATION_STATUS:-0}" ;;
|
|
*"up --help") printf '%s\n' ' --wait Wait for services' ;;
|
|
*"up -d --wait --wait-timeout 120 app"|*" ps") exit 0 ;;
|
|
*) printf 'Unexpected docker arguments: %s\n' "$*" >&2; exit 99 ;;
|
|
esac
|
|
EOF
|
|
chmod 0755 "${fixture}/bin/docker"
|
|
}
|
|
|
|
test_migration_succeeds_before_recreate() {
|
|
local fixture="${TEST_ROOT}/migration-before-recreate"
|
|
make_fixture "${fixture}"
|
|
make_migration_docker_stub "${fixture}"
|
|
|
|
PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"
|
|
|
|
local pull_line migration_line recreate_line
|
|
pull_line="$(grep -nF 'pull app' "${fixture}/calls" | cut -d: -f1)"
|
|
migration_line="$(grep -nF 'run --rm --no-deps -T app --migrate' "${fixture}/calls" | cut -d: -f1)"
|
|
recreate_line="$(grep -nF 'up -d --wait --wait-timeout 120 app' "${fixture}/calls" | cut -d: -f1)"
|
|
[[ "${pull_line}" -lt "${migration_line}" && "${migration_line}" -lt "${recreate_line}" ]] \
|
|
|| fail_test "update did not pull, migrate, then recreate in order"
|
|
[[ "$(grep -Fc ' --migrate' "${fixture}/calls")" -eq 1 ]] \
|
|
|| fail_test "update did not run migrations exactly once"
|
|
! grep -Eq 'db prepare|apply-backfills|--service-ports' "${fixture}/calls" \
|
|
|| fail_test "migration preflight enabled backfills or published service ports"
|
|
}
|
|
|
|
test_migration_failure_preserves_running_app() {
|
|
local fixture="${TEST_ROOT}/migration-failure"
|
|
make_fixture "${fixture}"
|
|
make_migration_docker_stub "${fixture}"
|
|
|
|
if PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
AETHER_TEST_MIGRATION_STATUS=42 \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"; then
|
|
fail_test "update succeeded after schema migration failed"
|
|
fi
|
|
|
|
grep -Fq 'run --rm --no-deps -T app --migrate' "${fixture}/calls" \
|
|
|| fail_test "update did not attempt schema migration"
|
|
! grep -Eq ' (up|stop|down|restart|rm) ' "${fixture}/calls" \
|
|
|| fail_test "failed migration changed the running service"
|
|
grep -Fq 'app was not recreated' "${fixture}/stderr" \
|
|
|| fail_test "migration failure did not explain that app was left running"
|
|
! grep -Fq '>>> Done.' "${fixture}/stdout" \
|
|
|| fail_test "failed migration was reported as successful"
|
|
}
|
|
|
|
test_prepare_only_pulls_image() {
|
|
local fixture="${TEST_ROOT}/prepare-only"
|
|
make_fixture "${fixture}"
|
|
make_migration_docker_stub "${fixture}"
|
|
|
|
PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" --prepare \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"
|
|
|
|
grep -Fq 'pull app' "${fixture}/calls" \
|
|
|| fail_test "--prepare did not pull the new app image"
|
|
! grep -Eq ' (run|up|stop|down|restart) ' "${fixture}/calls" \
|
|
|| fail_test "--prepare migrated or changed running services"
|
|
}
|
|
|
|
test_no_pull_still_migrates_before_recreate() {
|
|
local fixture="${TEST_ROOT}/migration-without-pull"
|
|
make_fixture "${fixture}"
|
|
make_migration_docker_stub "${fixture}"
|
|
|
|
PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" --no-pull \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"
|
|
|
|
! grep -Fq 'pull app' "${fixture}/calls" \
|
|
|| fail_test "--no-pull downloaded an app image"
|
|
local migration_line recreate_line
|
|
migration_line="$(grep -nF 'run --rm --no-deps -T app --migrate' "${fixture}/calls" | cut -d: -f1)"
|
|
recreate_line="$(grep -nF 'up -d --wait --wait-timeout 120 app' "${fixture}/calls" | cut -d: -f1)"
|
|
[[ "${migration_line}" -lt "${recreate_line}" ]] \
|
|
|| fail_test "--no-pull skipped schema preparation before recreate"
|
|
}
|
|
|
|
test_wait_failure_is_not_ignored() {
|
|
local fixture="${TEST_ROOT}/wait-failure"
|
|
make_fixture "${fixture}"
|
|
cat >"${fixture}/bin/docker" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
printf '%s\n' "$*" >>"${AETHER_TEST_CALLS}"
|
|
case "$*" in
|
|
"compose version"|"info") exit 0 ;;
|
|
*"config --services") printf 'app\n' ;;
|
|
*"up --help") printf '%s\n' ' --wait Wait for services' ;;
|
|
*"up -d --wait --wait-timeout 120 app") exit 42 ;;
|
|
*"pull app") exit 0 ;;
|
|
esac
|
|
exit 0
|
|
EOF
|
|
chmod 0755 "${fixture}/bin/docker"
|
|
|
|
if PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"; then
|
|
fail_test "update succeeded after compose --wait reported an unhealthy app"
|
|
fi
|
|
|
|
[[ "$(grep -Fc 'up -d' "${fixture}/calls")" -eq 1 ]] \
|
|
|| fail_test "failed update retried an unverified simple recreate"
|
|
! grep -Fq '>>> Done.' "${fixture}/stdout" \
|
|
|| fail_test "failed update was reported as successful"
|
|
}
|
|
|
|
test_legacy_compose_uses_health_polling() {
|
|
local fixture="${TEST_ROOT}/legacy-wait"
|
|
make_fixture "${fixture}"
|
|
cat >"${fixture}/bin/docker" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
printf '%s\n' "$*" >>"${AETHER_TEST_CALLS}"
|
|
case "$*" in
|
|
"compose version"|"info") exit 0 ;;
|
|
*"config --services") printf 'app\n' ;;
|
|
*"up --help") printf '%s\n' 'Usage: docker compose up' ;;
|
|
*"pull app"|*"up -d app"|*" ps") exit 0 ;;
|
|
*"ps -q app") printf 'container-id\n' ;;
|
|
"inspect --format={{.State.Health.Status}} container-id") printf 'healthy\n' ;;
|
|
esac
|
|
exit 0
|
|
EOF
|
|
chmod 0755 "${fixture}/bin/docker"
|
|
|
|
PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"
|
|
|
|
grep -Fq 'up -d app' "${fixture}/calls" \
|
|
|| fail_test "legacy Compose path did not recreate the app"
|
|
grep -Fq '>>> Container is healthy.' "${fixture}/stdout" \
|
|
|| fail_test "legacy Compose path did not require a healthy container"
|
|
}
|
|
|
|
test_legacy_compose_health_failure_is_not_ignored() {
|
|
local fixture="${TEST_ROOT}/legacy-unhealthy"
|
|
make_fixture "${fixture}"
|
|
cat >"${fixture}/bin/docker" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
printf '%s\n' "$*" >>"${AETHER_TEST_CALLS}"
|
|
case "$*" in
|
|
"compose version"|"info") exit 0 ;;
|
|
*"config --services") printf 'app\n' ;;
|
|
*"up --help") printf '%s\n' 'Usage: docker compose up' ;;
|
|
*"pull app"|*"up -d app") exit 0 ;;
|
|
*"ps -q app") printf 'container-id\n' ;;
|
|
"inspect --format={{.State.Health.Status}} container-id") printf 'unhealthy\n' ;;
|
|
esac
|
|
exit 0
|
|
EOF
|
|
cat >"${fixture}/bin/sleep" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exit 0
|
|
EOF
|
|
chmod 0755 "${fixture}/bin/docker" "${fixture}/bin/sleep"
|
|
|
|
if PATH="${fixture}/bin:${PATH}" AETHER_TEST_CALLS="${fixture}/calls" \
|
|
"${fixture}/compose/update.sh" --compose-dir "${fixture}/compose" \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"; then
|
|
fail_test "legacy Compose update succeeded with an unhealthy app"
|
|
fi
|
|
|
|
grep -Fq 'health check timed out' "${fixture}/stdout" \
|
|
|| fail_test "legacy unhealthy app did not reach the health failure path"
|
|
! grep -Fq '>>> Done.' "${fixture}/stdout" \
|
|
|| fail_test "legacy unhealthy update was reported as successful"
|
|
}
|
|
|
|
test_option_like_service_name_is_rejected() {
|
|
local fixture="${TEST_ROOT}/unsafe-service"
|
|
make_fixture "${fixture}"
|
|
|
|
if PATH="${fixture}/bin:${PATH}" "${fixture}/compose/update.sh" \
|
|
--compose-dir "${fixture}/compose" --service --ansi \
|
|
>"${fixture}/stdout" 2>"${fixture}/stderr"; then
|
|
fail_test "option-like service name was accepted"
|
|
fi
|
|
grep -Fq 'service name contains unsafe characters' "${fixture}/stderr" \
|
|
|| fail_test "unsafe service name was not rejected by the identifier guard"
|
|
}
|
|
|
|
test_migration_succeeds_before_recreate
|
|
test_migration_failure_preserves_running_app
|
|
test_prepare_only_pulls_image
|
|
test_no_pull_still_migrates_before_recreate
|
|
test_wait_failure_is_not_ignored
|
|
test_legacy_compose_uses_health_polling
|
|
test_legacy_compose_health_failure_is_not_ignored
|
|
test_option_like_service_name_is_rejected
|
|
|
|
echo "PASS: compose updater migration preflight, failure, and argument safety fixtures"
|