Upload 14 files
Browse files- tests/test_custom_ui_shell.py +14 -0
- tests/test_view_models.py +74 -0
tests/test_custom_ui_shell.py
CHANGED
|
@@ -226,3 +226,17 @@ def test_v40_tabbed_ui_logout_and_compact_progress():
|
|
| 226 |
assert 'overflow:auto;padding-right' not in css
|
| 227 |
assert "switchView('progress')" in js
|
| 228 |
assert 'inferredJobUrl' in runs_js
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
assert 'overflow:auto;padding-right' not in css
|
| 227 |
assert "switchView('progress')" in js
|
| 228 |
assert 'inferredJobUrl' in runs_js
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def test_v43_run_view_model_contract_is_exposed_to_custom_ui():
|
| 232 |
+
root = Path(__file__).resolve().parents[1]
|
| 233 |
+
app_py = (root / "app.py").read_text()
|
| 234 |
+
html = (root / "web" / "index.html").read_text()
|
| 235 |
+
js = (root / "web" / "static" / "app.js").read_text() + (root / "web" / "static" / "progress.js").read_text()
|
| 236 |
+
assert '"/api/runs/resumable"' in app_py
|
| 237 |
+
assert '"/api/runs/{run_id}/view"' in app_py
|
| 238 |
+
assert "build_run_view_model" in app_py
|
| 239 |
+
for element_id in ["runSummaryCard", "activityFeed", "zeroGpuChecklist", "issueCard", "spaceTestPreview"]:
|
| 240 |
+
assert element_id in html
|
| 241 |
+
assert "bootstrapRunRecovery" in js
|
| 242 |
+
assert "renderRunViewModel" in js
|
tests/test_view_models.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime, timezone
|
| 2 |
+
|
| 3 |
+
from src.view_models import build_run_view_model, find_latest_resumable_run, normalize_run_status
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def _bundle(status="running", events=None, **extra):
|
| 7 |
+
bundle = {
|
| 8 |
+
"summary": {
|
| 9 |
+
"run_id": "run-1",
|
| 10 |
+
"status": status,
|
| 11 |
+
"model_id": "org/model",
|
| 12 |
+
"target_space": "alice/demo",
|
| 13 |
+
"target_space_url": "https://huggingface.co/spaces/alice/demo",
|
| 14 |
+
"created_at": "2026-06-03T10:00:00+00:00",
|
| 15 |
+
"updated_at": "2026-06-03T10:01:00+00:00",
|
| 16 |
+
"selected_hardware": "zero-a10g",
|
| 17 |
+
},
|
| 18 |
+
"state": {"status": status},
|
| 19 |
+
"launch": {"status": status, "job_id": "job-1", "created_by": "alice"},
|
| 20 |
+
"events": events or [],
|
| 21 |
+
"inference_gate": {},
|
| 22 |
+
"generation_smoke": {},
|
| 23 |
+
"hardware_strategy": {},
|
| 24 |
+
"technical_blockers": {},
|
| 25 |
+
}
|
| 26 |
+
bundle.update(extra)
|
| 27 |
+
return bundle
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def test_view_model_maps_running_run_to_product_pipeline():
|
| 31 |
+
bundle = _bundle(
|
| 32 |
+
events=[
|
| 33 |
+
{"ts": "2026-06-03T10:00:10+00:00", "step": "model_analysis", "status": "success", "message": "Analyzed model"},
|
| 34 |
+
{"ts": "2026-06-03T10:00:20+00:00", "step": "pi_run", "status": "running", "message": "Generating files"},
|
| 35 |
+
]
|
| 36 |
+
)
|
| 37 |
+
view = build_run_view_model("run-1", bundle, bucket_source="alice/space-factory-runs", now=datetime(2026, 6, 3, 10, 2, tzinfo=timezone.utc))
|
| 38 |
+
assert view["header"]["status"] == "running"
|
| 39 |
+
assert view["header"]["current_phase"] == "generate_files"
|
| 40 |
+
assert [step["label"] for step in view["pipeline"]][:3] == ["Plan", "Generate Files", "Push to Hub"]
|
| 41 |
+
assert any(item["agent"] == "Coder Agent" for item in view["activity"])
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def test_view_model_manual_hardware_is_waiting_manual_action():
|
| 45 |
+
bundle = _bundle(status="manual_hardware_required", hardware_strategy={"manual_action_required": True})
|
| 46 |
+
view = build_run_view_model("run-1", bundle, bucket_source="alice/space-factory-runs", now=datetime(2026, 6, 3, 10, 2, tzinfo=timezone.utc))
|
| 47 |
+
assert view["header"]["status"] == "waiting_manual_action"
|
| 48 |
+
assert view["actions"]["requires_manual_action"] is True
|
| 49 |
+
assert view["diagnostics"]["issue"]["status"] == "action_required"
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def test_view_model_success_marks_pipeline_completed_and_live_test_passed():
|
| 53 |
+
bundle = _bundle(status="full_inference_success", generation_smoke={"ok": True, "latency_seconds": 12.4})
|
| 54 |
+
view = build_run_view_model("run-1", bundle, bucket_source="alice/space-factory-runs", now=datetime(2026, 6, 3, 10, 2, tzinfo=timezone.utc))
|
| 55 |
+
assert view["header"]["status"] == "succeeded"
|
| 56 |
+
assert all(step["status"] == "completed" for step in view["pipeline"])
|
| 57 |
+
assert view["space_test"]["status"] == "passed"
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def test_view_model_marks_old_non_terminal_run_as_stale():
|
| 61 |
+
bundle = _bundle(status="running")
|
| 62 |
+
status = normalize_run_status(bundle, now=datetime(2026, 6, 4, 10, 2, tzinfo=timezone.utc))
|
| 63 |
+
assert status["global_status"] == "stale"
|
| 64 |
+
assert status["is_stale"] is True
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def test_find_latest_resumable_run_ignores_terminal_and_manual():
|
| 68 |
+
runs = [
|
| 69 |
+
{"run_id": "old-success", "status": "full_inference_success", "updated_at": "2026-06-03T10:00:00+00:00"},
|
| 70 |
+
{"run_id": "manual", "status": "manual_hardware_required", "updated_at": "2026-06-03T10:03:00+00:00"},
|
| 71 |
+
{"run_id": "active", "status": "running", "updated_at": "2026-06-03T10:02:00+00:00"},
|
| 72 |
+
]
|
| 73 |
+
picked = find_latest_resumable_run(runs, now=datetime(2026, 6, 3, 10, 4, tzinfo=timezone.utc))
|
| 74 |
+
assert picked["run_id"] == "active"
|