This document covers continuous integration testing configuration, pipeline stages, and optimizing test runs in CI environments.
- Overview
- GitHub Actions Workflow
- Pipeline Stages
- cargo-nextest Profiles
- Environment Variables
- Test Artifacts
- Local CI Emulation
- Troubleshooting
- Performance Optimization
Tests run automatically on:
- Every pull request to
mainordevelop - Every push to
mainordevelop
Workflow file: .github/workflows/ci.yml
- Parallel execution: Backend and frontend tests run concurrently
- Dual database testing: SQLite for fast unit tests, PostgreSQL for integration tests
- Smart caching: sccache and dependency caching reduce build time by 30-50%
- Coverage on PRs: Coverage reports generated only for pull requests
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]┌─────────────────┐ ┌─────────────────────┐
│ backend-lint │ │ frontend-lint │
└────────┬────────┘ └──────────┬──────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│backend-unit-test│ │ frontend-test │
└────────┬────────┘ └──────────┬──────────┘
│ │
▼ │
┌─────────────────────────┐ │
│backend-integration-test │ │
└────────┬────────────────┘ │
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ backend-build │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────┐
│ docker-build │
└─────────────────┘
backend-lint:
- cargo fmt --check
- cargo clippy --all-targets --all-features -- -D warnings
backend-security:
- cargo audit
- cargo deny checkbackend-unit-test:
database: sqlite::memory:
runner: cargo nextest
threads: 4
retries: 2
timeout: 10 minutesFast feedback with in-memory SQLite. Tests that require PostgreSQL features are automatically skipped.
backend-integration-test:
service: postgres:16-alpine
database: postgres://ampel:ampel@localhost:5432/ampel_test
runner: cargo nextest
threads: 2
retries: 3
timeout: 15 minutesComprehensive testing with real PostgreSQL. Each test gets an isolated database instance.
frontend-lint:
- pnpm run lint
- pnpm run type-check
frontend-test:
runner: vitest
coverage: true
artifacts: coverage report (7 days)backend-build:
- cargo build --release
frontend-build:
- pnpm run build
docker-build:
- docker build (push events only)backend-coverage:
tool: cargo-llvm-cov
database: PostgreSQL (required)
upload: CodecovConfiguration in .config/nextest.toml:
[profile.fast]
test-threads = 8
retries = 0
fail-fast = true
slow-timeout = { period = "30s", terminate-after = 2 }- When: Quick feedback during coding
- Speed: Fastest
- Command:
cargo nextest run --profile fast
[profile.default]
test-threads = 4
retries = 2
fail-fast = false
slow-timeout = { period = "60s", terminate-after = 3 }- When: Pre-commit checks
- Speed: Moderate
- Command:
cargo nextest run
[profile.ci]
test-threads = 4
retries = 3
fail-fast = false
slow-timeout = { period = "120s", terminate-after = 3 }- When: Automatically in CI
- Speed: Optimized for reliability
- Command:
cargo nextest run --profile ci
[profile.coverage]
test-threads = 1
retries = 0- When: Generating coverage reports
- Speed: Slower (single-threaded for accurate coverage)
- Command:
cargo nextest run --profile coverage
# Rust build
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUSTFLAGS: '-D warnings'
RUST_VERSION: '1.92.0'
# Test configuration
DATABASE_URL: postgres://ampel:ampel@localhost:5432/ampel_test
TEST_DATABASE_TYPE: postgres
JWT_SECRET: test-jwt-secret-for-ci-minimum-32-chars
ENCRYPTION_KEY: dGVzdC1lbmNyeXB0aW9uLWtleS0zMi1ieXRlcw==
RUST_LOG: info
CI: true
# Performance
SCCACHE_GHA_ENABLED: 'true'
RUSTC_WRAPPER: 'sccache'JWT_SECRET=test-jwt-secret-for-ci-minimum-32-chars
ENCRYPTION_KEY=dGVzdC1lbmNyeXB0aW9uLWtleS0zMi1ieXRlcw==# SQLite (fast unit tests)
DATABASE_URL=sqlite::memory:
TEST_DATABASE_TYPE=sqlite
# PostgreSQL (integration tests)
DATABASE_URL=postgres://ampel:ampel@localhost:5432/ampel_test
TEST_DATABASE_TYPE=postgresUploaded on test completion with 7-day retention:
| Artifact | Contents |
|---|---|
backend-unit-test-results |
nextest JUnit output |
backend-integration-test-results |
nextest JUnit output |
frontend-coverage |
Vitest coverage report |
backend-coverage-report |
Tarpaulin coverage report |
# Using GitHub CLI
gh run download <run-id> -n backend-unit-test-results# Backend unit tests (SQLite)
DATABASE_URL=sqlite::memory: cargo nextest run --profile ci
# Backend integration tests (PostgreSQL)
docker compose up -d postgres
DATABASE_URL=postgres://ampel:ampel@localhost:5432/ampel_test \
cargo nextest run --profile ci
# Frontend tests
cd frontend && pnpm test -- --run --coverage# All checks in one command
make ci# 1. Run fast unit tests
cargo nextest run --profile fast
# 2. Run linter
make lint-backend
# 3. Check formatting
make format-check-backend# 1. Start PostgreSQL
docker compose up -d postgres
# 2. Run full test suite
make test-backend
# 3. Generate coverage
cargo llvm-cov --all-features --workspace --html --output-dir coverage
# 4. Run all CI checks
make ciLikely causes:
- Using different databases (SQLite vs PostgreSQL)
- Different environment variables
- Timing issues / race conditions
Solutions:
# Test with same environment as CI
DATABASE_URL=sqlite::memory: cargo nextest run --profile ci
# Then test with PostgreSQL
DATABASE_URL=postgres://... cargo nextest run --profile ciCheck which tests are slow:
cargo nextest run --profile ci
# Look for "SLOW" markers in outputSpeed up tests:
# Use more threads
cargo nextest run --test-threads 8
# Use fast profile
cargo nextest run --profile fastPostgreSQL not running:
# Start PostgreSQL
docker compose up -d postgres
# Wait for it to be ready
until pg_isready -h localhost -p 5432 -U ampel; do sleep 1; doneSQLite permission errors:
# Use in-memory SQLite
DATABASE_URL=sqlite::memory: cargo nextest runRun with retries:
# 2 retries (default profile)
cargo nextest run
# 3 retries (CI profile)
cargo nextest run --profile ci
# Custom retries
cargo nextest run --retries 5# View recent workflow runs
gh run list
# Watch current workflow
gh run watch
# View specific run
gh run view <run-id>
# View CI status for current branch
gh pr checksCI uses sccache for faster builds:
SCCACHE_GHA_ENABLED: 'true'
RUSTC_WRAPPER: 'sccache'This reduces rebuild time by 30-50%.
- uses: Swatinem/rust-cache@v2
with:
shared-key: 'ci'- Backend unit and integration tests run in parallel
- Frontend tests run in parallel with backend
- Up to 4 test threads per job
Consider implementing:
- Run only tests affected by changed files
- Skip unchanged crates
- Incremental test runs
cargo nextest run --profile fastSpeed: ~2 seconds for 100 tests
make ci-backendRuns lint, format check, and tests
docker compose up -d postgres
make test-backend
cargo tarpaulin --all-features --workspaceSpeed: ~30-60 seconds
cargo nextest run --profile ciSame configuration as GitHub Actions
| Use Case | Command | Speed |
|---|---|---|
| Quick check | cargo nextest run --profile fast |
2s |
| Pre-commit | make test-backend |
5s |
| Full validation | docker compose up -d && cargo nextest run |
30-60s |
| Coverage | cargo llvm-cov --all-features --workspace |
30-60s |
| CI emulation | cargo nextest run --profile ci |
30-60s |