| name: Fineract E2E Tests |
| |
| on: |
| workflow_call: |
| secrets: |
| DEVELOCITY_ACCESS_KEY: |
| required: false |
| |
| permissions: |
| contents: read |
| |
| jobs: |
| test: |
| name: E2E Tests (Shard ${{ matrix.shard_index }} of ${{ matrix.total_shards }}) |
| runs-on: ubuntu-24.04 |
| timeout-minutes: 90 |
| |
| strategy: |
| fail-fast: false |
| matrix: |
| # Define the number of shards (1-based indexing) |
| shard_index: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] |
| total_shards: [20] |
| |
| env: |
| DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} |
| IMAGE_NAME: fineract |
| COMPOSE_FILE: docker-compose-postgresql-test-activemq.yml |
| DOCKER_LOG_DIR: ci-logs/e2e-shard-${{ matrix.shard_index }} |
| BASE_URL: https://localhost:8443 |
| TEST_USERNAME: mifos |
| TEST_PASSWORD: password |
| TEST_STRONG_PASSWORD: A1b2c3d4e5f$ |
| TEST_TENANT_ID: default |
| INITIALIZATION_ENABLED: true |
| EVENT_VERIFICATION_ENABLED: true |
| ACTIVEMQ_BROKER_URL: tcp://localhost:61616 |
| ACTIVEMQ_TOPIC_NAME: events |
| |
| steps: |
| - name: Set up JDK 21 |
| uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 |
| with: |
| java-version: '21' |
| distribution: 'zulu' |
| |
| - name: Download workspace |
| id: download-workspace |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 |
| continue-on-error: true |
| with: |
| name: fineract-workspace-${{ github.run_id }} |
| path: . |
| |
| - name: Extract workspace |
| if: steps.download-workspace.outcome == 'success' |
| run: tar -xf fineract-workspace.tar |
| |
| - name: Checkout |
| if: steps.download-workspace.outcome != 'success' |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
| with: |
| persist-credentials: false |
| fetch-depth: 0 |
| fetch-tags: true |
| |
| - name: Build workspace fallback |
| if: steps.download-workspace.outcome != 'success' |
| uses: ./.github/actions/setup-and-build-fineract |
| with: |
| cache-read-only: false |
| |
| - name: Setup Gradle |
| uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 |
| |
| - name: Make scripts executable |
| run: chmod +x scripts/split-features.sh |
| |
| - name: Split feature files into shards |
| id: split-features |
| run: | |
| ./scripts/split-features.sh ${{ matrix.total_shards }} ${{ matrix.shard_index }} |
| echo "Shard ${{ matrix.shard_index }} feature files:" |
| cat feature_shard_${{ matrix.shard_index }}.txt |
| |
| - name: Build the image |
| run: ./gradlew --no-daemon --console=plain :fineract-provider:jibDockerBuild -Djib.to.image=$IMAGE_NAME -x test -x cucumber -x buildJavaSdk |
| |
| - name: Start the Fineract stack |
| run: docker compose -f "$COMPOSE_FILE" up -d |
| |
| - name: Check the stack |
| run: docker ps |
| |
| - name: Wait for Manager to be ready |
| run: | |
| # Wait for the container to be running |
| echo "Waiting for Manager container to be ready..." |
| timeout 300 bash -c 'until docker ps --filter "health=healthy" --filter "name=fineract" --format "{{.Status}}" | grep -q "healthy"; do |
| if docker ps --filter "name=fineract" --format "{{.Status}}" | grep -q "unhealthy"; then |
| echo "Container is unhealthy. Stopping..." |
| docker ps -a |
| docker logs $(docker ps -q --filter name=fineract) || true |
| exit 1 |
| fi |
| echo "Waiting for Manager to be ready..." |
| sleep 5 |
| done' |
| |
| # Check the health endpoint |
| echo "Checking Manager health endpoint..." |
| curl -f -k --retry 30 --retry-all-errors --connect-timeout 10 --retry-delay 10 \ |
| https://localhost:8443/fineract-provider/actuator/health |
| |
| - name: Execute tests for shard ${{ matrix.shard_index }} |
| id: tests |
| run: | |
| # Initialize failure flag |
| FAILED=0 |
| |
| # Create necessary directories |
| mkdir -p "allure-results-shard-${{ matrix.shard_index }}" |
| mkdir -p "allure-results-merged" |
| |
| # Read feature files from the shard file |
| if [ ! -s "feature_shard_${{ matrix.shard_index }}.txt" ]; then |
| echo "No features to test in this shard. Skipping..." |
| exit 0 |
| fi |
| |
| # Read each feature file path and run tests one by one |
| while IFS= read -r feature_file || [ -n "$feature_file" ]; do |
| # Skip empty lines |
| [ -z "$feature_file" ] && continue |
| |
| # Create a safe filename for the results |
| safe_name=$(echo "$feature_file" | tr '/' '-' | tr ' ' '_') |
| |
| echo "::group::Testing feature: $feature_file" |
| |
| # Run tests with individual allure results directory |
| if ! ./gradlew --no-daemon --console=plain \ |
| :fineract-e2e-tests-runner:cucumber \ |
| -Pcucumber.features="$feature_file" \ |
| -Dallure.results.directory="allure-results-shard-${{ matrix.shard_index }}/$safe_name" \ |
| allureReport \ |
| -x buildJavaSdk \ |
| -x :fineract-client:buildJavaSdk \ |
| -x :fineract-client-feign:buildJavaSdk \ |
| -x :fineract-avro-schemas:buildJavaSdk; then |
| |
| echo "::error::Test failed for $feature_file" |
| FAILED=1 |
| fi |
| |
| echo "::endgroup::" |
| |
| # Copy the results to a merged directory |
| if [ -d "allure-results-shard-${{ matrix.shard_index }}/$safe_name" ]; then |
| cp -r "allure-results-shard-${{ matrix.shard_index }}/$safe_name/." "allure-results-merged/" || true |
| fi |
| done < "feature_shard_${{ matrix.shard_index }}.txt" |
| |
| # Generate individual report for this shard |
| if [ -d "allure-results-merged" ] && [ "$(ls -A allure-results-merged)" ]; then |
| echo "Generating Allure report..." |
| mkdir -p "allure-report-shard-${{ matrix.shard_index }}" |
| ./fineract-e2e-tests-runner/build/allure/commandline/bin/allure generate "allure-results-merged" --clean -o "allure-report-shard-${{ matrix.shard_index }}" || \ |
| echo "::warning::Failed to generate Allure report for shard ${{ matrix.shard_index }}" |
| fi |
| |
| # Exit with failure status if any test failed |
| if [ "$FAILED" -eq 1 ]; then |
| echo "::error::Some tests failed in this shard" |
| exit 1 |
| fi |
| |
| - name: Collect Docker logs |
| if: failure() |
| run: | |
| mkdir -p "$DOCKER_LOG_DIR" |
| docker ps -a > "$DOCKER_LOG_DIR/docker-ps.txt" 2>&1 || true |
| docker compose -f "$COMPOSE_FILE" ps --all > "$DOCKER_LOG_DIR/docker-compose-ps.txt" 2>&1 || true |
| docker compose -f "$COMPOSE_FILE" logs --no-color --timestamps > "$DOCKER_LOG_DIR/docker-compose.log" 2>&1 || true |
| docker stats --no-stream > "$DOCKER_LOG_DIR/docker-stats.txt" 2>&1 || true |
| |
| docker compose -f "$COMPOSE_FILE" ps -q | while read -r container_id; do |
| [ -n "$container_id" ] || continue |
| container_name="$(docker inspect --format '{{.Name}}' "$container_id" 2>/dev/null | sed 's#^/##; s#[^A-Za-z0-9_.-]#_#g')" |
| [ -n "$container_name" ] || container_name="$container_id" |
| docker logs --timestamps "$container_id" > "$DOCKER_LOG_DIR/${container_name}.log" 2>&1 || true |
| docker inspect "$container_id" > "$DOCKER_LOG_DIR/${container_name}.inspect.json" 2>&1 || true |
| done |
| |
| - name: Upload test results |
| if: failure() |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| with: |
| name: allure-results-shard-${{ matrix.shard_index }}-attempt-${{ github.run_attempt }} |
| path: | |
| allure-results-shard-${{ matrix.shard_index }} |
| allure-results-merged |
| **/build/allure-results |
| **/build/reports/tests/test |
| **/build/test-results/test |
| if-no-files-found: ignore |
| retention-days: 5 |
| compression-level: 9 |
| |
| - name: Upload Allure Report |
| if: failure() |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| with: |
| name: allure-report-shard-${{ matrix.shard_index }}-attempt-${{ github.run_attempt }} |
| path: allure-report-shard-${{ matrix.shard_index }} |
| if-no-files-found: ignore |
| retention-days: 5 |
| compression-level: 9 |
| |
| - name: Upload logs |
| if: failure() |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| with: |
| name: logs-shard-${{ matrix.shard_index }}-attempt-${{ github.run_attempt }} |
| path: | |
| ci-logs/ |
| **/build/reports/tests/ |
| **/logs/ |
| **/out/ |
| if-no-files-found: ignore |
| retention-days: 5 |
| compression-level: 9 |
| |
| - name: Clean up |
| if: always() |
| run: | |
| docker compose -f "$COMPOSE_FILE" down -v |
| docker system prune -f |