name: Tests on: push: branches: [master, main] paths: - 'backend/**' - 'exam-app/**' - 'requirements*.txt' - '.github/workflows/test.yml' pull_request: branches: [master, main] paths: - 'backend/**' - 'exam-app/**' - 'requirements*.txt' jobs: # ── Backend unit + integration tests ──────────────────────────────────────── backend-tests: name: Backend Tests (Python ${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: ["3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: pip - name: Install dependencies run: | pip install -r requirements.txt pip install -r requirements-dev.txt - name: Run fast test suite (no live AI, no fault injection) env: ANTHROPIC_AUTH_TOKEN: test-token-ci JWT_SECRET: ${{ secrets.JWT_SECRET || 'ci-test-secret-at-least-32-chars-long' }} ADMIN_KEY: ci-admin-key ADMIN_MASTER_SECRET: "" SQLITE_PATH: ":memory:" run: | PYTHONPATH=backend python3 -m pytest backend/tests/ \ -m "not live_ai and not fault_injection" \ --tb=short \ -q \ --randomly-seed=0 \ -x - name: Run fault injection tests env: ANTHROPIC_AUTH_TOKEN: test-token-ci JWT_SECRET: ${{ secrets.JWT_SECRET || 'ci-test-secret-at-least-32-chars-long' }} ADMIN_KEY: ci-admin-key ADMIN_MASTER_SECRET: "" SQLITE_PATH: ":memory:" run: | PYTHONPATH=backend python3 -m pytest backend/tests/ \ -m "fault_injection" \ --tb=short \ -q continue-on-error: true # fault injection is informational until stabilised # ── Frontend unit tests ────────────────────────────────────────────────────── frontend-tests: name: Frontend Tests (Node ${{ matrix.node-version }}) runs-on: ubuntu-latest strategy: matrix: node-version: ["20"] defaults: run: working-directory: exam-app steps: - uses: actions/checkout@v4 - name: Set up Node ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: npm cache-dependency-path: exam-app/package-lock.json - name: Install dependencies run: npm ci - name: Run Vitest unit tests run: npm test # ── Mutation score gate (weekly, manual, or on demand) ─────────────────────── mutation-score: name: Mutation Score Gate runs-on: ubuntu-latest if: github.event_name == 'workflow_dispatch' || github.event.schedule != '' # Run manually via: gh workflow run test.yml # Or add to a scheduled cron job by uncommenting below: # on: # schedule: # - cron: '0 3 * * 1' # Monday 03:00 UTC steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - name: Install dependencies run: | pip install -r requirements.txt pip install -r requirements-dev.txt - name: Run mutation testing env: ANTHROPIC_AUTH_TOKEN: test-token-ci JWT_SECRET: ci-test-secret-at-least-32-chars-long ADMIN_KEY: ci-admin-key ADMIN_MASTER_SECRET: "" run: | PYTHONPATH=backend mutmut run \ --paths-to-mutate backend/app/agent/ \ --tests-dir backend/tests/ \ || true # mutmut exits non-zero when mutations survive — handled below - name: Check mutation score gate run: | PYTHONPATH=backend python3 tools/check_mutation_score.py --min 70