import os import pytest from unittest.mock import patch, AsyncMock os.environ["API_SECRET"] = "test-secret" os.environ["OPENROUTER_API_KEY"] = "test-key" os.environ["KENSHI_CALLBACK_URL"] = "http://kenshi-test/api/agent-report" os.environ["KENSHI_CALLBACK_SECRET"] = "callback-secret" import sys from unittest.mock import MagicMock sys.modules.setdefault('browser_use', MagicMock()) import jobs from fastapi.testclient import TestClient @pytest.fixture(autouse=True) def isolated_db(tmp_path, monkeypatch): monkeypatch.setattr(jobs, "DB_PATH", str(tmp_path / "test.db")) jobs.init_db() @pytest.fixture def client(): from app import app with TestClient(app) as c: yield c def test_health_returns_ok(client): r = client.get("/health") assert r.status_code == 200 assert r.json()["status"] == "ok" assert "jobs_running" in r.json() def test_run_async_returns_401_on_wrong_secret(client): r = client.post("/run/async", json={ "task": "browse google.com", "chat_id": "123", "secret": "wrong" }) assert r.status_code == 401 def test_run_async_queues_job_and_returns_job_id(client): with patch("app._run_async_job", new_callable=AsyncMock): r = client.post("/run/async", json={ "task": "browse google.com", "chat_id": "123", "secret": "test-secret" }) assert r.status_code == 200 data = r.json() assert data["status"] == "queued" assert data["job_id"].startswith("kaze_") def test_get_job_returns_404_for_missing_id(client): r = client.get("/job/kaze_nonexistent") assert r.status_code == 404 def test_get_job_returns_job_after_async_dispatch(client): with patch("app._run_async_job", new_callable=AsyncMock): create = client.post("/run/async", json={ "task": "find prices on example.com", "chat_id": "456", "secret": "test-secret", }) job_id = create.json()["job_id"] r = client.get(f"/job/{job_id}") assert r.status_code == 200 assert r.json()["task"] == "find prices on example.com" assert r.json()["status"] in ("queued", "running") def test_run_async_returns_503_when_at_capacity(client): j1 = jobs.create_job("task 1", "c1") j2 = jobs.create_job("task 2", "c2") jobs.update_job(j1, "running") jobs.update_job(j2, "running") r = client.post("/run/async", json={ "task": "overflow task", "chat_id": "789", "secret": "test-secret" }) assert r.status_code == 503 def test_run_sync_returns_401_on_wrong_secret(client): with patch("app.run_task", new_callable=AsyncMock, return_value={"result": "ok", "screenshots": []}): r = client.post("/run", json={ "task": "browse google.com", "chat_id": "123", "secret": "wrong" }) assert r.status_code == 401 def test_run_sync_returns_503_when_at_capacity(client): j1 = jobs.create_job("task 1", "c1") j2 = jobs.create_job("task 2", "c2") jobs.update_job(j1, "running") jobs.update_job(j2, "running") with patch("app.run_task", new_callable=AsyncMock, return_value={"result": "ok", "screenshots": []}): r = client.post("/run", json={ "task": "overflow task", "chat_id": "789", "secret": "test-secret" }) assert r.status_code == 503