This commit is contained in:
+48
-37
@@ -4,6 +4,8 @@ import threading
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
import src.task_manager as task_manager_module
|
||||
from src.config import AppConfig
|
||||
from src.models import AgentResult, RunArtifacts, UsageSummary
|
||||
@@ -11,15 +13,7 @@ from src.storage import HistoryDB
|
||||
from src.task_manager import JobManager
|
||||
|
||||
|
||||
class _OverlayRecorder:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[dict[str, Any]] = []
|
||||
|
||||
def show_completion(self, **kwargs: Any) -> None:
|
||||
self.calls.append(kwargs)
|
||||
|
||||
|
||||
def _build_manager(tmp_path: Path, overlay_manager: _OverlayRecorder) -> tuple[JobManager, HistoryDB, AppConfig]:
|
||||
def _build_manager(tmp_path: Path) -> tuple[JobManager, HistoryDB, AppConfig]:
|
||||
config = AppConfig(
|
||||
openai_api_key="test-key",
|
||||
screenjob_token="test-token",
|
||||
@@ -32,7 +26,7 @@ def _build_manager(tmp_path: Path, overlay_manager: _OverlayRecorder) -> tuple[J
|
||||
db_path=tmp_path / "screenjob.db",
|
||||
)
|
||||
db = HistoryDB(config.db_path)
|
||||
manager = JobManager(config=config, db=db, overlay_manager=overlay_manager)
|
||||
manager = JobManager(config=config, db=db)
|
||||
return manager, db, config
|
||||
|
||||
|
||||
@@ -59,10 +53,9 @@ def _create_job(db: HistoryDB, job_id: str, objective: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_completed_job_triggers_desktop_overlay(tmp_path: Path, monkeypatch) -> None:
|
||||
overlay = _OverlayRecorder()
|
||||
manager, db, _config = _build_manager(tmp_path, overlay)
|
||||
job_id = "job_overlay_complete"
|
||||
def test_completed_job_updates_status(tmp_path: Path, monkeypatch) -> None:
|
||||
manager, db, _config = _build_manager(tmp_path)
|
||||
job_id = "job_complete"
|
||||
objective = "Save todo-demo.txt in Documents"
|
||||
_create_job(db, job_id, objective)
|
||||
|
||||
@@ -101,23 +94,16 @@ def test_completed_job_triggers_desktop_overlay(tmp_path: Path, monkeypatch) ->
|
||||
cancel_event=threading.Event(),
|
||||
)
|
||||
|
||||
assert overlay.calls == [
|
||||
{
|
||||
"job_id": job_id,
|
||||
"objective": objective,
|
||||
"return_message": "Saved todo-demo.txt",
|
||||
"steps": 11,
|
||||
"elapsed_seconds": 12.599999999999994,
|
||||
}
|
||||
]
|
||||
assert db.get_job(job_id)["status"] == "completed"
|
||||
job = db.get_job(job_id)
|
||||
assert job is not None
|
||||
assert job["status"] == "completed"
|
||||
assert job["return"] == "Saved todo-demo.txt"
|
||||
|
||||
|
||||
def test_non_completed_jobs_do_not_trigger_desktop_overlay(tmp_path: Path, monkeypatch) -> None:
|
||||
overlay = _OverlayRecorder()
|
||||
manager, db, _config = _build_manager(tmp_path, overlay)
|
||||
def test_non_completed_jobs_are_recorded(tmp_path: Path, monkeypatch) -> None:
|
||||
manager, db, _config = _build_manager(tmp_path)
|
||||
|
||||
failed_job_id = "job_overlay_failed"
|
||||
failed_job_id = "job_failed"
|
||||
_create_job(db, failed_job_id, "Fail intentionally")
|
||||
failed_result = AgentResult(
|
||||
completed=False,
|
||||
@@ -131,7 +117,6 @@ def test_non_completed_jobs_do_not_trigger_desktop_overlay(tmp_path: Path, monke
|
||||
error="Failure",
|
||||
)
|
||||
monkeypatch.setattr(task_manager_module, "run_job", lambda **_kwargs: (failed_result, _artifacts(tmp_path)))
|
||||
|
||||
manager._execute_job(
|
||||
job_id=failed_job_id,
|
||||
objective="Fail intentionally",
|
||||
@@ -155,7 +140,7 @@ def test_non_completed_jobs_do_not_trigger_desktop_overlay(tmp_path: Path, monke
|
||||
cancel_event=threading.Event(),
|
||||
)
|
||||
|
||||
cancelled_job_id = "job_overlay_cancelled"
|
||||
cancelled_job_id = "job_cancelled"
|
||||
_create_job(db, cancelled_job_id, "Cancel intentionally")
|
||||
cancelled_result = AgentResult(
|
||||
completed=False,
|
||||
@@ -170,7 +155,6 @@ def test_non_completed_jobs_do_not_trigger_desktop_overlay(tmp_path: Path, monke
|
||||
cancelled=True,
|
||||
)
|
||||
monkeypatch.setattr(task_manager_module, "run_job", lambda **_kwargs: (cancelled_result, _artifacts(tmp_path)))
|
||||
|
||||
manager._execute_job(
|
||||
job_id=cancelled_job_id,
|
||||
objective="Cancel intentionally",
|
||||
@@ -194,13 +178,13 @@ def test_non_completed_jobs_do_not_trigger_desktop_overlay(tmp_path: Path, monke
|
||||
cancel_event=threading.Event(),
|
||||
)
|
||||
|
||||
assert overlay.calls == []
|
||||
assert db.get_job(failed_job_id)["status"] == "failed"
|
||||
assert db.get_job(cancelled_job_id)["status"] == "cancelled"
|
||||
|
||||
|
||||
def test_rejected_job_does_not_trigger_desktop_overlay(tmp_path: Path, monkeypatch) -> None:
|
||||
overlay = _OverlayRecorder()
|
||||
manager, db, _config = _build_manager(tmp_path, overlay)
|
||||
job_id = "job_overlay_rejected"
|
||||
def test_rejected_job_is_recorded(tmp_path: Path, monkeypatch) -> None:
|
||||
manager, db, _config = _build_manager(tmp_path)
|
||||
job_id = "job_rejected"
|
||||
_create_job(db, job_id, "Do something unsafe")
|
||||
|
||||
monkeypatch.setattr(task_manager_module, "create_openai_client", lambda *_args, **_kwargs: object())
|
||||
@@ -233,6 +217,33 @@ def test_rejected_job_does_not_trigger_desktop_overlay(tmp_path: Path, monkeypat
|
||||
cancel_event=threading.Event(),
|
||||
)
|
||||
|
||||
assert overlay.calls == []
|
||||
events = db.get_job_events(job_id)
|
||||
assert events[-1]["event_type"] == "job_rejected"
|
||||
|
||||
|
||||
def test_submit_job_rejects_when_another_run_is_active(tmp_path: Path) -> None:
|
||||
manager, _db, _config = _build_manager(tmp_path)
|
||||
ready = threading.Event()
|
||||
release = threading.Event()
|
||||
|
||||
def _hold() -> None:
|
||||
ready.set()
|
||||
release.wait()
|
||||
|
||||
active_thread = threading.Thread(target=_hold)
|
||||
active_thread.start()
|
||||
ready.wait(1)
|
||||
manager._running["job_active"] = task_manager_module._RunningJob(
|
||||
thread=active_thread,
|
||||
cancel_event=threading.Event(),
|
||||
started_at="2026-05-30T12:00:00+00:00",
|
||||
objective="Active",
|
||||
model="gpt-5.4-mini",
|
||||
)
|
||||
|
||||
try:
|
||||
with pytest.raises(ValueError, match="already active"):
|
||||
manager.submit_job(objective="Second run")
|
||||
finally:
|
||||
release.set()
|
||||
active_thread.join(timeout=1)
|
||||
|
||||
Reference in New Issue
Block a user