Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions bench_loop/dashboard/api/routes/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import asyncio
import json
import shutil
import time
import uuid
from datetime import datetime, timezone
Expand Down Expand Up @@ -386,3 +387,25 @@ async def get_run(run_id: str):
"result": data,
"hardware": hw_data,
}


@router.delete("/benchmark/runs/{run_id}")
async def delete_run(run_id: str):
"""Delete a persisted local benchmark run."""
state = _active_runs.get(run_id)
if state and state.get("status") not in ("completed", "failed", "cancelled"):
raise HTTPException(status_code=409, detail="Cannot delete an active run. Cancel it first.")

runs_root = RUNS_DIR.resolve()
run_dir = (RUNS_DIR / run_id).resolve()
if run_dir.parent != runs_root:
raise HTTPException(status_code=400, detail="Invalid run id")

if not run_dir.exists():
raise HTTPException(status_code=404, detail="Run not found")
if not run_dir.is_dir():
raise HTTPException(status_code=400, detail="Run path is not a directory")

shutil.rmtree(run_dir)
_active_runs.pop(run_id, None)
return {"ok": True, "run_id": run_id}
52 changes: 52 additions & 0 deletions tests/test_dashboard_runs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import asyncio

import pytest
from fastapi import HTTPException

from bench_loop.dashboard.api.routes import benchmark


def run(coro):
return asyncio.run(coro)


def test_delete_run_removes_persisted_run_directory(tmp_path, monkeypatch):
monkeypatch.setattr(benchmark, "RUNS_DIR", tmp_path)
run_dir = tmp_path / "20260601-120000-model-local-ollama"
run_dir.mkdir()
(run_dir / "run.json").write_text("{}", encoding="utf-8")

result = run(benchmark.delete_run(run_dir.name))

assert result == {"ok": True, "run_id": run_dir.name}
assert not run_dir.exists()


def test_delete_run_rejects_path_traversal(tmp_path, monkeypatch):
monkeypatch.setattr(benchmark, "RUNS_DIR", tmp_path)

with pytest.raises(HTTPException) as exc:
run(benchmark.delete_run("../outside"))

assert exc.value.status_code == 400


def test_delete_run_rejects_active_run(tmp_path, monkeypatch):
monkeypatch.setattr(benchmark, "RUNS_DIR", tmp_path)
benchmark._active_runs["active123"] = {"status": "running"}

try:
with pytest.raises(HTTPException) as exc:
run(benchmark.delete_run("active123"))
assert exc.value.status_code == 409
finally:
benchmark._active_runs.pop("active123", None)


def test_delete_run_returns_404_for_missing_run(tmp_path, monkeypatch):
monkeypatch.setattr(benchmark, "RUNS_DIR", tmp_path)

with pytest.raises(HTTPException) as exc:
run(benchmark.delete_run("missing"))

assert exc.value.status_code == 404