fix(scores,reports): fix scores/history 500, disable unfinished report generation cleanly
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled

- /scores/history was annotated -> dict but the service actually returns
  a list, so FastAPI's response validation raised an unhandled exception,
  surfacing as a raw 500 on every call. Fixed the annotation.
- The professional_reports.py endpoints (PDF/DOCX/HTML generation) back
  a Reports feature the frontend already hides entirely (unfinished).
  Hitting them directly fell through to whatever the render pipeline
  raised (e.g. missing weasyprint system deps) as an unhelpful 500.
  Added a REPORTS_ENABLED setting (off by default) so they now return a
  clean 503 instead, without removing the routes or their test coverage.
This commit is contained in:
kitos
2026-07-24 15:24:06 +02:00
parent 07403cbd9d
commit e951567ba1
5 changed files with 59 additions and 4 deletions
@@ -16,7 +16,8 @@ def test_purple_campaign_pdf_download(mock_gen, client, auth_headers, db):
f.write(b"%PDF-1.4 fake")
mock_gen.return_value = fake_pdf
with patch.object(settings, "REPORT_OUTPUT_DIR", tmpdir):
with patch.object(settings, "REPORT_OUTPUT_DIR", tmpdir), \
patch.object(settings, "REPORTS_ENABLED", True):
campaign = Campaign(name="Export Camp", status="active")
db.add(campaign)
db.commit()
@@ -38,7 +39,8 @@ def test_coverage_summary_html(mock_gen, client, auth_headers):
f.write("<html><body>ok</body></html>")
mock_gen.return_value = fake_html
with patch.object(settings, "REPORT_OUTPUT_DIR", tmpdir):
with patch.object(settings, "REPORT_OUTPUT_DIR", tmpdir), \
patch.object(settings, "REPORTS_ENABLED", True):
r = client.get(
"/api/v1/reports/generate/coverage-summary",
params={"format": "html"},
@@ -46,3 +48,15 @@ def test_coverage_summary_html(mock_gen, client, auth_headers):
)
assert r.status_code == 200
assert "text/html" in r.headers["content-type"]
def test_reports_disabled_by_default_returns_503(client, auth_headers):
"""Reports are unfinished — the frontend hides the UI entirely, and the
backend must refuse cleanly rather than let a half-working render
pipeline (e.g. missing weasyprint system deps) surface a raw 500."""
r = client.get(
"/api/v1/reports/generate/coverage-summary",
params={"format": "pdf"},
headers=auth_headers,
)
assert r.status_code == 503
+19
View File
@@ -0,0 +1,19 @@
"""GET /scores/history returns a list of weekly data points — regression
test for a response-model mismatch (endpoint annotated -> dict while the
service actually returns a list, causing a raw 500 on serialization)."""
def test_scores_history_returns_a_list(client, auth_headers):
resp = client.get("/api/v1/scores/history", headers=auth_headers)
assert resp.status_code == 200, resp.text
body = resp.json()
assert isinstance(body, list)
def test_scores_history_accepts_all_periods(client, auth_headers):
for period in ("30d", "90d", "1y"):
resp = client.get(
"/api/v1/scores/history", params={"period": period}, headers=auth_headers,
)
assert resp.status_code == 200, resp.text
assert isinstance(resp.json(), list)