# .gitea/workflows/test-matrix.yml # Unified test matrix pipeline with TRX reporting for all test categories # Sprint: SPRINT_20251226_007_CICD - Dynamic test discovery # Refactored: SPRINT_CICD_Enhancement - DRY principle, matrix strategy # # WORKFLOW INTEGRATION STRATEGY: # ============================== # This workflow is the PRIMARY test execution workflow for PR gating. # It dynamically discovers and runs ALL test projects by Category trait. # # PR-Gating Categories (required for merge): # Unit, Architecture, Contract, Integration, Security, Golden # # Scheduled/On-Demand Categories: # Performance, Benchmark, AirGap, Chaos, Determinism, Resilience, Observability name: Test Matrix on: push: branches: [main] paths-ignore: - 'docs/**' - '*.md' pull_request: paths-ignore: - 'docs/**' - '*.md' schedule: - cron: '0 5 * * *' # Daily at 5 AM UTC workflow_dispatch: inputs: include_performance: description: 'Include performance tests' type: boolean default: false include_benchmark: description: 'Include benchmark tests' type: boolean default: false include_airgap: description: 'Include airgap tests' type: boolean default: false include_chaos: description: 'Include chaos tests' type: boolean default: false include_determinism: description: 'Include determinism tests' type: boolean default: false include_resilience: description: 'Include resilience tests' type: boolean default: false include_observability: description: 'Include observability tests' type: boolean default: false env: DOTNET_VERSION: '10.0.100' DOTNET_NOLOGO: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1 TZ: UTC jobs: # =========================================================================== # DISCOVER TEST PROJECTS # =========================================================================== discover: name: Discover Tests runs-on: ubuntu-22.04 outputs: test-projects: ${{ steps.find.outputs.projects }} test-count: ${{ steps.find.outputs.count }} steps: - name: Checkout uses: actions/checkout@v4 - name: Find all test projects id: find run: | PROJECTS=$(find src \( \ -name "*.Tests.csproj" \ -o -name "*UnitTests.csproj" \ -o -name "*SmokeTests.csproj" \ -o -name "*FixtureTests.csproj" \ -o -name "*IntegrationTests.csproj" \ \) -type f \ ! -path "*/node_modules/*" \ ! -path "*/.git/*" \ ! -path "*/bin/*" \ ! -path "*/obj/*" \ ! -name "StellaOps.TestKit.csproj" \ ! -name "*Testing.csproj" \ | sort) COUNT=$(echo "$PROJECTS" | grep -c '.csproj' || echo "0") echo "Found $COUNT test projects" echo "projects=$(echo "$PROJECTS" | jq -R -s -c 'split("\n") | map(select(length > 0))')" >> $GITHUB_OUTPUT echo "count=$COUNT" >> $GITHUB_OUTPUT - name: Display discovered projects run: | echo "## Discovered Test Projects" >> $GITHUB_STEP_SUMMARY echo "Total: ${{ steps.find.outputs.count }}" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY find src \( -name "*.Tests.csproj" -o -name "*UnitTests.csproj" -o -name "*SmokeTests.csproj" -o -name "*FixtureTests.csproj" -o -name "*IntegrationTests.csproj" \) -type f ! -path "*/node_modules/*" ! -name "StellaOps.TestKit.csproj" ! -name "*Testing.csproj" | sort >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY # =========================================================================== # PR-GATING TESTS (run on every push/PR) # Uses matrix strategy to run all categories in parallel # =========================================================================== pr-gating-tests: name: ${{ matrix.category }} Tests runs-on: ubuntu-22.04 timeout-minutes: ${{ matrix.timeout }} needs: discover strategy: fail-fast: false matrix: include: - category: Unit timeout: 20 collect_coverage: true - category: Architecture timeout: 15 collect_coverage: false - category: Contract timeout: 15 collect_coverage: false - category: Security timeout: 25 collect_coverage: false - category: Golden timeout: 25 collect_coverage: false steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} include-prerelease: true - name: Run ${{ matrix.category }} Tests run: | chmod +x .gitea/scripts/test/run-test-category.sh if [[ "${{ matrix.collect_coverage }}" == "true" ]]; then .gitea/scripts/test/run-test-category.sh "${{ matrix.category }}" --collect-coverage else .gitea/scripts/test/run-test-category.sh "${{ matrix.category }}" fi - name: Upload Test Results uses: actions/upload-artifact@v4 if: always() with: name: test-results-${{ matrix.category }} path: ./TestResults/${{ matrix.category }} retention-days: 14 # =========================================================================== # INTEGRATION TESTS (separate due to service dependency) # =========================================================================== integration: name: Integration Tests runs-on: ubuntu-22.04 timeout-minutes: 45 needs: discover services: postgres: image: postgres:16 env: POSTGRES_USER: stellaops POSTGRES_PASSWORD: stellaops POSTGRES_DB: stellaops_test ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} include-prerelease: true - name: Run Integration Tests env: STELLAOPS_TEST_POSTGRES_CONNECTION: "Host=localhost;Port=5432;Database=stellaops_test;Username=stellaops;Password=stellaops" run: | chmod +x .gitea/scripts/test/run-test-category.sh .gitea/scripts/test/run-test-category.sh Integration - name: Upload Test Results uses: actions/upload-artifact@v4 if: always() with: name: test-results-Integration path: ./TestResults/Integration retention-days: 14 # =========================================================================== # SCHEDULED/ON-DEMAND TESTS # Uses matrix strategy for extended test categories # =========================================================================== extended-tests: name: ${{ matrix.category }} Tests runs-on: ubuntu-22.04 timeout-minutes: ${{ matrix.timeout }} needs: discover if: >- github.event_name == 'schedule' || github.event.inputs.include_performance == 'true' || github.event.inputs.include_benchmark == 'true' || github.event.inputs.include_airgap == 'true' || github.event.inputs.include_chaos == 'true' || github.event.inputs.include_determinism == 'true' || github.event.inputs.include_resilience == 'true' || github.event.inputs.include_observability == 'true' strategy: fail-fast: false matrix: include: - category: Performance timeout: 45 trigger_input: include_performance run_on_schedule: true - category: Benchmark timeout: 60 trigger_input: include_benchmark run_on_schedule: true - category: AirGap timeout: 45 trigger_input: include_airgap run_on_schedule: false - category: Chaos timeout: 45 trigger_input: include_chaos run_on_schedule: false - category: Determinism timeout: 45 trigger_input: include_determinism run_on_schedule: false - category: Resilience timeout: 45 trigger_input: include_resilience run_on_schedule: false - category: Observability timeout: 30 trigger_input: include_observability run_on_schedule: false steps: - name: Check if should run id: should_run run: | SHOULD_RUN="false" if [[ "${{ github.event_name }}" == "schedule" && "${{ matrix.run_on_schedule }}" == "true" ]]; then SHOULD_RUN="true" fi if [[ "${{ github.event.inputs[matrix.trigger_input] }}" == "true" ]]; then SHOULD_RUN="true" fi echo "run=$SHOULD_RUN" >> $GITHUB_OUTPUT echo "Should run ${{ matrix.category }}: $SHOULD_RUN" - name: Checkout if: steps.should_run.outputs.run == 'true' uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup .NET if: steps.should_run.outputs.run == 'true' uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} include-prerelease: true - name: Run ${{ matrix.category }} Tests if: steps.should_run.outputs.run == 'true' run: | chmod +x .gitea/scripts/test/run-test-category.sh .gitea/scripts/test/run-test-category.sh "${{ matrix.category }}" - name: Upload Test Results uses: actions/upload-artifact@v4 if: always() && steps.should_run.outputs.run == 'true' with: name: test-results-${{ matrix.category }} path: ./TestResults/${{ matrix.category }} retention-days: 14 # =========================================================================== # SUMMARY JOB # =========================================================================== summary: name: Test Summary runs-on: ubuntu-22.04 needs: [discover, pr-gating-tests, integration] if: always() steps: - name: Download all test results uses: actions/download-artifact@v4 with: pattern: test-results-* path: ./TestResults - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} include-prerelease: true - name: Install trx2junit run: dotnet tool install -g trx2junit - name: Convert TRX to JUnit run: | find ./TestResults -name "*.trx" -exec trx2junit {} \; || true - name: Generate Summary run: | echo "## Test Results Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### PR-Gating Tests" >> $GITHUB_STEP_SUMMARY echo "| Category | Status |" >> $GITHUB_STEP_SUMMARY echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY echo "| Discover | ${{ needs.discover.result }} |" >> $GITHUB_STEP_SUMMARY echo "| PR-Gating Matrix | ${{ needs.pr-gating-tests.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Integration | ${{ needs.integration.result }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Test Projects Discovered: ${{ needs.discover.outputs.test-count }}" >> $GITHUB_STEP_SUMMARY - name: Count TRX files run: | TRX_COUNT=$(find ./TestResults -name "*.trx" 2>/dev/null | wc -l || echo "0") echo "### Total TRX Files Generated: $TRX_COUNT" >> $GITHUB_STEP_SUMMARY - name: Upload Combined Results uses: actions/upload-artifact@v4 with: name: test-results-combined path: ./TestResults retention-days: 14 - name: Check for failures if: contains(needs.*.result, 'failure') run: exit 1