Files
git.stella-ops.org/.gitea/workflows/test-matrix.yml

375 lines
12 KiB
YAML

# .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