name: Test Pipeline on: push: branches: - main - develop - 'feature/**' pull_request: branches: - main - develop env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 SQLX_OFFLINE: true # Use offline mode for CI jobs: test: name: Test Suite runs-on: ubuntu-latest timeout-minutes: 30 services: postgres: image: timescale/timescaledb:latest-pg16 env: POSTGRES_USER: foxhunt POSTGRES_PASSWORD: foxhunt_dev_password POSTGRES_DB: foxhunt ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 redis: image: redis:7-alpine ports: - 6379:6379 options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 vault: image: hashicorp/vault:latest env: VAULT_DEV_ROOT_TOKEN_ID: foxhunt-dev-root VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200 ports: - 8200:8200 options: >- --cap-add=IPC_LOCK --health-cmd "vault status" --health-interval 10s --health-timeout 5s --health-retries 5 env: DATABASE_URL: postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt REDIS_URL: redis://localhost:6379 VAULT_ADDR: http://localhost:8200 VAULT_TOKEN: foxhunt-dev-root JWT_SECRET: test_jwt_secret_key RUST_LOG: info steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Rust Toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy, llvm-tools-preview - name: Cache Cargo Registry uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Install SQLx CLI run: cargo install sqlx-cli --no-default-features --features postgres - name: Run Database Migrations run: cargo sqlx migrate run - name: Install llvm-cov run: cargo install cargo-llvm-cov - name: Check Formatting run: cargo fmt --all -- --check - name: Run Clippy run: | cargo clippy --workspace --all-targets -- \ -D warnings \ -D clippy::unwrap_used \ -D clippy::expect_used \ -D clippy::panic \ -W clippy::all - name: Run Unit Tests run: cargo test --workspace --lib --bins env: RUST_LOG: debug - name: Run Integration Tests run: cargo test --workspace --test '*' -- --test-threads=1 timeout-minutes: 15 - name: Generate Coverage Report run: | cargo llvm-cov --workspace --lcov --output-path lcov.info cargo llvm-cov --workspace --html --output-dir coverage_report - name: Check Coverage Threshold run: | COVERAGE=$(cargo llvm-cov --workspace --summary-only | grep -oP 'TOTAL.*\K[0-9.]+(?=%)') echo "::notice::Coverage: $COVERAGE%" if (( $(echo "$COVERAGE < 60.0" | bc -l) )); then echo "::error::Coverage $COVERAGE% below threshold 60%" exit 1 fi echo "::notice::✅ Coverage $COVERAGE% meets threshold" - name: Upload Coverage to Codecov uses: codecov/codecov-action@v4 with: files: ./lcov.info flags: unittests name: codecov-umbrella fail_ci_if_error: false - name: Upload Coverage Report uses: actions/upload-artifact@v4 if: always() with: name: coverage-report path: coverage_report/ retention-days: 30 - name: Upload Test Results uses: actions/upload-artifact@v4 if: always() with: name: test-results path: target/nextest/ retention-days: 30 security-audit: name: Security Audit runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Rust Toolchain uses: dtolnay/rust-toolchain@stable - name: Install Cargo Audit run: cargo install cargo-audit - name: Run Security Audit run: | cargo audit --deny warnings \ --ignore RUSTSEC-2020-0071 # RSA Marvin Attack (mitigated) - name: Install Cargo Geiger run: cargo install cargo-geiger - name: Unsafe Code Audit run: | cargo geiger --all-features --output-format GitHubMarkdown >> $GITHUB_STEP_SUMMARY dependency-check: name: Dependency Check runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Rust Toolchain uses: dtolnay/rust-toolchain@stable - name: Install Cargo Outdated run: cargo install cargo-outdated - name: Check Outdated Dependencies run: cargo outdated --exit-code 1 continue-on-error: true - name: Install Cargo Deny run: cargo install cargo-deny - name: Check Licenses run: cargo deny check licenses - name: Check Advisories run: cargo deny check advisories build-check: name: Build Check runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Rust Toolchain uses: dtolnay/rust-toolchain@stable - name: Cache Cargo Registry uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Check Compilation run: cargo check --workspace --all-targets - name: Build Release run: cargo build --workspace --release summary: name: Test Summary runs-on: ubuntu-latest needs: [test, security-audit, dependency-check, build-check] if: always() steps: - name: Generate Summary run: | echo "## Test Pipeline Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY echo "| Test Suite | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Security Audit | ${{ needs.security-audit.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Dependency Check | ${{ needs.dependency-check.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Build Check | ${{ needs.build-check.result }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - name: Check Overall Status if: contains(needs.*.result, 'failure') run: | echo "::error::Pipeline failed - check job results above" exit 1