| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- #!/usr/bin/env bash
- # End-to-end smoke check.
- #
- # Mirrors the README quickstart: boots the compose stack, creates an
- # admin token, creates a reporter + consumer + their tokens, posts a
- # report, triggers the recompute job, pulls the blocklist, and asserts
- # the IP shows up. Tears down at the end.
- #
- # Run from the repo root:
- # ./tests/e2e/demo.sh
- #
- # Requirements: Docker. No PHP/Node/Composer needed on the host.
- # Expected runtime: ~90 seconds (most of it the FrankenPHP cold start).
- #
- # Exits non-zero on any step failure. Sets `set -e` so the first error
- # aborts; `trap` ensures the stack is torn down even on early exit.
- set -euo pipefail
- REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
- cd "$REPO_ROOT"
- LOG_PREFIX="\033[1;36m[e2e]\033[0m"
- log() { printf "%b %s\n" "$LOG_PREFIX" "$*"; }
- fail() { printf "\033[1;31m[fail]\033[0m %s\n" "$*" >&2; exit 1; }
- cleanup() {
- log "tearing down compose stack"
- docker compose down -v >/dev/null 2>&1 || true
- }
- trap cleanup EXIT
- # ---- Step 1: prepare a clean .env ---------------------------------
- log "preparing .env from .env.example"
- cp .env.example .env
- # Generate a deterministic local-admin password hash so the test is
- # reproducible. The helper escape doubles every $ so docker-compose
- # variable substitution doesn't eat them.
- HASH=$(php -r "echo password_hash('e2e-demo-password', PASSWORD_ARGON2ID);" | sed 's/\$/$$/g')
- # Fill in the secrets needed for boot. Use a small awk to substitute
- # in place — POSIX sed's behaviour for "key=" lines varies across
- # distros for special chars in the value.
- awk -v hash="$HASH" '
- BEGIN { ui_secret=ui_svc=int_tok=app_secret="" }
- /^UI_SECRET=/ { print "UI_SECRET=" "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; next }
- /^APP_SECRET=/ { print "APP_SECRET=" "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"; next }
- /^INTERNAL_JOB_TOKEN=/ { print "INTERNAL_JOB_TOKEN=" "abababababababababababababababababababababababababababababababab"; next }
- /^UI_SERVICE_TOKEN=/ { print "UI_SERVICE_TOKEN=" "irdb_svc_AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD"; next }
- /^OIDC_ENABLED=/ { print "OIDC_ENABLED=false"; next }
- /^LOCAL_ADMIN_PASSWORD_HASH=/ { print "LOCAL_ADMIN_PASSWORD_HASH=" hash; next }
- { print }
- ' .env > .env.tmp && mv .env.tmp .env
- # ---- Step 2: bring the stack up ----------------------------------
- log "compose up"
- docker compose up -d --build >/dev/null
- log "waiting for api healthcheck (up to 60s)"
- for _ in $(seq 1 60); do
- if curl -sf http://localhost:8081/healthz >/dev/null 2>&1; then
- break
- fi
- sleep 1
- done
- curl -sf http://localhost:8081/healthz >/dev/null || fail "api never became healthy"
- log "waiting for ui healthcheck (up to 30s)"
- for _ in $(seq 1 30); do
- if curl -sf http://localhost:8080/healthz >/dev/null 2>&1; then
- break
- fi
- sleep 1
- done
- curl -sf http://localhost:8080/healthz >/dev/null || fail "ui never became healthy"
- # ---- Step 3: bootstrap the service token + admin token -----------
- log "bootstrapping the UI service token row"
- docker compose exec -T api php bin/console auth:bootstrap-service-token >/dev/null
- log "creating an admin token via CLI"
- ADMIN_TOKEN=$(docker compose exec -T api php bin/console \
- auth:create-token --kind=admin --role=admin --quiet | tr -d '\r')
- [ -n "$ADMIN_TOKEN" ] || fail "no admin token returned"
- # ---- Step 4: create a reporter + reporter token ------------------
- log "creating reporter + token"
- RID=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
- -H "Content-Type: application/json" \
- -d '{"name":"e2e-rep","trust_weight":1.0}' \
- http://localhost:8081/api/v1/admin/reporters \
- | php -r 'echo json_decode(stream_get_contents(STDIN),true)["id"];')
- [ -n "$RID" ] || fail "no reporter id returned"
- REP_TOKEN=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
- -H "Content-Type: application/json" \
- -d "{\"kind\":\"reporter\",\"reporter_id\":$RID}" \
- http://localhost:8081/api/v1/admin/tokens \
- | php -r 'echo json_decode(stream_get_contents(STDIN),true)["raw_token"];')
- [ -n "$REP_TOKEN" ] || fail "no reporter token returned"
- # ---- Step 5: post some reports ----------------------------------
- log "submitting reports for 203.0.113.10..12"
- for i in 10 11 12; do
- curl -sf -X POST -H "Authorization: Bearer $REP_TOKEN" \
- -H "Content-Type: application/json" \
- -d "{\"ip\":\"203.0.113.$i\",\"category\":\"brute_force\"}" \
- http://localhost:8081/api/v1/report > /dev/null
- done
- # ---- Step 6: create a consumer + consumer token -----------------
- log "creating consumer + token"
- # Pick policy id 1 (the seeded "moderate" policy).
- CID=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
- -H "Content-Type: application/json" \
- -d '{"name":"e2e-con","policy_id":1}' \
- http://localhost:8081/api/v1/admin/consumers \
- | php -r 'echo json_decode(stream_get_contents(STDIN),true)["id"];')
- [ -n "$CID" ] || fail "no consumer id returned"
- CON_TOKEN=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
- -H "Content-Type: application/json" \
- -d "{\"kind\":\"consumer\",\"consumer_id\":$CID}" \
- http://localhost:8081/api/v1/admin/tokens \
- | php -r 'echo json_decode(stream_get_contents(STDIN),true)["raw_token"];')
- [ -n "$CON_TOKEN" ] || fail "no consumer token returned"
- # ---- Step 7: trigger recompute via admin endpoint ---------------
- log "triggering recompute-scores"
- RESP=$(curl -sf -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
- -H "Content-Type: application/json" -d '{"full":true}' \
- http://localhost:8081/api/v1/admin/jobs/trigger/recompute-scores)
- echo "$RESP" | grep -q '"status":"success"' || fail "recompute did not succeed: $RESP"
- # ---- Step 8: pull the blocklist; assert non-empty ----------------
- log "pulling blocklist"
- LIST=$(curl -sf -H "Authorization: Bearer $CON_TOKEN" http://localhost:8081/api/v1/blocklist)
- COUNT=$(printf '%s\n' "$LIST" | grep -cE '^[0-9]' || true)
- [ "$COUNT" -ge 1 ] || fail "blocklist empty (count=$COUNT)"
- log "blocklist has $COUNT entries"
- # ---- Step 9: poke the OpenAPI viewer -----------------------------
- log "fetching /api/v1/openapi.yaml"
- curl -sf http://localhost:8081/api/v1/openapi.yaml | grep -q '^openapi:' \
- || fail "openapi.yaml missing or unexpected"
- log "fetching /api/docs"
- curl -sf http://localhost:8081/api/docs | grep -qi 'rapi-doc' \
- || fail "/api/docs did not render the viewer"
- log "all checks passed"
|