| """Phase 1D β API payload integration tests for DeckQualityReport. |
| |
| These tests target the small, dependency-light helper |
| ``agent.deck_quality.attach_quality_report`` that the API routes |
| ``GET /api/slides/{task_id}`` and ``GET /api/share/{token}`` use to attach |
| the report to their JSON responses without a DB migration. |
| |
| They are intentionally conftest-free and import only ``agent.*`` modules |
| so they run cleanly under:: |
| |
| python -m pytest --noconftest -p no:cacheprovider \\ |
| tests/test_api_deck_quality_payload.py -v |
| """ |
|
|
| from __future__ import annotations |
|
|
| import copy |
| import json |
| import sys |
| from pathlib import Path |
|
|
| |
| _BACKEND_ROOT = Path(__file__).resolve().parent.parent |
| if str(_BACKEND_ROOT) not in sys.path: |
| sys.path.insert(0, str(_BACKEND_ROOT)) |
|
|
| from agent.deck_quality import attach_quality_report |
|
|
|
|
| VALID_DECK: list[dict] = [ |
| { |
| "id": "slide-000", |
| "layout": "title", |
| "title": "Topic", |
| "subtitle": "Sub", |
| "eyebrow": "Presentation", |
| }, |
| { |
| "id": "slide-001", |
| "layout": "bullets", |
| "title": "Drivers", |
| "bullets": ["a", "b", "c"], |
| }, |
| { |
| "id": "slide-002", |
| "layout": "closing", |
| "title": "Bye", |
| "subtitle": "", |
| "cta": "Q&A", |
| }, |
| ] |
|
|
|
|
| def _payload_for(slides: list[dict]) -> dict: |
| return { |
| "task_id": "task-abc", |
| "topic": "Renewables", |
| "theme": "Editorial", |
| "slide_count": len(slides), |
| "slides": slides, |
| } |
|
|
|
|
| |
|
|
|
|
| def test_attach_quality_report_adds_deck_quality_key(): |
| payload = _payload_for(VALID_DECK) |
| out = attach_quality_report(payload, VALID_DECK) |
|
|
| |
| for k in ("task_id", "topic", "theme", "slide_count", "slides"): |
| assert out[k] == payload[k] |
|
|
| |
| assert "deck_quality" in out |
| quality = out["deck_quality"] |
| assert isinstance(quality, dict) |
| assert set(quality.keys()) == { |
| "ok", |
| "slide_count", |
| "valid_count", |
| "invalid_count", |
| "errors", |
| "repair_actions", |
| "repair_preview", |
| "summary", |
| "source_warnings", |
| } |
|
|
|
|
| def test_attach_quality_report_does_not_mutate_inputs(): |
| payload = _payload_for(VALID_DECK) |
| payload_snapshot = copy.deepcopy(payload) |
| slides_snapshot = copy.deepcopy(VALID_DECK) |
|
|
| out = attach_quality_report(payload, VALID_DECK) |
|
|
| assert payload == payload_snapshot, "payload must not be mutated" |
| assert VALID_DECK == slides_snapshot, "slides must not be mutated" |
| assert out is not payload, "must return a new dict" |
|
|
|
|
| def test_attach_quality_report_is_json_serializable(): |
| payload = _payload_for(VALID_DECK) |
| out = attach_quality_report(payload, VALID_DECK) |
| |
| encoded = json.dumps(out, default=str) |
| decoded = json.loads(encoded) |
| assert decoded["deck_quality"]["ok"] is True |
| assert decoded["deck_quality"]["slide_count"] == len(VALID_DECK) |
|
|
|
|
| |
|
|
|
|
| def test_attach_quality_report_flags_invalid_chart(): |
| bad = copy.deepcopy(VALID_DECK) |
| |
| bad.insert( |
| 2, |
| { |
| "id": "slide-002b", |
| "layout": "chart", |
| "title": "Capacity", |
| "chart_type": "bar", |
| "chart_data": { |
| "labels": ["a", "b"], |
| "values": [1.0, 2.0], |
| "unit": "GW", |
| "source": "IRENA", |
| }, |
| |
| }, |
| ) |
| payload = _payload_for(bad) |
| out = attach_quality_report(payload, bad) |
| quality = out["deck_quality"] |
|
|
| assert quality["ok"] is False |
| assert quality["invalid_count"] >= 1 |
| assert any( |
| e["path"] == "subtitle" and e["code"] == "missing" and e["layout"] == "chart" |
| for e in quality["errors"] |
| ), quality["errors"] |
| assert quality["summary"]["repairs_needed"] >= 1 |
|
|
|
|
| |
|
|
|
|
| def test_attach_quality_report_handles_empty_slides(): |
| payload = _payload_for([]) |
| out = attach_quality_report(payload, []) |
| quality = out["deck_quality"] |
| assert quality["ok"] is True |
| assert quality["slide_count"] == 0 |
| assert quality["valid_count"] == 0 |
| assert quality["invalid_count"] == 0 |
| assert quality["errors"] == [] |
| assert quality["repair_actions"] == [] |
|
|
|
|
| def test_attach_quality_report_handles_non_list_slides(): |
| payload = {"slides": None} |
| out = attach_quality_report(payload, None) |
| quality = out["deck_quality"] |
| assert quality["ok"] is False |
| assert quality["summary"].get("deck_payload") == "invalid" |
|
|