From e976fb4e87e5cbe9ae46afee5e018d3ccd2dafa1 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Sat, 25 Apr 2026 12:01:05 -0400 Subject: [PATCH] fix(ci): mock AI provider in record_decision test + cache pip/npm + drop term-missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three changes that get PR #150 to a green CI gate: 1. **test_record_decision_persists_and_bumps_state_version** — the `decision: draft_template` path calls `_extract_template_parameters` (TemplateExtractionService → AI provider). CI doesn't set ANTHROPIC_API_KEY/GOOGLE_AI_API_KEY, so the endpoint raised `RuntimeError: No AI provider configured` and returned 500. The test isn't exercising the AI integration — patched the extractor with an AsyncMock returning a minimal valid `{templated_body, parameters}` dict. Verified locally: the test now passes. 2. **pip + npm caches** in backend, frontend, and e2e jobs. Keyed on the hash of requirements*.txt / package-lock.json with a runner-os restore-key fallback. Saves ~30-60s per run on cache hit. 3. **Pytest invocation tightened**: - Dropped `--cov-report=term-missing` — the custom "Display coverage summary" step below parses coverage.json and prints the same module list more concisely. Term-missing dumps every uncovered line which adds ~5-10s of stdout. - Added `--maxfail=10` so a structural breakage (fixture explosion, DB unreachable) bails after 10 errors instead of running the full 25-min suite. Tunable. Co-Authored-By: Claude Opus 4.7 --- .gitea/workflows/ci.yml | 38 ++++++++++++++++++- .../tests/test_session_suggested_fixes_api.py | 22 ++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 0ea10f73..a801f984 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -43,6 +43,14 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }}-${{ hashFiles('backend/requirements.txt', 'backend/requirements-dev.txt') }} + restore-keys: | + pip-${{ runner.os }}- + - name: Install system dependencies run: | apt-get update @@ -58,7 +66,11 @@ jobs: run: cd backend && python scripts/check_tenant_filters.py - name: Run tests with coverage - run: cd backend && python -m pytest --override-ini="addopts=" --cov=app --cov-report=term-missing --cov-report=json:coverage.json --cov-fail-under=50 + # term-missing dropped — the custom "Display coverage summary" step + # below parses coverage.json and prints the same info more concisely. + # --maxfail=10 short-circuits on structural breakage so we don't burn + # 25 minutes when a fixture explodes. + run: cd backend && python -m pytest --override-ini="addopts=" --maxfail=10 --cov=app --cov-report=json:coverage.json --cov-fail-under=50 - name: Display coverage summary if: always() @@ -86,6 +98,14 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Cache npm + uses: actions/cache@v3 + with: + path: ~/.npm + key: npm-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }} + restore-keys: | + npm-${{ runner.os }}- + - name: Install dependencies run: cd frontend && npm ci @@ -136,6 +156,22 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }}-${{ hashFiles('backend/requirements.txt', 'backend/requirements-dev.txt') }} + restore-keys: | + pip-${{ runner.os }}- + + - name: Cache npm + uses: actions/cache@v3 + with: + path: ~/.npm + key: npm-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }} + restore-keys: | + npm-${{ runner.os }}- + - name: Install backend dependencies run: pip install --break-system-packages -r backend/requirements.txt -r backend/requirements-dev.txt diff --git a/backend/tests/test_session_suggested_fixes_api.py b/backend/tests/test_session_suggested_fixes_api.py index 427e4d32..dfb78243 100644 --- a/backend/tests/test_session_suggested_fixes_api.py +++ b/backend/tests/test_session_suggested_fixes_api.py @@ -218,11 +218,23 @@ async def test_record_decision_persists_and_bumps_state_version( test_db.add(fix) await test_db.commit() - r = await client.post( - f"/api/v1/ai-sessions/{session.id}/suggested-fixes/{fix.id}/decision", - headers=auth_headers, - json={"decision": "draft_template"}, - ) + # The draft_template path calls TemplateExtractionService, which needs an + # AI provider configured. CI doesn't set ANTHROPIC_API_KEY/GOOGLE_AI_API_KEY, + # and this test isn't exercising the AI integration — patch the extractor + # with a minimal valid response so the rest of the decision flow runs. + extractor_stub = AsyncMock(return_value={ + "templated_body": "Write-Output 'ok'", + "parameters": [], + }) + with patch( + "app.api.endpoints.session_suggested_fixes._extract_template_parameters", + extractor_stub, + ): + r = await client.post( + f"/api/v1/ai-sessions/{session.id}/suggested-fixes/{fix.id}/decision", + headers=auth_headers, + json={"decision": "draft_template"}, + ) assert r.status_code == 200 assert r.json()["user_decision"] == "draft_template"