| import json |
| from pathlib import Path |
|
|
| from fastapi.testclient import TestClient |
|
|
| from backend.config_loader import ConfigLoader |
| from backend.main import create_app |
| from backend.services.session_progress import SessionProgress |
|
|
|
|
| def _make_session(config_loader: ConfigLoader, session_id: str) -> Path: |
| session_dir = config_loader.paths.workspace_root / session_id |
| (session_dir / "reports" / "feature_engineering").mkdir(parents=True, exist_ok=True) |
| (session_dir / "data").mkdir(parents=True, exist_ok=True) |
| return session_dir |
|
|
|
|
| def _write(path: Path, payload: dict) -> None: |
| path.parent.mkdir(parents=True, exist_ok=True) |
| path.write_text(json.dumps(payload), encoding="utf-8") |
|
|
|
|
| def test_progress_empty_session_all_pending(test_config_loader: ConfigLoader) -> None: |
| session_dir = _make_session(test_config_loader, "s_empty") |
| progress = SessionProgress(session_dir=session_dir, config_loader=test_config_loader) |
|
|
| assert progress.phase_status() == { |
| "validation": "pending", |
| "metadata": "pending", |
| "feature_engineering": "pending", |
| "training": "pending", |
| "evaluation": "pending", |
| } |
| assert progress.first_incomplete_phase() == "validation" |
|
|
|
|
| def test_progress_validation_failed_blocks_at_validation( |
| test_config_loader: ConfigLoader, |
| ) -> None: |
| session_dir = _make_session(test_config_loader, "s_failed") |
| _write(session_dir / "reports" / "validation_report.json", {"passed": False}) |
| progress = SessionProgress(session_dir=session_dir, config_loader=test_config_loader) |
|
|
| assert progress.phase_status()["validation"] == "failed" |
| assert progress.first_incomplete_phase() == "validation" |
|
|
|
|
| def test_progress_partial_completion_next_is_feature_engineering( |
| test_config_loader: ConfigLoader, |
| ) -> None: |
| session_dir = _make_session(test_config_loader, "s_partial") |
| _write(session_dir / "reports" / "validation_report.json", {"passed": True}) |
| _write(session_dir / "reports" / "metadata.json", {"problem_type": "classification"}) |
| progress = SessionProgress(session_dir=session_dir, config_loader=test_config_loader) |
|
|
| statuses = progress.phase_status() |
| assert statuses["validation"] == "passed" |
| assert statuses["metadata"] == "complete" |
| assert statuses["feature_engineering"] == "pending" |
| assert progress.first_incomplete_phase() == "feature_engineering" |
|
|
|
|
| def test_progress_all_complete_returns_none(test_config_loader: ConfigLoader) -> None: |
| session_dir = _make_session(test_config_loader, "s_done") |
| _write(session_dir / "reports" / "validation_report.json", {"passed": True}) |
| _write(session_dir / "reports" / "metadata.json", {}) |
| _write(session_dir / "reports" / "feature_engineering" / "feature_artifact.json", {}) |
| _write(session_dir / "reports" / "model_config.json", {}) |
| _write(session_dir / "reports" / "training_summary.json", {}) |
| _write(session_dir / "reports" / "judge_decision.json", {}) |
| progress = SessionProgress(session_dir=session_dir, config_loader=test_config_loader) |
|
|
| assert progress.first_incomplete_phase() is None |
| assert all( |
| status in {"complete", "passed"} for status in progress.phase_status().values() |
| ) |
|
|
|
|
| def test_progress_endpoint_returns_phases_and_next( |
| test_config_loader: ConfigLoader, |
| ) -> None: |
| session_dir = _make_session(test_config_loader, "s_api") |
| _write(session_dir / "reports" / "validation_report.json", {"passed": True}) |
| _write(session_dir / "reports" / "metadata.json", {}) |
| client = TestClient(create_app(config_loader=test_config_loader)) |
|
|
| response = client.get("/api/runs/s_api/progress") |
|
|
| assert response.status_code == 200 |
| payload = response.json() |
| assert payload["session_id"] == "s_api" |
| assert payload["phases"]["metadata"] == "complete" |
| assert payload["next_phase"] == "feature_engineering" |
|
|
|
|
| def test_progress_endpoint_unknown_session_404( |
| test_config_loader: ConfigLoader, |
| ) -> None: |
| client = TestClient(create_app(config_loader=test_config_loader)) |
| response = client.get("/api/runs/does_not_exist/progress") |
| assert response.status_code == 404 |
|
|
|
|
| def test_metadata_starter_skips_when_cached(test_config_loader: ConfigLoader) -> None: |
| session_dir = _make_session(test_config_loader, "s_meta_skip") |
| (session_dir / "data" / "data.csv").write_text("a,b\n1,2\n", encoding="utf-8") |
| _write(session_dir / "reports" / "metadata.json", {"problem_type": "classification"}) |
| client = TestClient(create_app(config_loader=test_config_loader)) |
|
|
| response = client.post("/api/metadata", json={"session_id": "s_meta_skip"}) |
|
|
| assert response.status_code == 200 |
| assert response.json()["status"] == "skipped" |
| assert response.json()["artifact"] == "metadata.json" |
|
|
|
|
| def test_feature_engineering_starter_skips_when_cached( |
| test_config_loader: ConfigLoader, |
| ) -> None: |
| session_dir = _make_session(test_config_loader, "s_fe_skip") |
| (session_dir / "data" / "data.csv").write_text("a,b\n1,2\n", encoding="utf-8") |
| _write(session_dir / "reports" / "metadata.json", {}) |
| _write(session_dir / "reports" / "feature_engineering" / "feature_artifact.json", {}) |
| _write(session_dir / "reports" / "model_config.json", {}) |
| client = TestClient(create_app(config_loader=test_config_loader)) |
|
|
| response = client.post( |
| "/api/feature-engineering", json={"session_id": "s_fe_skip"} |
| ) |
|
|
| assert response.status_code == 200 |
| assert response.json()["status"] == "skipped" |
|
|