| """Unit tests for the report-proxy route and the inline iframe viewer. |
| |
| The Space exposes two paths for the per-submission HTML report: |
| |
| - ``/reports/{submission_id}.html`` (FastAPI route): re-serves the |
| file with ``Content-Type: text/html``. Works under Bearer auth |
| for programmatic clients; gets 404'd by HF's edge for logged-in |
| browser users on a private Space (no auth carryover across |
| same-origin pathname navigations). Kept as a backdoor / for the |
| future public migration. |
| - ``_format_detail_and_report`` (row-click handler): server-side |
| fetches the report via ``hf_hub_download`` and inlines it into |
| an ``<iframe srcdoc=...>``. No browser HTTP request → no edge |
| auth gate → renders for any logged-in user. |
| |
| Tests stub the Hub fetch via monkeypatch so the suite has zero |
| network I/O. |
| """ |
| from __future__ import annotations |
|
|
| import re |
| import types |
|
|
| import pandas as pd |
|
|
| import app |
|
|
|
|
| def test_serve_report_returns_html_when_file_exists(monkeypatch): |
| """Successful fetch -> 200 + text/html + body passthrough.""" |
| monkeypatch.setattr( |
| app, |
| "_fetch_report_html", |
| lambda sid: b"<!DOCTYPE html><html><body>ok</body></html>", |
| ) |
| resp = app.serve_report("sub-test") |
| assert resp.status_code == 200 |
| assert resp.media_type.startswith("text/html") |
| assert resp.body == b"<!DOCTYPE html><html><body>ok</body></html>" |
|
|
|
|
| def test_serve_report_returns_404_when_file_missing(monkeypatch): |
| """``_fetch_report_html`` returning None -> 404 with a small html body.""" |
| monkeypatch.setattr(app, "_fetch_report_html", lambda sid: None) |
| resp = app.serve_report("sub-missing") |
| assert resp.status_code == 404 |
| |
| assert "Report not found" in resp.body.decode("utf-8") |
|
|
|
|
| def test_fetch_report_html_returns_none_on_hub_failure(monkeypatch): |
| """A Hub-side exception is caught and surfaced as None. |
| |
| The serve handler relies on this to keep a transient Hub blip |
| from leaking a stack trace into the Space's HTTP response. |
| """ |
| def boom(*a, **kw): |
| raise RuntimeError("simulated Hub failure") |
|
|
| monkeypatch.setattr(app, "hf_hub_download", boom) |
| |
| |
| assert app._fetch_report_html("sub-failure-probe-unique-1") is None |
|
|
|
|
| def test_proxy_route_is_registered(): |
| """The mounted FastAPI app exposes ``/reports/{submission_id}.html`` as GET. |
| |
| Catches the regression where the ``add_api_route`` call moves |
| below ``mount_gradio_app`` (which would still register the route |
| but make this regression silent until someone tries to hit it). |
| """ |
| routes = [getattr(r, "path", None) for r in app.app.routes] |
| assert "/reports/{submission_id}.html" in routes |
|
|
|
|
| |
|
|
| def _stub_row(**overrides): |
| base = { |
| "submission_id": "sub-test-x", |
| "submission_name": "Test Agent", |
| "submitter_name": "team-test", |
| "status": "completed", |
| "submitted_at": "2026-05-26T12:02:31Z", |
| "notes": None, |
| "model details (optional)": "_None_", |
| "submission_blob_url": "https://example.test/sub-test-x.zip", |
| "report_url": "/reports/sub-test-x.html", |
| "failure_reason": None, |
| } |
| base.update(overrides) |
| return pd.DataFrame([base]) |
|
|
|
|
| def _fake_evt(idx=0): |
| """Minimal stand-in for gr.SelectData with the .index attr we read.""" |
| return types.SimpleNamespace(index=[idx, 0]) |
|
|
|
|
| def test_iframe_viewer_inlines_report_for_modern_row(monkeypatch): |
| """A completed modern row's HTML lands inside <iframe srcdoc>. |
| |
| Confirms the fetched bytes are HTML-escaped (so `<html>` is not |
| re-parsed by the host page) and the iframe carries explicit |
| sizing so Gradio's column flex can't clip it vertically. |
| """ |
| monkeypatch.setattr( |
| app, "_fetch_report_html", |
| lambda sid: b"<!DOCTYPE html><body><h1>Report for " + sid.encode() + b"</h1></body>", |
| ) |
| df = _stub_row() |
| md, iframe = app._format_detail_and_report(df, _fake_evt()) |
| assert "### Test Agent" in md |
| assert iframe.startswith('<iframe srcdoc="') |
| |
| |
| assert "<!DOCTYPE html>" in iframe |
| assert "sub-test-x" in iframe |
| assert 'style="width:100%; height:90vh; border:0; display:block;"' in iframe |
|
|
|
|
| def test_iframe_viewer_empty_for_pending_or_failed_row(monkeypatch): |
| """Rows without a report_url get an empty viewer (no iframe at all). |
| |
| Pending: still evaluating; no report exists yet. Failed: eval |
| crashed; no report uploaded. Legacy: pre-modern-pipeline; the |
| file genuinely doesn't exist on the dataset. All three are |
| handled by the same `report_url == ""` gate. |
| """ |
| monkeypatch.setattr(app, "_fetch_report_html", lambda sid: b"unused") |
| for row_overrides in [ |
| {"status": "pending", "report_url": ""}, |
| {"status": "failed", "report_url": "", "failure_reason": "boom"}, |
| {"status": "completed", "report_url": ""}, |
| ]: |
| df = _stub_row(**row_overrides) |
| md, iframe = app._format_detail_and_report(df, _fake_evt()) |
| assert iframe == "", f"expected empty viewer for {row_overrides}" |
| |
| assert "### Test Agent" in md |
|
|
|
|
| def test_iframe_viewer_falls_back_to_empty_when_fetch_fails(monkeypatch): |
| """If _fetch_report_html returns None (Hub blip), no iframe is emitted. |
| |
| Avoids surfacing a broken iframe on a transient failure. |
| """ |
| monkeypatch.setattr(app, "_fetch_report_html", lambda sid: None) |
| df = _stub_row() |
| _md, iframe = app._format_detail_and_report(df, _fake_evt()) |
| assert iframe == "" |
|
|
|
|
| def test_iframe_viewer_returns_placeholder_on_null_event(): |
| """A null SelectData (no row clicked) returns placeholder + empty viewer.""" |
| df = _stub_row() |
| fake = types.SimpleNamespace(index=None) |
| md, iframe = app._format_detail_and_report(df, fake) |
| assert md == app.DETAIL_PLACEHOLDER |
| assert iframe == "" |
|
|
|
|
| def test_iframe_escape_is_attribute_safe(monkeypatch): |
| """Quotes / ampersands inside the report HTML are escaped properly. |
| |
| A `"` inside the report would otherwise terminate the srcdoc |
| attribute prematurely and break parsing. Regression guard. |
| """ |
| monkeypatch.setattr( |
| app, "_fetch_report_html", |
| lambda sid: b'<html><body>tag: <a href="https://x.test">x</a> & co.</body></html>', |
| ) |
| df = _stub_row() |
| _md, iframe = app._format_detail_and_report(df, _fake_evt()) |
| |
| srcdoc = re.search(r'srcdoc="(.*)"\s+style=', iframe, re.DOTALL) |
| assert srcdoc is not None |
| inner = srcdoc.group(1) |
| |
| assert '"' not in inner |
| assert """ in inner |
| assert "&" in inner |
|
|