"""Unit tests for the submit-tab pending-row builder. C4 contract: ``_build_pending_row`` defaults the three Bundle 1+2 schema fields (``validation_status="unvalidated"``, ``validation_method=None``, ``hf_username=None``) and keeps the existing metadata + sha256 fields intact. Hub I/O (``_resolve_data_revision`` reads from ``HfApi.dataset_info``) is monkeypatched out, so the suite has zero network traffic. """ from __future__ import annotations import submit def _stub_meta() -> dict: """Minimum meta.json shape that survives ``_load_and_validate_meta``.""" return { "submitter_name": "team-test", "submission_name": "Stub Agent v1", "agent_url": "https://github.com/example/stub-agent", "notes": "test row, not a real submission", "agree_to_publish": True, } def test_pending_row_defaults_new_fields(monkeypatch): """Three Bundle 1+2 fields land with their schema defaults.""" monkeypatch.setattr(submit, "_resolve_data_revision", lambda: "test-rev") row = submit._build_pending_row( submission_id="sub-test-x", meta=_stub_meta(), blob_url="https://huggingface.co/datasets/example/sub-test-x.zip", submission_sha256="a" * 64, ) assert row["validation_status"] == "unvalidated" assert row["validation_method"] is None assert row["hf_username"] is None def test_pending_row_preserves_sha256(monkeypatch): """Existing dedup path's row-level half: sha256 still gets stamped on the row.""" monkeypatch.setattr(submit, "_resolve_data_revision", lambda: "test-rev") expected_hash = "f" * 64 row = submit._build_pending_row( submission_id="sub-test-x", meta=_stub_meta(), blob_url="https://huggingface.co/datasets/example/sub-test-x.zip", submission_sha256=expected_hash, ) assert row["submission_sha256"] == expected_hash def test_pending_row_populates_hf_username_when_provided(monkeypatch): """C10 OAuth path: profile.username flows into the row's hf_username. The submit handler reads ``gr.OAuthProfile`` (injected by Gradio) and passes ``profile.username`` through as a kwarg. This test exercises just the row builder's side of that handoff so a refactor that drops the kwarg gets caught. """ monkeypatch.setattr(submit, "_resolve_data_revision", lambda: "test-rev") row = submit._build_pending_row( submission_id="sub-test-x", meta=_stub_meta(), blob_url="https://huggingface.co/datasets/example/sub-test-x.zip", submission_sha256="a" * 64, hf_username="alice", ) assert row["hf_username"] == "alice" def test_pending_row_hf_username_defaults_to_none(monkeypatch): """Omitting the kwarg keeps `hf_username` null. Covers the pre-OAuth callers (test fixtures, scripts) that don't have a profile in scope. Pre-C10 row writers and any future non-OAuth caller default cleanly. """ monkeypatch.setattr(submit, "_resolve_data_revision", lambda: "test-rev") row = submit._build_pending_row( submission_id="sub-test-x", meta=_stub_meta(), blob_url="https://huggingface.co/datasets/example/sub-test-x.zip", submission_sha256="a" * 64, ) assert row["hf_username"] is None def test_pending_row_preserves_existing_metadata(monkeypatch): """Pre-Bundle-1+2 fields keep their values from meta + args. Regression guard: a future refactor of ``_build_pending_row`` that accidentally drops one of these keys would silently change the schema of every row the Space writes. """ monkeypatch.setattr(submit, "_resolve_data_revision", lambda: "test-rev") meta = _stub_meta() row = submit._build_pending_row( submission_id="sub-test-x", meta=meta, blob_url="https://example.test/sub-test-x.zip", submission_sha256="0" * 64, ) assert row["submission_id"] == "sub-test-x" assert row["status"] == "pending" assert row["failure_reason"] is None assert row["submitter_name"] == meta["submitter_name"] assert row["submission_name"] == meta["submission_name"] assert row["agent_url"] == meta["agent_url"] assert row["notes"] == meta["notes"] assert row["submission_blob_url"] == "https://example.test/sub-test-x.zip" assert row["cadgenbench_data_revision"] == "test-rev" # Score-shaped fields are null on a fresh pending row. for k in ( "aggregate_score", "validity_rate", "score_by_task_type", "per_task_scores", "per_fixture_scores", "per_fixture_breakdown", ): assert row[k] is None