Upload 29 files
Browse files- tests/test_v52_ui_fixes.py +1 -1
- tests/test_v57_live_pi_traces.py +12 -10
- tests/test_v58_stable_refresh_pi_stdout.py +10 -15
- tests/test_v59_job_log_progress_clean.py +2 -2
- tests/test_v60_cancel_persist_delete_run.py +2 -2
- tests/test_v61_stabilize_buttons_runtime.py +48 -0
- tests/test_v62_progress_perf_clean_delete.py +44 -0
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": "v62-progress-perf-clean-delete"' 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
|
@@ -10,24 +10,26 @@ def test_worker_streams_pi_output_while_running():
|
|
| 10 |
assert "run_cmd_streaming(" in worker
|
| 11 |
|
| 12 |
|
| 13 |
-
def
|
| 14 |
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 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
|
|
|
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
def test_ui_has_pi_live_trace_panel_and_renderer():
|
| 22 |
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 23 |
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 24 |
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 25 |
-
assert 'id="piTracePanel"' in html
|
| 26 |
-
assert "Pi process stdout" in html
|
| 27 |
-
assert "function renderPiLiveTrace" in js
|
| 28 |
-
assert "p.pi_live_log" in js
|
| 29 |
-
assert ".pi-trace-output" in css
|
| 30 |
|
| 31 |
|
| 32 |
def test_app_version_v57():
|
| 33 |
-
assert '"version": "
|
|
|
|
| 10 |
assert "run_cmd_streaming(" in worker
|
| 11 |
|
| 12 |
|
| 13 |
+
def test_progress_endpoint_no_longer_exposes_pi_stdout_payload():
|
| 14 |
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 15 |
app = Path("app.py").read_text(encoding="utf-8")
|
| 16 |
+
assert '"pi_live_log": _safe_read_text' not in bucket
|
| 17 |
+
assert '"pi_output_log": _safe_read_text' not in bucket
|
| 18 |
+
assert "include_heavy: bool = True" in bucket
|
| 19 |
+
assert "include_job_logs: bool = False" in app
|
| 20 |
+
assert '"pi_live_log"' not in app
|
| 21 |
|
| 22 |
|
| 23 |
def test_ui_has_pi_live_trace_panel_and_renderer():
|
| 24 |
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 25 |
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 26 |
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 27 |
+
assert 'id="piTracePanel"' not in html
|
| 28 |
+
assert "Pi process stdout" not in html
|
| 29 |
+
assert "function renderPiLiveTrace" not in js
|
| 30 |
+
assert "p.pi_live_log" not in js
|
| 31 |
+
assert ".pi-trace-output" not in css
|
| 32 |
|
| 33 |
|
| 34 |
def test_app_version_v57():
|
| 35 |
+
assert '"version": "v62-progress-perf-clean-delete"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v58_stable_refresh_pi_stdout.py
CHANGED
|
@@ -1,11 +1,18 @@
|
|
| 1 |
from pathlib import Path
|
| 2 |
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
def test_progress_poll_does_not_refresh_runs_every_tick():
|
| 5 |
js = Path("web/static/app.js").read_text(encoding="utf-8")
|
| 6 |
assert "lastRunsRefreshFromPoll" in js
|
| 7 |
assert ">15000" in js
|
| 8 |
-
assert "loadRuns(false)}}catch" in js
|
| 9 |
|
| 10 |
|
| 11 |
def test_elapsed_ticks_client_side_each_second():
|
|
@@ -13,19 +20,7 @@ def test_elapsed_ticks_client_side_each_second():
|
|
| 13 |
assert "const elapsedClock" in progress
|
| 14 |
assert "window.setInterval" in progress
|
| 15 |
assert "elapsedClock.baseSeconds+Math.floor" in progress
|
| 16 |
-
assert "setElapsedClock(p.elapsed_seconds??header.elapsed_seconds" in progress
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def test_pi_live_panel_is_stdout_not_trace_jsonl():
|
| 20 |
-
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 21 |
-
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 22 |
-
app = Path("app.py").read_text(encoding="utf-8")
|
| 23 |
-
assert "Pi process stdout" in html
|
| 24 |
-
assert "logs/pi_live_output.txt" in html
|
| 25 |
-
assert "pi_live_log_source" in app
|
| 26 |
-
assert "No Pi stdout yet" in js
|
| 27 |
-
|
| 28 |
|
| 29 |
|
| 30 |
-
def
|
| 31 |
-
assert '"version": "
|
|
|
|
| 1 |
from pathlib import Path
|
| 2 |
|
| 3 |
|
| 4 |
+
def test_pi_stdout_panel_removed_from_active_run():
|
| 5 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 6 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 7 |
+
assert 'id="piTracePanel"' not in html
|
| 8 |
+
assert "Pi process stdout" not in html
|
| 9 |
+
assert "renderPiLiveTrace" not in js
|
| 10 |
+
|
| 11 |
+
|
| 12 |
def test_progress_poll_does_not_refresh_runs_every_tick():
|
| 13 |
js = Path("web/static/app.js").read_text(encoding="utf-8")
|
| 14 |
assert "lastRunsRefreshFromPoll" in js
|
| 15 |
assert ">15000" in js
|
|
|
|
| 16 |
|
| 17 |
|
| 18 |
def test_elapsed_ticks_client_side_each_second():
|
|
|
|
| 20 |
assert "const elapsedClock" in progress
|
| 21 |
assert "window.setInterval" in progress
|
| 22 |
assert "elapsedClock.baseSeconds+Math.floor" in progress
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
+
def test_app_version_v62():
|
| 26 |
+
assert '"version": "v62-progress-perf-clean-delete"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v59_job_log_progress_clean.py
CHANGED
|
@@ -9,7 +9,7 @@ def test_progress_endpoint_polls_hf_job_logs_and_extracts_worker_events():
|
|
| 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 '"
|
| 13 |
assert '"job_log_events_count": len(job_log_events)' in app_py
|
| 14 |
|
| 15 |
|
|
@@ -51,4 +51,4 @@ def test_progress_renderer_no_longer_sets_active_run_link_buttons():
|
|
| 51 |
|
| 52 |
|
| 53 |
def test_app_version_v59():
|
| 54 |
-
assert '"version": "
|
|
|
|
| 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_polled": bool(include_job_logs)' in app_py
|
| 13 |
assert '"job_log_events_count": len(job_log_events)' in app_py
|
| 14 |
|
| 15 |
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
def test_app_version_v59():
|
| 54 |
+
assert '"version": "v62-progress-perf-clean-delete"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v60_cancel_persist_delete_run.py
CHANGED
|
@@ -5,7 +5,7 @@ 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
|
| 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
|
|
@@ -44,4 +44,4 @@ def test_active_run_still_only_has_cancel_button():
|
|
| 44 |
|
| 45 |
|
| 46 |
def test_app_version_v60():
|
| 47 |
-
assert '"version": "
|
|
|
|
| 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 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
|
|
|
|
| 44 |
|
| 45 |
|
| 46 |
def test_app_version_v60():
|
| 47 |
+
assert '"version": "v62-progress-perf-clean-delete"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v61_stabilize_buttons_runtime.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_cancel_job_import_is_lazy_to_avoid_app_crash_on_cached_hub_versions():
|
| 5 |
+
jobs = Path("src/jobs.py").read_text(encoding="utf-8")
|
| 6 |
+
first_lines = "\n".join(jobs.splitlines()[:12])
|
| 7 |
+
assert "cancel_job" not in first_lines
|
| 8 |
+
assert "from huggingface_hub import cancel_job" in jobs
|
| 9 |
+
assert "huggingface_hub.cancel_job is unavailable" in jobs
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_custom_static_scripts_have_no_syntax_errors():
|
| 13 |
+
import subprocess
|
| 14 |
+
for path in Path("web/static").glob("*.js"):
|
| 15 |
+
result = subprocess.run(["node", "--check", str(path)], capture_output=True, text=True)
|
| 16 |
+
assert result.returncode == 0, result.stderr
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def test_frontend_loads_with_minimal_dom_without_throwing():
|
| 20 |
+
import subprocess
|
| 21 |
+
script = r"""
|
| 22 |
+
const fs=require('fs');
|
| 23 |
+
class El{
|
| 24 |
+
constructor(id){this.id=id;this.value='';this.textContent='';this.innerHTML='';this.dataset={};this.classList={add(){},remove(){},toggle(){}};this.style={setProperty(){}};this.attributes={};this.disabled=false;this.hidden=false;this.href='';this.open=false;this.clientHeight=100;this.scrollHeight=100;this.scrollTop=0;}
|
| 25 |
+
addEventListener(){}
|
| 26 |
+
setAttribute(k,v){this.attributes[k]=v}
|
| 27 |
+
removeAttribute(k){delete this.attributes[k]}
|
| 28 |
+
querySelectorAll(){return[]}
|
| 29 |
+
querySelector(){return new El('child')}
|
| 30 |
+
scrollIntoView(){}
|
| 31 |
+
}
|
| 32 |
+
const ids = Array.from(fs.readFileSync('web/index.html','utf8').matchAll(/id="([^"]+)"/g)).map(m=>m[1]);
|
| 33 |
+
const map={};
|
| 34 |
+
for(const id of ids) map[id]=new El(id);
|
| 35 |
+
global.document={getElementById(id){return map[id]||null},querySelectorAll(sel){if(sel.includes('filter')) return ['all','running','failed'].map(s=>{let e=new El('filter');e.dataset={status:s};return e}); return [];}};
|
| 36 |
+
global.window={setTimeout,setInterval,clearInterval};
|
| 37 |
+
global.localStorage={getItem(){return null},setItem(){},removeItem(){}};
|
| 38 |
+
global.confirm=()=>true;
|
| 39 |
+
global.fetch=async()=>({ok:false,text:async()=>'forced error',json:async()=>({})});
|
| 40 |
+
for(const f of ['web/static/components.js','web/static/api.js','web/static/progress.js','web/static/runs.js','web/static/app.js']) global.eval(fs.readFileSync(f,'utf8'));
|
| 41 |
+
setTimeout(()=>process.exit(0), 25);
|
| 42 |
+
"""
|
| 43 |
+
result = subprocess.run(["node", "-e", script], capture_output=True, text=True, timeout=5)
|
| 44 |
+
assert result.returncode == 0, result.stderr
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def test_app_version_v61():
|
| 48 |
+
assert '"version": "v62-progress-perf-clean-delete"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v62_progress_perf_clean_delete.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_progress_endpoint_uses_light_bucket_bundle_and_optional_job_logs():
|
| 5 |
+
app = Path("app.py").read_text(encoding="utf-8")
|
| 6 |
+
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 7 |
+
assert "include_heavy: bool = True" in bucket
|
| 8 |
+
assert "include_heavy=False" in app
|
| 9 |
+
assert "include_job_logs: bool = False" in app
|
| 10 |
+
assert "if include_job_logs:" in app
|
| 11 |
+
assert '"job_logs_polled": bool(include_job_logs)' in app
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_progress_polls_job_logs_less_often_from_frontend():
|
| 15 |
+
js = Path("web/static/app.js").read_text(encoding="utf-8")
|
| 16 |
+
assert "lastJobLogPoll" in js
|
| 17 |
+
assert ">12000" in js
|
| 18 |
+
assert "include_job_logs=1" in js
|
| 19 |
+
assert "state.lastJobLogPoll=0" in js
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_pi_stdout_display_and_api_payload_are_removed():
|
| 23 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 24 |
+
progress = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 25 |
+
app = Path("app.py").read_text(encoding="utf-8")
|
| 26 |
+
assert "piTracePanel" not in html
|
| 27 |
+
assert "Pi process stdout" not in html
|
| 28 |
+
assert "renderPiLiveTrace" not in progress
|
| 29 |
+
assert "pi_live_log" not in progress
|
| 30 |
+
assert '"pi_live_log"' not in app
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_delete_selected_run_clears_ui_and_refreshes_runs():
|
| 34 |
+
runs = Path("web/static/runs.js").read_text(encoding="utf-8")
|
| 35 |
+
assert "async function deleteSelectedRun" in runs
|
| 36 |
+
assert "await apiDelete" in runs
|
| 37 |
+
assert "await loadRuns(true)" in runs
|
| 38 |
+
assert "No report selected." in runs
|
| 39 |
+
assert "No artifacts indexed yet." in runs
|
| 40 |
+
assert "Delete run from bucket" in runs
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_app_version_v62():
|
| 44 |
+
assert '"version": "v62-progress-perf-clean-delete"' in Path("app.py").read_text(encoding="utf-8")
|