feat: run tests in background with progress polling

Improve the testing dashboard to run pytest in the background:

- Add background task execution using FastAPI's BackgroundTasks
- Create test_runner_tasks.py following existing background task pattern
- API now returns immediately after starting the test run
- Frontend polls for status every 2 seconds until completion
- Show running indicator with elapsed time counter
- Resume polling if user navigates away and returns while tests running
- Tests continue running even if user closes the page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-12 23:20:26 +01:00
parent 6e3ae4bebb
commit 0e6c9e3eea
5 changed files with 228 additions and 41 deletions

View File

@@ -25,6 +25,25 @@ class TestRunnerService:
def __init__(self):
self.project_root = Path(__file__).parent.parent.parent
def create_test_run(
self,
db: Session,
test_path: str = "tests",
triggered_by: str = "manual",
) -> TestRun:
"""Create a test run record without executing tests"""
test_run = TestRun(
timestamp=datetime.now(UTC),
triggered_by=triggered_by,
test_path=test_path,
status="running",
git_commit_hash=self._get_git_commit(),
git_branch=self._get_git_branch(),
)
db.add(test_run)
db.flush()
return test_run
def run_tests(
self,
db: Session,
@@ -33,7 +52,7 @@ class TestRunnerService:
extra_args: list[str] | None = None,
) -> TestRun:
"""
Run pytest and store results in database
Run pytest synchronously and store results in database
Args:
db: Database session
@@ -44,21 +63,19 @@ class TestRunnerService:
Returns:
TestRun object with results
"""
# Create test run record
test_run = TestRun(
timestamp=datetime.now(UTC),
triggered_by=triggered_by,
test_path=test_path,
status="running",
)
db.add(test_run)
db.flush() # Get the ID
test_run = self.create_test_run(db, test_path, triggered_by)
self._execute_tests(db, test_run, test_path, extra_args)
return test_run
def _execute_tests(
self,
db: Session,
test_run: TestRun,
test_path: str,
extra_args: list[str] | None,
) -> None:
"""Execute pytest and update the test run record"""
try:
# Get git info
test_run.git_commit_hash = self._get_git_commit()
test_run.git_branch = self._get_git_branch()
# Build pytest command with JSON output
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json_report_path = f.name
@@ -66,7 +83,7 @@ class TestRunnerService:
pytest_args = [
"python", "-m", "pytest",
test_path,
f"--json-report",
"--json-report",
f"--json-report-file={json_report_path}",
"-v",
"--tb=short",
@@ -120,8 +137,6 @@ class TestRunnerService:
test_run.status = "error"
logger.error(f"Error running tests: {e}")
return test_run
def _process_json_report(self, db: Session, test_run: TestRun, report: dict):
"""Process pytest-json-report output"""
summary = report.get("summary", {})