Files
geointel/backend/tests/test_sprint65_project_report_polish.py
T
Jens faeb58ef6d
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
Initial public release
2026-08-31 21:56:53 +02:00

126 lines
4.4 KiB
Python

from __future__ import annotations
from app.services.export_service import ExportService
def test_project_report_html_uses_handoff_layout_and_print_styles() -> None:
summary = {
"project": {
"id": "project-1",
"name": "Demo <Kempen>",
"description": "QA handoff",
"region": "Kempen",
"status": "active",
},
"datasets": [
{
"id": "dataset-1",
"name": "reference.geojson",
"dataset_type": "vector",
"dataset_role": "reference",
"source_name": "fixture",
"reference_layer_name": "buildings",
"status": "ready",
"crs": "EPSG:4326",
"bounds_json": None,
"feature_count": 2,
}
],
"quality_checks": [
{
"id": "quality-1",
"analysis_run_id": None,
"candidate_dataset_id": "candidate-1",
"reference_dataset_id": "dataset-1",
"check_type": "demo_candidate_vs_reference",
"status": "ok",
"score": 0.75,
}
],
"exports": [
{
"id": "export-1",
"analysis_run_id": None,
"export_type": "project_metadata_json",
"storage_path": "storage/exports/metadata.json",
"metadata_json": {"readiness_state": "ready"},
"created_at": "2026-06-18T10:00:00+00:00",
}
],
"readiness_summary": {
"overall_state": "ready",
"items": [
{"key": "project", "label": "Project", "state": "ready", "detail": "Demo <Kempen> (Kempen)"},
{"key": "datasets", "label": "Datasets", "state": "ready", "detail": "1/1 ready"},
],
"counts": {
"area_count": 1,
"dataset_count": 1,
"ready_dataset_count": 1,
"vector_dataset_count": 1,
"raster_dataset_count": 0,
"reference_dataset_count": 1,
"quality_check_count": 1,
"export_count": 1,
},
},
"known_limitations": ["No live GRB/OSM/Sentinel fetching is performed by the report export."],
}
html = ExportService._render_project_report_html(summary)
assert 'class="report-shell"' in html
assert 'class="report-hero"' in html
assert 'class="report-scorecards"' in html
assert 'class="readiness-pill readiness-ready"' in html
assert 'class="section-kicker"' in html
assert "@media print" in html
assert "page-break-inside: avoid" in html
assert "Generated from persisted GeoIntel state" in html
assert "Dataset inventory" in html
assert "QA/QC evidence" in html
assert "Artifact history" in html
assert "Demo &lt;Kempen&gt;" in html
def test_project_report_html_escapes_table_values_in_polished_layout() -> None:
summary = {
"project": {
"id": "project-1",
"name": "<script>alert(1)</script>",
"description": "<b>unsafe</b>",
"region": "Kempen",
"status": "active",
},
"datasets": [],
"quality_checks": [],
"exports": [],
"readiness_summary": {
"overall_state": "needs_attention",
"items": [
{"key": "project", "label": "<Project>", "state": "waiting", "detail": "<missing>"},
],
"counts": {
"area_count": 0,
"dataset_count": 0,
"ready_dataset_count": 0,
"vector_dataset_count": 0,
"raster_dataset_count": 0,
"reference_dataset_count": 0,
"quality_check_count": 0,
"export_count": 0,
},
},
"known_limitations": ["<unsafe limitation>"],
}
html = ExportService._render_project_report_html(summary)
assert "<script>alert(1)</script>" not in html
assert "&lt;script&gt;alert(1)&lt;/script&gt;" in html
assert "<b>unsafe</b>" not in html
assert "&lt;b&gt;unsafe&lt;/b&gt;" in html
assert "&lt;Project&gt;" in html
assert "&lt;unsafe limitation&gt;" in html
assert 'class="readiness-pill readiness-needs_attention"' in html