Upload 58 files
Browse files- tests/test_v52_ui_fixes.py +1 -1
- tests/test_v57_live_pi_traces.py +1 -1
- tests/test_v58_stable_refresh_pi_stdout.py +1 -1
- tests/test_v59_job_log_progress_clean.py +1 -1
- tests/test_v60_cancel_persist_delete_run.py +1 -1
- tests/test_v61_stabilize_buttons_runtime.py +1 -1
- tests/test_v62_progress_perf_clean_delete.py +1 -1
- tests/test_v63_delete_import_fix.py +1 -1
- tests/test_v64_delete_clean_events_timeline.py +1 -1
- tests/test_v65_floating_form_timeline.py +1 -1
- tests/test_v66_clean_qwen_coder_path.py +1 -1
- tests/test_v67_monotonic_progress_events.py +1 -1
- tests/test_v68_stable_events_timeline_centering.py +1 -1
- tests/test_v69_unified_progress_state.py +2 -2
- tests/test_v70_progress_resume_events_latency.py +1 -1
- tests/test_v71_compact_stable_progress.py +1 -1
- tests/test_v72_clear_modes_space_link_gating.py +1 -1
- tests/test_v73_toast_timer_clean_active_run.py +1 -1
- tests/test_v74_grouped_timeline_rings.py +1 -1
- tests/test_v75_runs_sort_timeline_cost_note.py +1 -1
- tests/test_v76_coherent_progress_status_colors.py +1 -1
- tests/test_v77_terminal_report_refresh_no_launch_result.py +1 -1
- tests/test_v78_report_preview_cost_note_cleanup.py +1 -1
- tests/test_v79_remove_active_report_preview.py +1 -1
- tests/test_v80_topbar_clean_timeline_failure_feedback.py +1 -1
- tests/test_v81_robust_timeline_color_feedback.py +2 -2
- tests/test_v82_elegant_failure_trace_timeline.py +40 -0
- tests/test_v83_success_progress_bar_color.py +44 -0
- tests/test_v84_hardware_fallback_visual_state.py +36 -0
- tests/test_v85_failure_reason_feedback.py +41 -0
- tests/test_v86_hf_error_detail_reasons.py +42 -0
- tests/test_v87_validation_panel_prefill_separation.py +50 -0
- tests/test_v88_validation_schema_kwargs_sanitizer.py +35 -0
- tests/test_v89_main_validation_zerogpu_contract_layout.py +53 -0
- tests/test_v90_pi_model_mismatch_warning.py +44 -0
- tests/test_v91_pi_model_detection_from_published_traces.py +29 -0
tests/test_v52_ui_fixes.py
CHANGED
|
@@ -35,6 +35,6 @@ def test_runs_explorer_is_limited_to_five_rows_with_pagination():
|
|
| 35 |
|
| 36 |
def test_progress_endpoint_uses_view_model_links_for_job_url():
|
| 37 |
app_py = Path("app.py").read_text(encoding="utf-8")
|
| 38 |
-
assert '"version": "
|
| 39 |
assert 'view = build_run_view_model(run_id, bundle, bucket_source=bucket_source)' in app_py
|
| 40 |
assert '"links": view.get("links")' in app_py
|
|
|
|
| 35 |
|
| 36 |
def test_progress_endpoint_uses_view_model_links_for_job_url():
|
| 37 |
app_py = Path("app.py").read_text(encoding="utf-8")
|
| 38 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in app_py
|
| 39 |
assert 'view = build_run_view_model(run_id, bundle, bucket_source=bucket_source)' in app_py
|
| 40 |
assert '"links": view.get("links")' in app_py
|
tests/test_v57_live_pi_traces.py
CHANGED
|
@@ -32,4 +32,4 @@ def test_ui_has_pi_live_trace_panel_and_renderer():
|
|
| 32 |
|
| 33 |
|
| 34 |
def test_app_version_v57():
|
| 35 |
-
assert '"version": "
|
|
|
|
| 32 |
|
| 33 |
|
| 34 |
def test_app_version_v57():
|
| 35 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v58_stable_refresh_pi_stdout.py
CHANGED
|
@@ -23,4 +23,4 @@ def test_elapsed_ticks_client_side_each_second():
|
|
| 23 |
|
| 24 |
|
| 25 |
def test_app_version_v62():
|
| 26 |
-
assert '"version": "
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
def test_app_version_v62():
|
| 26 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v59_job_log_progress_clean.py
CHANGED
|
@@ -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": "
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
def test_app_version_v59():
|
| 54 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v60_cancel_persist_delete_run.py
CHANGED
|
@@ -44,4 +44,4 @@ def test_active_run_still_only_has_cancel_button():
|
|
| 44 |
|
| 45 |
|
| 46 |
def test_app_version_v60():
|
| 47 |
-
assert '"version": "
|
|
|
|
| 44 |
|
| 45 |
|
| 46 |
def test_app_version_v60():
|
| 47 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v61_stabilize_buttons_runtime.py
CHANGED
|
@@ -45,4 +45,4 @@ setTimeout(()=>process.exit(0), 25);
|
|
| 45 |
|
| 46 |
|
| 47 |
def test_app_version_v61():
|
| 48 |
-
assert '"version": "
|
|
|
|
| 45 |
|
| 46 |
|
| 47 |
def test_app_version_v61():
|
| 48 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v62_progress_perf_clean_delete.py
CHANGED
|
@@ -40,4 +40,4 @@ def test_delete_selected_run_clears_ui_and_refreshes_runs():
|
|
| 40 |
|
| 41 |
|
| 42 |
def test_app_version_v62():
|
| 43 |
-
assert '"version": "
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
def test_app_version_v62():
|
| 43 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v63_delete_import_fix.py
CHANGED
|
@@ -14,4 +14,4 @@ def test_bucket_delete_helper_exists():
|
|
| 14 |
|
| 15 |
|
| 16 |
def test_app_version_v63():
|
| 17 |
-
assert '"version": "
|
|
|
|
| 14 |
|
| 15 |
|
| 16 |
def test_app_version_v63():
|
| 17 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v64_delete_clean_events_timeline.py
CHANGED
|
@@ -50,4 +50,4 @@ def test_timeline_has_stopped_state_without_running_animation_confusion():
|
|
| 50 |
|
| 51 |
|
| 52 |
def test_app_version_v64():
|
| 53 |
-
assert '"version": "
|
|
|
|
| 50 |
|
| 51 |
|
| 52 |
def test_app_version_v64():
|
| 53 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v65_floating_form_timeline.py
CHANGED
|
@@ -57,4 +57,4 @@ def test_frontend_timeline_supports_stopped_status():
|
|
| 57 |
|
| 58 |
|
| 59 |
def test_app_version_v65():
|
| 60 |
-
assert '"version": "
|
|
|
|
| 57 |
|
| 58 |
|
| 59 |
def test_app_version_v65():
|
| 60 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v66_clean_qwen_coder_path.py
CHANGED
|
@@ -19,4 +19,4 @@ def test_preferred_gpu_stays_zerogpu_only_and_fallback_a10g_large_default():
|
|
| 19 |
|
| 20 |
|
| 21 |
def test_app_version_v66():
|
| 22 |
-
assert '"version": "
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
def test_app_version_v66():
|
| 22 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v67_monotonic_progress_events.py
CHANGED
|
@@ -36,4 +36,4 @@ def test_light_poll_cannot_reset_timeline_signature_to_earlier_steps():
|
|
| 36 |
|
| 37 |
|
| 38 |
def test_app_version_v67():
|
| 39 |
-
assert '"version": "
|
|
|
|
| 36 |
|
| 37 |
|
| 38 |
def test_app_version_v67():
|
| 39 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v68_stable_events_timeline_centering.py
CHANGED
|
@@ -31,4 +31,4 @@ def test_timeline_centers_even_when_signature_is_unchanged():
|
|
| 31 |
|
| 32 |
|
| 33 |
def test_app_version_v68():
|
| 34 |
-
assert '"version": "
|
|
|
|
| 31 |
|
| 32 |
|
| 33 |
def test_app_version_v68():
|
| 34 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v69_unified_progress_state.py
CHANGED
|
@@ -7,7 +7,7 @@ def test_current_step_uses_stabilized_timeline_not_view_header():
|
|
| 7 |
assert "const stable=stabilizeProgressPayload(p,view)" in render
|
| 8 |
assert "setText('currentStep',stable.currentStepLabel" in render
|
| 9 |
assert "header.current_phase_label" not in render
|
| 10 |
-
assert "animateProgressBar(progress,
|
| 11 |
|
| 12 |
|
| 13 |
def test_events_and_last_event_use_same_stable_event_source():
|
|
@@ -33,4 +33,4 @@ def test_timeline_centering_css_escape_is_safe():
|
|
| 33 |
|
| 34 |
|
| 35 |
def test_app_version_v69():
|
| 36 |
-
assert '"version": "
|
|
|
|
| 7 |
assert "const stable=stabilizeProgressPayload(p,view)" in render
|
| 8 |
assert "setText('currentStep',stable.currentStepLabel" in render
|
| 9 |
assert "header.current_phase_label" not in render
|
| 10 |
+
assert "animateProgressBar(progress,visualRunStatus,stable.currentStep)" in render
|
| 11 |
|
| 12 |
|
| 13 |
def test_events_and_last_event_use_same_stable_event_source():
|
|
|
|
| 33 |
|
| 34 |
|
| 35 |
def test_app_version_v69():
|
| 36 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v70_progress_resume_events_latency.py
CHANGED
|
@@ -58,4 +58,4 @@ def test_resume_latest_falls_back_to_latest_run():
|
|
| 58 |
|
| 59 |
|
| 60 |
def test_app_version_v70():
|
| 61 |
-
assert '"version": "
|
|
|
|
| 58 |
|
| 59 |
|
| 60 |
def test_app_version_v70():
|
| 61 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v71_compact_stable_progress.py
CHANGED
|
@@ -32,4 +32,4 @@ def test_refresh_progress_prefers_canonical_status():
|
|
| 32 |
|
| 33 |
|
| 34 |
def test_app_version_v71():
|
| 35 |
-
assert '"version": "
|
|
|
|
| 32 |
|
| 33 |
|
| 34 |
def test_app_version_v71():
|
| 35 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v72_clear_modes_space_link_gating.py
CHANGED
|
@@ -32,4 +32,4 @@ def test_launch_result_removed_and_selected_links_are_still_gated():
|
|
| 32 |
assert "setLink('selectedOpenSettings',ready&&summary.target_space_url" in runs_js
|
| 33 |
|
| 34 |
def test_app_version_v72():
|
| 35 |
-
assert '"version": "
|
|
|
|
| 32 |
assert "setLink('selectedOpenSettings',ready&&summary.target_space_url" in runs_js
|
| 33 |
|
| 34 |
def test_app_version_v72():
|
| 35 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v73_toast_timer_clean_active_run.py
CHANGED
|
@@ -27,4 +27,4 @@ def test_progress_poll_errors_use_floating_toast_not_internal_banner():
|
|
| 27 |
|
| 28 |
|
| 29 |
def test_app_version_v73():
|
| 30 |
-
assert '"version": "
|
|
|
|
| 27 |
|
| 28 |
|
| 29 |
def test_app_version_v73():
|
| 30 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v74_grouped_timeline_rings.py
CHANGED
|
@@ -41,4 +41,4 @@ def test_timeline_dots_use_conic_gradient_ring():
|
|
| 41 |
|
| 42 |
|
| 43 |
def test_app_version_v74():
|
| 44 |
-
assert '"version": "
|
|
|
|
| 41 |
|
| 42 |
|
| 43 |
def test_app_version_v74():
|
| 44 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v75_runs_sort_timeline_cost_note.py
CHANGED
|
@@ -38,4 +38,4 @@ def test_inference_provider_cost_note_is_visible_near_pi_model():
|
|
| 38 |
|
| 39 |
|
| 40 |
def test_app_version_v75():
|
| 41 |
-
assert '"version": "
|
|
|
|
| 38 |
|
| 39 |
|
| 40 |
def test_app_version_v75():
|
| 41 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v76_coherent_progress_status_colors.py
CHANGED
|
@@ -50,4 +50,4 @@ def test_failed_events_superseded_by_later_step_are_shown_as_recovered_warning()
|
|
| 50 |
|
| 51 |
|
| 52 |
def test_app_version_v76():
|
| 53 |
-
assert '"version": "
|
|
|
|
| 50 |
|
| 51 |
|
| 52 |
def test_app_version_v76():
|
| 53 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v77_terminal_report_refresh_no_launch_result.py
CHANGED
|
@@ -23,4 +23,4 @@ def test_terminal_run_still_refreshes_selected_run_detail_without_active_report_
|
|
| 23 |
|
| 24 |
|
| 25 |
def test_app_version_v77():
|
| 26 |
-
assert '"version": "
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
def test_app_version_v77():
|
| 26 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v78_report_preview_cost_note_cleanup.py
CHANGED
|
@@ -36,4 +36,4 @@ def test_active_run_report_preview_block_removed():
|
|
| 36 |
|
| 37 |
|
| 38 |
def test_app_version_v78():
|
| 39 |
-
assert '"version": "
|
|
|
|
| 36 |
|
| 37 |
|
| 38 |
def test_app_version_v78():
|
| 39 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v79_remove_active_report_preview.py
CHANGED
|
@@ -27,4 +27,4 @@ def test_selected_run_report_preview_support_can_remain_if_used_elsewhere():
|
|
| 27 |
|
| 28 |
|
| 29 |
def test_app_version_v79():
|
| 30 |
-
assert '"version": "
|
|
|
|
| 27 |
|
| 28 |
|
| 29 |
def test_app_version_v79():
|
| 30 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v80_topbar_clean_timeline_failure_feedback.py
CHANGED
|
@@ -31,4 +31,4 @@ def test_recovered_failure_has_warning_color_not_green():
|
|
| 31 |
|
| 32 |
|
| 33 |
def test_app_version_v80():
|
| 34 |
-
assert '"version": "
|
|
|
|
| 31 |
|
| 32 |
|
| 33 |
def test_app_version_v80():
|
| 34 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v81_robust_timeline_color_feedback.py
CHANGED
|
@@ -16,7 +16,7 @@ def test_timeline_prefers_problem_group_for_scroll_on_selected_old_runs():
|
|
| 16 |
assert "function firstProblemGroupKey" in js
|
| 17 |
assert "function currentCompactGroupKey(fullItems=[],overallStatus='')" in js
|
| 18 |
assert "const problem=firstProblemGroupKey(fullItems,overallStatus)" in js
|
| 19 |
-
assert "const currentKey=currentCompactGroupKey(fullList,overallStatus)" in js
|
| 20 |
|
| 21 |
|
| 22 |
def test_timeline_scroll_resets_cleanly_between_selected_runs():
|
|
@@ -34,4 +34,4 @@ def test_recovered_failures_remain_amber_not_green():
|
|
| 34 |
|
| 35 |
|
| 36 |
def test_app_version_v81():
|
| 37 |
-
assert '"version": "
|
|
|
|
| 16 |
assert "function firstProblemGroupKey" in js
|
| 17 |
assert "function currentCompactGroupKey(fullItems=[],overallStatus='')" in js
|
| 18 |
assert "const problem=firstProblemGroupKey(fullItems,overallStatus)" in js
|
| 19 |
+
assert "const currentKey=trace.key||currentCompactGroupKey(fullList,overallStatus)" in js
|
| 20 |
|
| 21 |
|
| 22 |
def test_timeline_scroll_resets_cleanly_between_selected_runs():
|
|
|
|
| 34 |
|
| 35 |
|
| 36 |
def test_app_version_v81():
|
| 37 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v82_elegant_failure_trace_timeline.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_timeline_has_explicit_failure_trace_note():
|
| 5 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 6 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 7 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 8 |
+
assert 'id="timelineTraceNote"' in html
|
| 9 |
+
assert "function timelineTraceModel" in js
|
| 10 |
+
assert "function renderTimelineTraceNote" in js
|
| 11 |
+
assert "Failure trace" in js
|
| 12 |
+
assert ".timeline-trace-note.failed" in css
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def test_failure_trace_styles_breakpoint_and_post_failure_tail():
|
| 16 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 17 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 18 |
+
assert "function traceClassForStep" in js
|
| 19 |
+
assert "trace-breakpoint" in js
|
| 20 |
+
assert "trace-post-failure" in js
|
| 21 |
+
assert "timeline.trace-failed .step.trace-breakpoint" in css
|
| 22 |
+
assert "repeating-linear-gradient" in css
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_old_runs_merge_events_before_stabilizing_timeline():
|
| 26 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 27 |
+
render = js.split("function renderRunViewModel", 1)[1].split("function renderProgress", 1)[0]
|
| 28 |
+
assert render.index("mergeStableEvents") < render.index("stabilizeProgressPayload")
|
| 29 |
+
render_progress = js.split("function renderProgress", 1)[1]
|
| 30 |
+
assert render_progress.index("mergeStableEvents") < render_progress.index("stabilizeProgressPayload")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_backend_timeline_failed_item_fallback_exists():
|
| 34 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 35 |
+
assert "function failedItemInfoForGroup" in js
|
| 36 |
+
assert "failedItemInfoForGroup(group,byStep)" in js
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_app_version_v82():
|
| 40 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v83_success_progress_bar_color.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
from src.progress import progress_from_events
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_frontend_success_detection_covers_real_success_statuses():
|
| 7 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 8 |
+
components = Path("web/static/components.js").read_text(encoding="utf-8")
|
| 9 |
+
for token in [
|
| 10 |
+
"full_inference_candidate_health_passed",
|
| 11 |
+
"full_inference_success",
|
| 12 |
+
"health_only",
|
| 13 |
+
"repair_success",
|
| 14 |
+
]:
|
| 15 |
+
assert token in js
|
| 16 |
+
assert token in components
|
| 17 |
+
assert "let klass=visualStatusClass(status);if(value>=100&&klass==='neutral')klass='success'" in js
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def test_progress_bar_prefers_backend_visual_status():
|
| 21 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 22 |
+
assert "const visualRunStatus=p.visual_status||runStatus" in js
|
| 23 |
+
assert "animateProgressBar(progress,visualRunStatus" in js
|
| 24 |
+
assert "animateProgressBar(progress,p.visual_status||p.status" in js
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_backend_visual_status_success_for_health_only():
|
| 28 |
+
result = progress_from_events([
|
| 29 |
+
{"ts": "2026-06-02T10:00:00+00:00", "step": "done", "status": "health_only"}
|
| 30 |
+
], state={"status": "health_only"})
|
| 31 |
+
assert result["status"] == "health_only"
|
| 32 |
+
assert result["visual_status"] == "success"
|
| 33 |
+
assert result["progress"] == 100
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def test_backend_visual_status_stopped_for_manual_or_blocker():
|
| 37 |
+
result = progress_from_events([
|
| 38 |
+
{"ts": "2026-06-02T10:00:00+00:00", "step": "done", "status": "manual_hardware_required"}
|
| 39 |
+
], state={"status": "manual_hardware_required"})
|
| 40 |
+
assert result["visual_status"] == "stopped"
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_app_version_v83():
|
| 44 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v84_hardware_fallback_visual_state.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_hardware_fallback_has_dedicated_timeline_status_not_recovered_incident():
|
| 5 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 6 |
+
assert "function isHardwareFallbackEvent" in js
|
| 7 |
+
assert "function isExpectedFallbackFailure" in js
|
| 8 |
+
assert "return 'fallback'" in js
|
| 9 |
+
assert "status==='fallback'" in js
|
| 10 |
+
assert "Hardware fallback" in js
|
| 11 |
+
assert "Recovered after an incident at Hardware" not in js
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_fallback_trace_message_is_neutral_routing_language():
|
| 15 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 16 |
+
assert "ZeroGPU was not used for this run" in js
|
| 17 |
+
assert "switched to the fallback GPU" in js
|
| 18 |
+
assert "const title=trace.mode==='failed'?'Failure trace':(trace.mode==='fallback'?'Hardware fallback':'Recovered incident')" in js
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_hardware_fallback_has_blue_visual_style():
|
| 22 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 23 |
+
assert ".step.status-fallback" in css
|
| 24 |
+
assert ".timeline-trace-note.fallback" in css
|
| 25 |
+
assert ".timeline.trace-fallback .step.trace-fallback" in css
|
| 26 |
+
assert ".activity-row.hardware-fallback" in css
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def test_activity_feed_labels_hardware_fallback_as_route():
|
| 30 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 31 |
+
assert "return'hardware-fallback'" in js
|
| 32 |
+
assert "fallback route" in js
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_app_version_v84():
|
| 36 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v85_failure_reason_feedback.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_failure_and_fallback_reason_helpers_exist():
|
| 5 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 6 |
+
assert "function eventReason" in js
|
| 7 |
+
assert "function zeroGpuReason" in js
|
| 8 |
+
assert "function failureReasonForGroup" in js
|
| 9 |
+
assert "function fallbackReasonForGroup" in js
|
| 10 |
+
assert "quota" in js
|
| 11 |
+
assert "ZeroGPU was unavailable" in js
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_timeline_trace_note_displays_reason():
|
| 15 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 16 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 17 |
+
assert "trace.reason" in js
|
| 18 |
+
assert '<b>Reason:</b>' in js
|
| 19 |
+
assert "trace-reason" in js
|
| 20 |
+
assert ".timeline-trace-note .trace-reason" in css
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def test_timeline_step_has_reason_tooltip_and_short_reason_line():
|
| 24 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 25 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 26 |
+
assert "reasonForTimelineItem" in js
|
| 27 |
+
assert "step-reason" in js
|
| 28 |
+
assert 'title="${escapeHtml(reason)}"' in js
|
| 29 |
+
assert ".step .step-reason" in css
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def test_activity_feed_can_show_event_reason_detail():
|
| 33 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 34 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 35 |
+
assert "event-reason" in js
|
| 36 |
+
assert "const reason=eventReason(item)" in js
|
| 37 |
+
assert ".activity-row .event-reason" in css
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def test_app_version_v85():
|
| 41 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v86_hf_error_detail_reasons.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_frontend_reads_error_reason_from_event_data_before_generic_message():
|
| 5 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 6 |
+
assert "data.hf_error_detail" in js
|
| 7 |
+
assert "data.hf_error_message" in js
|
| 8 |
+
assert "data.response_text" in js
|
| 9 |
+
assert "data.response_body" in js
|
| 10 |
+
assert "data.error" in js
|
| 11 |
+
assert "data.reason" in js
|
| 12 |
+
assert "event.message" in js
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def test_zerogpu_reason_detects_quota_usage_exceeded_terms():
|
| 16 |
+
js = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 17 |
+
assert "lower.includes('quota')" in js
|
| 18 |
+
assert "lower.includes('usage')" in js
|
| 19 |
+
assert "lower.includes('exceeded')" in js
|
| 20 |
+
assert "ZeroGPU was unavailable" in js
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def test_worker_extracts_hf_http_exception_response_details():
|
| 24 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 25 |
+
assert "def exception_payload" in worker
|
| 26 |
+
assert "response.text" in worker
|
| 27 |
+
assert "response.json()" in worker
|
| 28 |
+
assert '"hf_error_detail"' in worker
|
| 29 |
+
assert '"hf_error_message"' in worker
|
| 30 |
+
assert '"status_code"' in worker
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_space_hardware_events_include_hf_error_payload():
|
| 34 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 35 |
+
assert "exc_payload = exception_payload(exc)" in worker
|
| 36 |
+
assert "create_repo_space_hardware" in worker
|
| 37 |
+
assert "**exc_payload" in worker
|
| 38 |
+
assert "Could not create Space with requested hardware" in worker
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def test_app_version_v86():
|
| 42 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v87_validation_panel_prefill_separation.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_validation_launch_does_not_replace_active_run():
|
| 5 |
+
js = Path("web/static/app.js").read_text(encoding="utf-8")
|
| 6 |
+
launch = js.split("async function launchValidate", 1)[1].split("async function refreshRunReportPreview", 1)[0]
|
| 7 |
+
assert "setActiveRun(result)" not in launch
|
| 8 |
+
assert "renderValidationLaunchResult(result)" in launch
|
| 9 |
+
assert "startValidationPolling(result.run_id)" in launch
|
| 10 |
+
assert "state.validationRunId" in js
|
| 11 |
+
assert "validationPoll" in js
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_validation_prefill_copies_args_kwargs_from_selected_run():
|
| 15 |
+
js = Path("web/static/app.js").read_text(encoding="utf-8")
|
| 16 |
+
assert "function validationPrefillFromDetail" in js
|
| 17 |
+
assert "smoke.test_args" in js
|
| 18 |
+
assert "launch.test_args_json" in js
|
| 19 |
+
assert "smoke.test_kwargs" in js
|
| 20 |
+
assert "launch.test_kwargs_json" in js
|
| 21 |
+
assert "setInputValue('testArgs',prettyJson(prefill.args))" in js
|
| 22 |
+
assert "setInputValue('testKwargs',prettyJson(prefill.kwargs))" in js
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_validation_form_enforces_json_array_and_object():
|
| 26 |
+
js = Path("web/static/app.js").read_text(encoding="utf-8")
|
| 27 |
+
app = Path("app.py").read_text(encoding="utf-8")
|
| 28 |
+
assert "Test args must be a JSON array" in js
|
| 29 |
+
assert "Test kwargs must be a JSON object" in js
|
| 30 |
+
assert "test_args_json must be a JSON array" in app
|
| 31 |
+
assert "test_kwargs_json must be a JSON object" in app
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def test_validation_ui_explains_separate_validation_run():
|
| 35 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 36 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 37 |
+
assert "Validation launches a separate validation run" in html
|
| 38 |
+
assert "does not replace the current Active Run" in html
|
| 39 |
+
assert ".validation-help" in css
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def test_default_validation_args_are_positional_not_inputs_object():
|
| 43 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 44 |
+
test_args = html.split('id="testArgs"', 1)[1].split("</textarea>", 1)[0]
|
| 45 |
+
assert '"inputs"' not in test_args
|
| 46 |
+
assert "Explain the concept of agentic AI" in test_args
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def test_app_version_v87():
|
| 50 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v88_validation_schema_kwargs_sanitizer.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_validation_worker_extracts_endpoint_parameters_from_schema():
|
| 5 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 6 |
+
assert "def endpoint_schema_for_api" in worker
|
| 7 |
+
assert "def endpoint_parameter_names" in worker
|
| 8 |
+
assert "parameter_name" in worker
|
| 9 |
+
assert "endpoint_parameters" in worker
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_validation_worker_sanitizes_unsupported_kwargs():
|
| 13 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 14 |
+
assert "def sanitize_kwargs_for_schema" in worker
|
| 15 |
+
assert "safe_kwargs, dropped_kwargs, endpoint_parameters" in worker
|
| 16 |
+
assert "Ignored unsupported validation kwargs" in worker
|
| 17 |
+
assert "ignored_test_kwargs" in worker
|
| 18 |
+
assert "original_test_kwargs" in worker
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_validation_worker_retries_when_gradio_rejects_kwargs():
|
| 22 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 23 |
+
assert "not a valid key-word argument" in worker
|
| 24 |
+
assert "Retrying validation without keyword arguments" in worker
|
| 25 |
+
assert "client.predict(*test_args, api_name=api_name)" in worker
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def test_validation_ui_explains_endpoint_specific_kwargs():
|
| 29 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 30 |
+
assert "Test kwargs are endpoint-specific" in html
|
| 31 |
+
assert "ignores unsupported kwargs with a warning" in html
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def test_app_version_v88():
|
| 35 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v89_main_validation_zerogpu_contract_layout.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_space_test_panel_is_inside_workspace_after_active_run():
|
| 5 |
+
html = Path("web/index.html").read_text(encoding="utf-8")
|
| 6 |
+
active_idx = html.index('id="activeRunPanel"')
|
| 7 |
+
space_test_idx = html.index('id="spaceTestSection"')
|
| 8 |
+
runs_idx = html.index('id="runsExplorerPanel"')
|
| 9 |
+
workspace_end = html.index('</section>', runs_idx)
|
| 10 |
+
assert active_idx < space_test_idx < runs_idx
|
| 11 |
+
assert space_test_idx < workspace_end
|
| 12 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 13 |
+
assert "v89: Space Test lives under Active Run" in css
|
| 14 |
+
assert "workspace-grid > .space-test-panel" in css
|
| 15 |
+
assert "grid-column:2" in css
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def test_build_job_passes_expected_output_type_to_worker():
|
| 19 |
+
app = Path("app.py").read_text(encoding="utf-8")
|
| 20 |
+
jobs = Path("src/jobs.py").read_text(encoding="utf-8")
|
| 21 |
+
assert "expected_output_type=payload.get(\"expected_output_type\")" in app
|
| 22 |
+
assert "expected_output_type: str | None = None" in jobs
|
| 23 |
+
assert 'env["EXPECTED_OUTPUT_TYPE"]' in jobs
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test_main_worker_runs_live_generation_smoke_and_zerogpu_contract():
|
| 27 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 28 |
+
assert "def run_generation_smoke" in worker
|
| 29 |
+
assert "Calling live generation endpoint for ZeroGPU timing" in worker
|
| 30 |
+
assert "measured_zero_gpu_recommendation" in worker
|
| 31 |
+
assert "recommendation_source" in worker
|
| 32 |
+
assert "recommendation_confidence" in worker
|
| 33 |
+
assert "observed_latency_seconds" in worker
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def test_inference_gate_uses_generation_smoke_success():
|
| 37 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 38 |
+
assert "generation_smoke: dict | None" in worker
|
| 39 |
+
assert "generation_smoke_passed" in worker
|
| 40 |
+
assert "zero_gpu_duration_measured" in worker
|
| 41 |
+
assert "full_inference_success" in worker
|
| 42 |
+
assert "live generation smoke test passed" in worker.lower()
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_report_and_state_include_generation_smoke():
|
| 46 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 47 |
+
assert '"generation_smoke": generation_smoke' in worker
|
| 48 |
+
assert "Live generation smoke / ZeroGPU duration" in worker
|
| 49 |
+
assert "zero_gpu_duration_recommendation" in worker
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def test_app_version_v89():
|
| 53 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v90_pi_model_mismatch_warning.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_worker_detects_pi_model_resolution_and_mismatch():
|
| 5 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 6 |
+
assert "def detect_pi_model_resolution" in worker
|
| 7 |
+
assert "def extract_pi_models_from_text" in worker
|
| 8 |
+
assert "Kimi" in worker
|
| 9 |
+
assert "pi_model_resolution.json" in worker
|
| 10 |
+
assert "mismatch" in worker
|
| 11 |
+
assert "emit_pi_model_resolution" in worker
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_worker_emits_pi_model_mismatch_event_and_state():
|
| 15 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 16 |
+
assert "Pi assistant model appears to differ from the requested model" in worker
|
| 17 |
+
assert '"pi_model_resolution": pi_model_resolution' in worker
|
| 18 |
+
assert "## Pi assistant model" in worker
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_bucket_exposes_pi_model_resolution_to_ui():
|
| 22 |
+
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 23 |
+
assert "pi_model_resolution.json" in bucket
|
| 24 |
+
assert '"pi_model_resolution"' in bucket
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_ui_warns_when_pi_provider_model_changed():
|
| 28 |
+
runs = Path("web/static/runs.js").read_text(encoding="utf-8")
|
| 29 |
+
progress = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 30 |
+
css = Path("web/static/app.css").read_text(encoding="utf-8")
|
| 31 |
+
assert "Pi assistant model changed" in runs
|
| 32 |
+
assert "Inference Providers routing/fallback" in runs
|
| 33 |
+
assert "pi_model_resolution" in progress
|
| 34 |
+
assert "v90: Pi/provider model mismatch warning" in css
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_pi_model_resolution_step_is_in_timeline():
|
| 38 |
+
progress = Path("web/static/progress.js").read_text(encoding="utf-8")
|
| 39 |
+
assert "pi_model_resolution" in progress
|
| 40 |
+
assert "Pi verify" in progress
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_app_version_v90():
|
| 44 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|
tests/test_v91_pi_model_detection_from_published_traces.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_bucket_can_reconstruct_pi_model_resolution_from_published_traces():
|
| 5 |
+
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 6 |
+
assert "def _trace_based_pi_model_resolution" in bucket
|
| 7 |
+
assert "traces/redacted/**/*.jsonl" in bucket
|
| 8 |
+
assert "logs/pi_output.txt" in bucket
|
| 9 |
+
assert "published_pi_traces" in bucket
|
| 10 |
+
assert "_extract_pi_models_from_text" in bucket
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def test_read_run_bundle_uses_trace_fallback_when_resolution_file_missing():
|
| 14 |
+
bucket = Path("src/bucket.py").read_text(encoding="utf-8")
|
| 15 |
+
assert 'if not bundle.get("pi_model_resolution")' in bucket
|
| 16 |
+
assert '_trace_based_pi_model_resolution(run_id, bundle' in bucket
|
| 17 |
+
assert '"pi_model_resolution": pi_model_resolution' in bucket
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def test_worker_marks_model_resolution_source_as_after_trace_sync():
|
| 21 |
+
worker = Path("src/worker_payload.py").read_text(encoding="utf-8")
|
| 22 |
+
assert "Pi sessions are the source of truth" in worker
|
| 23 |
+
assert "published_pi_traces_after_sync" in worker
|
| 24 |
+
assert "collect_pi_traces(run_dir, events_path)" in worker
|
| 25 |
+
assert "detect_pi_model_resolution(pi_model, run_dir, pi_out)" in worker
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def test_app_version_v91():
|
| 29 |
+
assert '"version": "v91-pi-model-detection-from-published-traces"' in Path("app.py").read_text(encoding="utf-8")
|