name: Integration Tests permissions: contents: read concurrency: group: integration-tests-${{ github.ref }} cancel-in-progress: true on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] jobs: integration-tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: keycloak_db: [postgres, dev-file] env: KEYCLOAK_VERSION: "26.4.2" services: postgres: image: postgres:16-alpine env: POSTGRES_USER: meldestelle POSTGRES_PASSWORD: meldestelle POSTGRES_DB: meldestelle ports: - 5432:5432 options: >- --health-cmd "pg_isready -U meldestelle -d $${POSTGRES_DB}" --health-interval 10s --health-timeout 5s --health-retries 12 --health-start-period 20s redis: image: redis:7-alpine ports: - 6379:6379 options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 zookeeper: image: confluentinc/cp-zookeeper:7.5.0 env: ZOOKEEPER_CLIENT_PORT: 2181 ports: - 2181:2181 options: >- --health-cmd "nc -z localhost 2181 || exit 1" --health-interval 10s --health-timeout 5s --health-retries 3 --health-start-period 10s kafka: image: confluentinc/cp-kafka:7.5.0 env: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 ports: - 9092:9092 options: >- --health-cmd "kafka-topics --bootstrap-server localhost:9092 --list || exit 1" --health-interval 10s --health-timeout 5s --health-retries 3 --health-start-period 30s zipkin: image: openzipkin/zipkin:2 ports: - 9411:9411 options: >- --health-cmd "wget -q -O - http://localhost:9411/health || exit 1" --health-interval 10s --health-timeout 5s --health-retries 3 --health-start-period 10s steps: - uses: actions/checkout@v6 - name: Set up JDK 21 uses: actions/setup-java@v5 with: java-version: 21 distribution: 'temurin' cache: 'gradle' - name: Setup Gradle (modern) uses: gradle/actions/setup-gradle@v5 - name: Wait for Postgres to be ready (pg_isready in service network) if: ${{ matrix.keycloak_db == 'postgres' }} run: | echo "Waiting for Postgres..." for i in {1..40}; do if docker run --rm --network ${{ job.services.postgres.network }} \ postgres:16-alpine pg_isready -h postgres -p 5432 -U meldestelle -d meldestelle; then echo "Postgres is ready"; break; fi; echo -n "."; sleep 3; done - name: Start Keycloak with Postgres (dev) and wait for readiness if: ${{ matrix.keycloak_db == 'postgres' }} run: | set -euo pipefail echo "Starting Keycloak (DB=postgres)..." docker run -d --name keycloak \ --network ${{ job.services.postgres.network }} \ -p 8180:8080 \ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ -e KC_DB=postgres \ -e KC_DB_URL=jdbc:postgresql://postgres:5432/meldestelle \ -e KC_DB_USERNAME=meldestelle \ -e KC_DB_PASSWORD=meldestelle \ -e KC_HEALTH_ENABLED=true \ -e JAVA_OPTS="-Xms256m -Xmx1024m -XX:MaxRAMPercentage=60" \ quay.io/keycloak/keycloak:${{ env.KEYCLOAK_VERSION }} \ start-dev echo "Giving Keycloak 30s to initialize..."; sleep 30 wait_for() { local url="$1"; local label="$2"; local timeout="${3:-180}"; local interval="${4:-5}" echo "Waiting for $label ($url) ..." if ! timeout ${timeout}s bash -c 'until curl -fsS --output /dev/null '"$url"'; do echo -n "."; sleep '"$interval"'; done'; then echo "\n[WAIT] Timeout on $url" return 1 fi echo "\n[WAIT] $label is up" } if ! wait_for http://localhost:8180/ "root" 180 5; then docker logs --tail=200 keycloak || true exit 1 fi if ! wait_for http://localhost:8180/health "health" 180 5; then echo "[INFO] /health nicht erreichbar – versuche /q/health (Quarkus default)" wait_for http://localhost:8180/q/health "q-health" 180 5 || true fi wait_for http://localhost:8180/health/ready "health-ready" 300 5 || true wait_for http://localhost:8180/admin/master/console/ "admin-console" 300 5 || (docker logs --tail=400 keycloak && exit 1) - name: Start Keycloak with dev-file (no Postgres) and wait for readiness if: ${{ matrix.keycloak_db == 'dev-file' }} run: | set -euo pipefail echo "Starting Keycloak (DB=dev-file, no Postgres)..." docker run -d --name keycloak \ -p 8180:8080 \ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ -e KC_DB=dev-file \ -e KC_HEALTH_ENABLED=true \ -e JAVA_OPTS="-Xms256m -Xmx1024m -XX:MaxRAMPercentage=60" \ quay.io/keycloak/keycloak:${{ env.KEYCLOAK_VERSION }} \ start-dev echo "Giving Keycloak 20s to initialize..."; sleep 20 wait_for() { local url="$1"; local label="$2"; local timeout="${3:-180}"; local interval="${4:-5}" echo "Waiting for $label ($url) ..." if ! timeout ${timeout}s bash -c 'until curl -fsS --output /dev/null '"$url"'; do echo -n "."; sleep '"$interval"'; done'; then echo "\n[WAIT] Timeout on $url" return 1 fi echo "\n[WAIT] $label is up" } if ! wait_for http://localhost:8180/ "root" 180 5; then docker logs --tail=200 keycloak || true exit 1 fi if ! wait_for http://localhost:8180/health "health" 180 5; then echo "[INFO] /health nicht erreichbar – versuche /q/health (Quarkus default)" wait_for http://localhost:8180/q/health "q-health" 180 5 || true fi wait_for http://localhost:8180/health/ready "health-ready" 300 5 || true wait_for http://localhost:8180/admin/master/console/ "admin-console" 300 5 || (docker logs --tail=400 keycloak && exit 1) - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Static Analysis run: ./gradlew staticAnalysis --no-daemon - name: Run integration tests run: ./gradlew integrationTest --no-daemon --parallel env: # Environment variables for Redis connection REDIS_HOST: localhost REDIS_PORT: 6379 # Keycloak base URL for integration tests (manual container) KEYCLOAK_AUTH_SERVER_URL: http://localhost:8180 # Spring profile for integration tests SPRING_PROFILES_ACTIVE: integration-test - name: Upload test reports uses: actions/upload-artifact@v5 if: always() with: name: integration-test-reports path: | **/build/reports/tests/integrationTest/ **/build/test-results/integrationTest/ retention-days: 7 - name: Dump service logs (Keycloak, Postgres) if: always() run: | echo "=== Docker ps ===" && docker ps -a || true echo "=== Keycloak logs (tail) ===" && docker logs --tail=400 keycloak || true echo "=== Postgres logs (tail) ===" && docker logs --tail=200 $(docker ps -a --filter "name=postgres" --format "{{.ID}}") || true