File size: 7,056 Bytes
0e3b21f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77edebf
 
 
0e3b21f
 
 
 
 
77edebf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0e3b21f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""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
    # 404 body is still HTML so the browser renders the message.
    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)
    # The lru_cache on _fetch_report_html caches by arg; use a unique
    # id per test so prior runs don't shortcut this one.
    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


# --- Inline iframe viewer (_format_detail_and_report) ----------------

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="')
    # HTML-escaped content: literal "<!DOCTYPE html>" becomes
    # "&lt;!DOCTYPE html&gt;" inside the attribute.
    assert "&lt;!DOCTYPE html&gt;" 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": ""},  # legacy
    ]:
        df = _stub_row(**row_overrides)
        md, iframe = app._format_detail_and_report(df, _fake_evt())
        assert iframe == "", f"expected empty viewer for {row_overrides}"
        # The metadata panel still renders.
        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())
    # Within the srcdoc value, double-quotes must be HTML-escaped.
    srcdoc = re.search(r'srcdoc="(.*)"\s+style=', iframe, re.DOTALL)
    assert srcdoc is not None
    inner = srcdoc.group(1)
    # No unescaped " inside the attribute value.
    assert '"' not in inner
    assert "&quot;" in inner
    assert "&amp;" in inner