126 lines
4.4 KiB
Python
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 <Kempen>" 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 "<script>alert(1)</script>" in html
|
|
assert "<b>unsafe</b>" not in html
|
|
assert "<b>unsafe</b>" in html
|
|
assert "<Project>" in html
|
|
assert "<unsafe limitation>" in html
|
|
assert 'class="readiness-pill readiness-needs_attention"' in html
|