name: Test Orchestrator - All Suites on: push: branches: ["main", "claude/**", "copilot/**", "codex/**"] pull_request: branches: ["main"] workflow_dispatch: inputs: suite: description: 'Specific test suite to run (leave empty for all)' required: false type: choice options: - '' - backend - agents - operator - sdk-python - sdk-typescript - frontend strict_mode: description: 'Enable strict mode (fail-fast)' required: false type: boolean default: false jobs: orchestrator: name: Run Test Orchestrator runs-on: ubuntu-latest timeout-minutes: 30 # Service containers for backend tests services: postgres: image: postgres:15-alpine env: POSTGRES_USER: blackroad POSTGRES_PASSWORD: test_password POSTGRES_DB: blackroad_test 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 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' cache: 'pip' cache-dependency-path: | backend/requirements.txt sdk/python/pyproject.toml agents/requirements.txt - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: 'sdk/typescript/package-lock.json' - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential \ libpq-dev - name: Create test environment file run: | mkdir -p backend cat > backend/.env << EOF DATABASE_URL=postgresql://blackroad:test_password@localhost:5432/blackroad_test DATABASE_ASYNC_URL=postgresql+asyncpg://blackroad:test_password@localhost:5432/blackroad_test REDIS_URL=redis://localhost:6379/0 SECRET_KEY=test-secret-key-for-ci-$(openssl rand -hex 16) ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=30 ENVIRONMENT=testing DEBUG=True ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8000 WALLET_MASTER_KEY=test-master-key-32-characters-long EOF - name: Run Test Orchestrator (All Suites) if: ${{ github.event.inputs.suite == '' }} run: | if [[ "${{ github.event.inputs.strict_mode }}" == "true" ]]; then ./test_all.sh --strict --verbose else ./test_all.sh --verbose fi - name: Run Test Orchestrator (Specific Suite) if: ${{ github.event.inputs.suite != '' }} run: | if [[ "${{ github.event.inputs.strict_mode }}" == "true" ]]; then ./test_all.sh --suite "${{ github.event.inputs.suite }}" --strict --verbose else ./test_all.sh --suite "${{ github.event.inputs.suite }}" --verbose fi - name: Upload test artifacts (Backend) if: always() uses: actions/upload-artifact@v4 with: name: backend-test-results path: | backend/test.db backend/.coverage backend/htmlcov/ backend/pytest-report.xml retention-days: 7 if-no-files-found: ignore - name: Upload test artifacts (Python SDK) if: always() uses: actions/upload-artifact@v4 with: name: python-sdk-test-results path: | sdk/python/.coverage sdk/python/htmlcov/ sdk/python/pytest-report.xml retention-days: 7 if-no-files-found: ignore - name: Upload test artifacts (TypeScript SDK) if: always() uses: actions/upload-artifact@v4 with: name: typescript-sdk-test-results path: | sdk/typescript/coverage/ sdk/typescript/test-results/ retention-days: 7 if-no-files-found: ignore - name: Generate test summary if: always() run: | echo "## ๐Ÿงช Test Orchestrator Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Repository:** ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY echo "**Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Test Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Check the job output above for detailed test results." >> $GITHUB_STEP_SUMMARY # Parallel suite jobs (optional - can run in parallel with orchestrator) backend-coverage: name: Backend Coverage Report runs-on: ubuntu-latest needs: orchestrator if: github.event_name == 'pull_request' services: postgres: image: postgres:15-alpine env: POSTGRES_USER: blackroad POSTGRES_PASSWORD: test_password POSTGRES_DB: blackroad_test 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 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' cache: 'pip' cache-dependency-path: 'backend/requirements.txt' - name: Install dependencies run: | cd backend pip install --upgrade pip pip install -r requirements.txt pip install pytest-cov - name: Create test .env file run: | cd backend cat > .env << EOF DATABASE_URL=postgresql://blackroad:test_password@localhost:5432/blackroad_test DATABASE_ASYNC_URL=postgresql+asyncpg://blackroad:test_password@localhost:5432/blackroad_test REDIS_URL=redis://localhost:6379/0 SECRET_KEY=test-secret-key-for-ci ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=30 ENVIRONMENT=testing DEBUG=True ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8000 WALLET_MASTER_KEY=test-master-key-32-characters-long EOF - name: Run pytest with coverage run: | cd backend pytest -v --cov=app --cov-report=xml --cov-report=term --cov-report=html - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 if: always() with: file: ./backend/coverage.xml flags: backend name: backend-coverage fail_ci_if_error: false token: ${{ secrets.CODECOV_TOKEN }} - name: Comment coverage on PR if: github.event_name == 'pull_request' uses: py-cov-action/python-coverage-comment-action@v3 with: GITHUB_TOKEN: ${{ github.token }} MINIMUM_GREEN: 80 MINIMUM_ORANGE: 60 status-check: name: Final Status Check runs-on: ubuntu-latest needs: [orchestrator] if: always() steps: - name: Check orchestrator status run: | if [[ "${{ needs.orchestrator.result }}" != "success" ]]; then echo "โŒ Test orchestrator failed or was cancelled" exit 1 fi echo "โœ… All test suites passed!" - name: Post status to PR if: github.event_name == 'pull_request' && always() uses: actions/github-script@v7 with: script: | const status = '${{ needs.orchestrator.result }}'; const icon = status === 'success' ? 'โœ…' : 'โŒ'; const message = status === 'success' ? 'All test suites passed!' : 'One or more test suites failed. Check the orchestrator job for details.'; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `## ${icon} Test Orchestrator\n\n${message}\n\n[View Details](${context.payload.pull_request.html_url}/checks)` });