Tests: main #759
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "CI: Tests" | |
| run-name: "Tests: ${{ github.ref_name }}" | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| - 'feature/**' | |
| pull_request: | |
| branches: | |
| - main | |
| - develop | |
| - 'specification/**' | |
| - 'implementation/**' | |
| workflow_dispatch: | |
| inputs: | |
| force_run: | |
| description: 'Force run all tests (ignore change detection)' | |
| type: boolean | |
| default: true | |
| jobs: | |
| test: | |
| name: Run Tests | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for Workload Identity Federation | |
| env: | |
| ENVIRONMENT: test | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for testable changes | |
| id: check | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| HEAD_SHA="${{ github.event.pull_request.head.sha }}" | |
| CHANGED_FILES=$(git diff --name-only $BASE_SHA $HEAD_SHA) | |
| else | |
| CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "") | |
| fi | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| # Check for Python changes outside plots/ (api, core, tests, automation) | |
| # Plot implementations don't have unit tests | |
| TESTABLE_CHANGES=$(echo "$CHANGED_FILES" | grep '\.py$' | grep -v '^plots/' || true) | |
| # Also run tests if test configuration files change | |
| CONFIG_CHANGES=$(echo "$CHANGED_FILES" | grep -E '^(pyproject\.toml|pytest\.ini|conftest\.py)$' || true) | |
| # Force run on workflow_dispatch with force_run=true | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.force_run }}" == "true" ]]; then | |
| echo "Manual trigger with force_run=true, will run tests" | |
| echo "should_test=true" >> $GITHUB_OUTPUT | |
| elif [[ -n "$TESTABLE_CHANGES" ]]; then | |
| echo "Found testable Python changes, will run tests" | |
| echo "should_test=true" >> $GITHUB_OUTPUT | |
| elif [[ -n "$CONFIG_CHANGES" ]]; then | |
| echo "Found test config changes ($CONFIG_CHANGES), will run tests" | |
| echo "should_test=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "No testable changes (only plots/ or non-Python), skipping tests" | |
| echo "should_test=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Set up Python | |
| if: steps.check.outputs.should_test == 'true' | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.14' | |
| - name: Install uv | |
| if: steps.check.outputs.should_test == 'true' | |
| uses: astral-sh/setup-uv@v7 | |
| - name: Install dependencies | |
| if: steps.check.outputs.should_test == 'true' | |
| run: uv sync --extra test | |
| - name: Authenticate to GCP | |
| if: steps.check.outputs.should_test == 'true' | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| project_id: anyplot | |
| workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} | |
| - name: Start Cloud SQL Proxy | |
| if: steps.check.outputs.should_test == 'true' | |
| run: | | |
| # Download Cloud SQL Proxy | |
| curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.3/cloud-sql-proxy.linux.amd64 | |
| chmod +x cloud-sql-proxy | |
| # Start proxy in background (localhost:5432 -> Cloud SQL) | |
| ./cloud-sql-proxy --port 5432 ${{ secrets.INSTANCE_CONNECTION_NAME }} & | |
| # Wait for proxy to be ready | |
| sleep 5 | |
| # Set DATABASE_URL for tests | |
| echo "DATABASE_URL=postgresql+asyncpg://${{ secrets.DB_USER }}:${{ secrets.DB_PASS }}@localhost:5432/${{ secrets.DB_NAME }}" >> $GITHUB_ENV | |
| - name: Run all tests with coverage | |
| if: steps.check.outputs.should_test == 'true' | |
| run: | | |
| uv run pytest tests/unit tests/integration tests/e2e \ | |
| -v --tb=short \ | |
| --cov=core --cov=api --cov=automation --cov=agentic/workflows/modules \ | |
| --cov-report=term-missing \ | |
| --cov-report=xml | |
| # Tests include: | |
| # - Unit tests: Fast, mocked dependencies | |
| # - Integration tests: SQLite in-memory for repository layer | |
| # - E2E tests: Real PostgreSQL with separate 'test' database | |
| - name: Upload coverage to GitHub Artifacts | |
| if: steps.check.outputs.should_test == 'true' && always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-report | |
| path: coverage.xml | |
| retention-days: 30 | |
| - name: Upload coverage to Codecov | |
| if: steps.check.outputs.should_test == 'true' | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage.xml | |
| flags: backend | |
| fail_ci_if_error: false | |
| - name: Skip notice | |
| if: steps.check.outputs.should_test == 'false' | |
| run: echo "::notice::Tests skipped - no testable Python changes (only plots/ or non-Python files)" | |
| test-frontend: | |
| name: Run Frontend Tests | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for frontend changes | |
| id: check | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| HEAD_SHA="${{ github.event.pull_request.head.sha }}" | |
| CHANGED_FILES=$(git diff --name-only $BASE_SHA $HEAD_SHA) | |
| else | |
| CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "") | |
| fi | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| # Check for frontend changes | |
| FRONTEND_CHANGES=$(echo "$CHANGED_FILES" | grep '^app/' || true) | |
| # Force run on workflow_dispatch with force_run=true | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.force_run }}" == "true" ]]; then | |
| echo "Manual trigger with force_run=true, will run frontend tests" | |
| echo "should_test=true" >> $GITHUB_OUTPUT | |
| elif [[ -n "$FRONTEND_CHANGES" ]]; then | |
| echo "Found frontend changes, will run tests" | |
| echo "should_test=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "No frontend changes, skipping frontend tests" | |
| echo "should_test=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Set up Node.js | |
| if: steps.check.outputs.should_test == 'true' | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| cache: 'yarn' | |
| cache-dependency-path: app/yarn.lock | |
| - name: Install dependencies | |
| if: steps.check.outputs.should_test == 'true' | |
| working-directory: app | |
| run: yarn install --frozen-lockfile | |
| - name: Run frontend tests with coverage | |
| if: steps.check.outputs.should_test == 'true' | |
| working-directory: app | |
| run: yarn test --coverage | |
| - name: Upload frontend coverage to Codecov | |
| if: steps.check.outputs.should_test == 'true' | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./app/coverage/coverage-final.json | |
| flags: frontend | |
| fail_ci_if_error: false | |
| - name: Skip notice | |
| if: steps.check.outputs.should_test == 'false' | |
| run: echo "::notice::Frontend tests skipped - no app/ changes" |