import asyncio import time import app.api.server as server from app.api.server import chat, critic_feedback, health, list_events, resolve_permission, resolve_review, resolve_secret from app.core.permission_resolution import PermissionResolutionRequest, ReviewResolutionRequest, SecretResolutionRequest from app.api.server import CriticFeedbackRequest from app.core.contracts import UserTask def test_health_handler() -> None: assert health() == {"status": "ok"} def test_events_handler_returns_event_list() -> None: body = list_events(limit=10) assert "events" in body assert isinstance(body["events"], list) def test_chat_handler_returns_runtime_events() -> None: body = chat(UserTask(input="hello from handler test")) assert body["status"] in {"accepted", "completed"} if body["status"] == "completed": assert body["events"][0]["type"] == "task_received" def test_chat_handler_submits_task_without_waiting_for_completion(monkeypatch) -> None: class SlowRuntime: def submit_task(self, task): return {"task_id": task.task_id, "status": "accepted"} def handle_task(self, task): time.sleep(0.25) return {"task_id": task.task_id, "status": "completed", "events": []} monkeypatch.setattr("app.api.server.runtime", SlowRuntime()) started = time.monotonic() body = chat(UserTask(input="long task")) assert time.monotonic() - started < 0.1 assert body["status"] == "accepted" def test_lifespan_loads_models_without_threadpool_executor(monkeypatch) -> None: class FakeRuntime: _memory_interface = None def __init__(self) -> None: self.loaded = False def load_models_at_startup(self) -> None: self.loaded = True class FailingLoop: def run_in_executor(self, *args, **kwargs): raise AssertionError("lifespan must not load llama models via run_in_executor") fake_runtime = FakeRuntime() monkeypatch.setattr(server, "runtime", fake_runtime) monkeypatch.setattr(server.asyncio, "get_event_loop", lambda: FailingLoop()) async def run_lifespan() -> None: async with server.lifespan(None): pass asyncio.run(run_lifespan()) assert fake_runtime.loaded is True def test_resolve_permission_handler_allows_completion() -> None: initial = chat(UserTask(input="запусти pwd")) if initial["status"] == "awaiting_permission": body = resolve_permission( PermissionResolutionRequest(task_id=initial["task_id"], decision="allow_once") ) assert body["status"] in {"completed", "failed"} def test_resolve_secret_handler_requires_pending_request() -> None: body = resolve_secret(SecretResolutionRequest(task_id="missing", secret="x")) assert body["status"] == "failed" def test_resolve_review_handler_submits_review_resolution(monkeypatch) -> None: class ReviewRuntime: def submit_review_resolution(self, task_id, decision, correction=None): return { "task_id": task_id, "status": "accepted", "decision": decision, "correction": correction, } monkeypatch.setattr("app.api.server.runtime", ReviewRuntime()) body = resolve_review( ReviewResolutionRequest( task_id="task-1", decision="wrong_action", correction="replan", ) ) assert body["status"] == "accepted" assert body["decision"] == "wrong_action" def test_structured_feedback_can_be_accepted_without_memory_write() -> None: initial = chat(UserTask(input="feedback target")) body = critic_feedback( CriticFeedbackRequest( task_id=initial["task_id"], feedback="wrong answer", feedback_type="hallucination", severity="major", correction="check first", remember=False, ) ) assert body["status"] == "ok" assert body["stored"] is False assert "hallucination" in body["lesson"]