Upload 27 files
Browse files
tests/test_v52_ui_fixes.py
CHANGED
|
@@ -39,6 +39,6 @@ def test_runs_explorer_is_limited_to_five_rows_with_pagination():
|
|
| 39 |
|
| 40 |
def test_progress_endpoint_uses_view_model_links_for_job_url():
|
| 41 |
app_py = Path("app.py").read_text(encoding="utf-8")
|
| 42 |
-
assert '"version": "
|
| 43 |
assert 'view = build_run_view_model(run_id, bundle, bucket_source=bucket_source)' in app_py
|
| 44 |
assert '"links": view.get("links")' in app_py
|
|
|
|
| 39 |
|
| 40 |
def test_progress_endpoint_uses_view_model_links_for_job_url():
|
| 41 |
app_py = Path("app.py").read_text(encoding="utf-8")
|
| 42 |
+
assert '"version": "v60-cancel-persist-delete-run"' in app_py
|
| 43 |
assert 'view = build_run_view_model(run_id, bundle, bucket_source=bucket_source)' in app_py
|
| 44 |
assert '"links": view.get("links")' in app_py
|
tests/test_v57_live_pi_traces.py
CHANGED
|
@@ -15,7 +15,7 @@ def test_bucket_and_progress_endpoint_expose_pi_live_log():
|
|
| 15 |
app = Path("app.py").read_text(encoding="utf-8")
|
| 16 |
assert '"pi_live_log": _safe_read_text' in bucket
|
| 17 |
assert '"pi_output_log": _safe_read_text' in bucket
|
| 18 |
-
assert '"pi_live_log":
|
| 19 |
|
| 20 |
|
| 21 |
def test_ui_has_pi_live_trace_panel_and_renderer():
|
|
@@ -30,4 +30,4 @@ def test_ui_has_pi_live_trace_panel_and_renderer():
|
|
| 30 |
|
| 31 |
|
| 32 |
def test_app_version_v57():
|
| 33 |
-
assert '"version": "
|
|
|
|
| 15 |
app = Path("app.py").read_text(encoding="utf-8")
|
| 16 |
assert '"pi_live_log": _safe_read_text' in bucket
|
| 17 |
assert '"pi_output_log": _safe_read_text' in bucket
|
| 18 |
+
assert '"pi_live_log": pi_stdout[-12000:]' in app
|
| 19 |
|
| 20 |
|
| 21 |
def test_ui_has_pi_live_trace_panel_and_renderer():
|
|
|
|
| 30 |
|
| 31 |
|
| 32 |
def test_app_version_v57():
|
| 33 |
+
assert '"version": "v60-cancel-persist-delete-run"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v58_stable_refresh_pi_stdout.py
CHANGED
|
@@ -26,26 +26,6 @@ def test_pi_live_panel_is_stdout_not_trace_jsonl():
|
|
| 26 |
assert "No Pi stdout yet" in js
|
| 27 |
|
| 28 |
|
| 29 |
-
def test_active_run_uses_bucket_file_links_instead_of_duplicate_space_job_buttons():
|
| 30 |
-
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 31 |
-
active = html.split('id="activeRunPanel"', 1)[1].split('id="runsExplorerPanel"', 1)[0]
|
| 32 |
-
assert 'id="openRunState"' in active
|
| 33 |
-
assert 'id="openRunReport"' in active
|
| 34 |
-
assert 'id="openRunLogs"' in active
|
| 35 |
-
assert 'id="openRunTraces"' in active
|
| 36 |
-
assert 'id="openJob"' not in active
|
| 37 |
-
assert 'id="openSpace"' not in active
|
| 38 |
-
assert 'id="openSettings"' not in active
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def test_bucket_file_links_are_rendered_from_run_and_bucket():
|
| 42 |
-
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 43 |
-
assert "bucketBlobUrl" in js
|
| 44 |
-
assert "bucketTreeUrl" in js
|
| 45 |
-
assert "openRunState" in js
|
| 46 |
-
assert "openRunLogs" in js
|
| 47 |
-
assert "openRunTraces" in js
|
| 48 |
-
|
| 49 |
|
| 50 |
def test_app_version_v58():
|
| 51 |
-
assert '"version": "
|
|
|
|
| 26 |
assert "No Pi stdout yet" in js
|
| 27 |
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
def test_app_version_v58():
|
| 31 |
+
assert '"version": "v60-cancel-persist-delete-run"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v59_job_log_progress_clean.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
from app import _events_from_job_logs, _merge_events, _normalize_job_stage
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_progress_endpoint_polls_hf_job_logs_and_extracts_worker_events():
|
| 7 |
+
app_py = Path("app.py").read_text(encoding="utf-8")
|
| 8 |
+
assert "inspect_job_safe(job_id" in app_py
|
| 9 |
+
assert "fetch_recent_logs_safe(job_id" in app_py
|
| 10 |
+
assert "_events_from_job_logs(job_logs)" in app_py
|
| 11 |
+
assert "_merge_events(bundle.get" in app_py
|
| 12 |
+
assert '"job_logs": job_logs[-20000:]' in app_py
|
| 13 |
+
assert '"job_log_events_count": len(job_log_events)' in app_py
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_job_log_event_parser_extracts_json_events_from_logs():
|
| 17 |
+
logs = """noise
|
| 18 |
+
{"ts":"2026-06-02T10:00:00+00:00","step":"pi_run","status":"running","message":"Pi is still working"}
|
| 19 |
+
prefix {"ts":"2026-06-02T10:00:01+00:00","step":"upload_files","status":"success","message":"Uploaded"}
|
| 20 |
+
not json
|
| 21 |
+
"""
|
| 22 |
+
events = _events_from_job_logs(logs)
|
| 23 |
+
assert [event["step"] for event in events] == ["pi_run", "upload_files"]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test_merge_events_deduplicates_bucket_and_job_log_events():
|
| 27 |
+
event = {"ts": "t", "step": "pi_run", "status": "running", "message": "same"}
|
| 28 |
+
merged = _merge_events([event], [dict(event), {"ts": "t2", "step": "done", "status": "success"}])
|
| 29 |
+
assert len(merged) == 2
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def test_job_stage_normalization():
|
| 33 |
+
assert _normalize_job_stage("JobStage.RUNNING") == "running"
|
| 34 |
+
assert _normalize_job_stage("COMPLETED") == "success"
|
| 35 |
+
assert _normalize_job_stage("CANCELLED") == "cancelled"
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def test_active_run_only_keeps_cancel_button():
|
| 39 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 40 |
+
active = html.split('id="activeRunPanel"', 1)[1].split('id="runsExplorerPanel"', 1)[0]
|
| 41 |
+
assert 'id="cancelRun"' in active
|
| 42 |
+
for removed in ['id="openArtifacts"', 'id="openRunState"', 'id="openRunReport"', 'id="openRunLogs"', 'id="openRunTraces"', 'id="openJob"', 'id="openSpace"', 'id="openSettings"']:
|
| 43 |
+
assert removed not in active
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def test_progress_renderer_no_longer_sets_active_run_link_buttons():
|
| 47 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 48 |
+
assert "renderActiveBucketLinks" not in js
|
| 49 |
+
assert "openRunState" not in js
|
| 50 |
+
assert "setLink('openJob'" not in js
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def test_app_version_v59():
|
| 54 |
+
assert '"version": "v60-cancel-persist-delete-run"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v60_cancel_persist_delete_run.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_cancel_uses_hf_jobs_cancel_api_and_persists_bucket_state():
|
| 5 |
+
jobs = Path("src/jobs.py").read_text(encoding="utf-8")
|
| 6 |
+
app = Path("app.py").read_text(encoding="utf-8")
|
| 7 |
+
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 8 |
+
assert "from huggingface_hub import Volume, cancel_job" in jobs
|
| 9 |
+
assert "cancel_job(job_id=job_id, namespace=namespace, token=token)" in jobs
|
| 10 |
+
assert '"cancel_requested": True' in app
|
| 11 |
+
assert '"cancelled_at": cancelled_at' in app
|
| 12 |
+
assert 'write_json(f"{paths.root}/cancel.json"' in app
|
| 13 |
+
assert 'append_run_event(' in app
|
| 14 |
+
assert "def append_run_event" in bucket
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def test_delete_run_endpoint_and_bucket_helper_exist():
|
| 18 |
+
app = Path("app.py").read_text(encoding="utf-8")
|
| 19 |
+
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 20 |
+
assert '@fastapi_app.delete("/api/runs/{run_id}")' in app
|
| 21 |
+
assert "delete_run_folder(run_id" in app
|
| 22 |
+
assert "def delete_run_folder" in bucket
|
| 23 |
+
assert ".rm(paths.root, recursive=True)" in bucket
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test_selected_run_has_delete_button_and_frontend_delete_flow():
|
| 27 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 28 |
+
runs = Path("web/static/runs.js").read_text(encoding="utf-8")
|
| 29 |
+
api = Path("web/static/api.js").read_text(encoding="utf-8")
|
| 30 |
+
assert 'id="deleteSelectedRun"' in html
|
| 31 |
+
assert "Delete run from bucket" in html
|
| 32 |
+
assert "async function deleteSelectedRun" in runs
|
| 33 |
+
assert "/api/runs/${encodeURIComponent(runId)}" in runs
|
| 34 |
+
assert "async function apiDelete" in api
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_active_run_still_only_has_cancel_button():
|
| 38 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 39 |
+
active = html.split('id="activeRunPanel"', 1)[1].split('id="runsExplorerPanel"', 1)[0]
|
| 40 |
+
assert 'id="cancelRun"' in active
|
| 41 |
+
assert 'id="deleteSelectedRun"' not in active
|
| 42 |
+
for removed in ['id="openArtifacts"', 'id="openRunState"', 'id="openRunReport"', 'id="openRunLogs"', 'id="openRunTraces"', 'id="openJob"', 'id="openSpace"', 'id="openSettings"']:
|
| 43 |
+
assert removed not in active
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def test_app_version_v60():
|
| 47 |
+
assert '"version": "v60-cancel-persist-delete-run"' in Path("app.py").read_text(encoding="utf-8")
|