Newer
Older
navi-1 / tests / integration / test_api_routes.py
"""Integration tests for REST API routes."""

import pytest

from navi.llm.base import Message


class TestHealth:
    def test_health(self, client):
        response = client.get("/health")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "ok"
        assert "embed" in data

    def test_health_embed(self, client):
        response = client.get("/health/embed")
        assert response.status_code == 200
        data = response.json()
        assert "ok" in data


class TestAgents:
    def test_list_profiles(self, client):
        response = client.get("/agents/profiles")
        assert response.status_code == 200
        data = response.json()
        assert len(data) >= 2
        assert any(p["id"] == "secretary" for p in data)

    def test_list_tools(self, client):
        response = client.get("/agents/tools")
        assert response.status_code == 200
        data = response.json()
        assert len(data) >= 2
        names = {t["name"] for t in data}
        assert "test_tool" in names


class TestSessions:
    async def test_create_session(self, client):
        response = client.post("/sessions", json={"profile_id": "secretary"})
        assert response.status_code == 201
        data = response.json()
        assert "session_id" in data
        assert data["profile_id"] == "secretary"

    def test_create_session_invalid_profile(self, client):
        response = client.post("/sessions", json={"profile_id": "nonexistent"})
        assert response.status_code == 404

    @pytest.mark.anyio
    async def test_list_sessions(self, client, make_session):
        session = await make_session("secretary", [Message(role="user", content="hi")])
        response = client.get("/sessions")
        assert response.status_code == 200
        data = response.json()
        assert any(s["session_id"] == session.id for s in data)

    @pytest.mark.anyio
    async def test_get_session(self, client, make_session):
        session = await make_session("secretary")
        response = client.get(f"/sessions/{session.id}")
        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == session.id
        assert data["profile_id"] == "secretary"

    def test_get_session_not_found(self, client):
        response = client.get("/sessions/nonexistent")
        assert response.status_code == 404

    @pytest.mark.anyio
    async def test_pin_session(self, client, make_session):
        session = await make_session("secretary")
        response = client.patch(f"/sessions/{session.id}/pin", json={"pinned": True})
        assert response.status_code == 200
        data = response.json()
        assert data["pinned"] is True

    def test_pin_session_not_found(self, client):
        response = client.patch("/sessions/nonexistent/pin", json={"pinned": True})
        assert response.status_code == 404

    @pytest.mark.anyio
    async def test_get_context(self, client, make_session, mock_deps):
        session = await make_session("secretary", [Message(role="user", content="hello")])
        session.context.append(Message(role="user", content="hello"))
        await mock_deps["session_store"].save(session)
        response = client.get(f"/sessions/{session.id}/context")
        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == session.id
        assert data["message_count"] == 1

    def test_get_context_not_found(self, client):
        response = client.get("/sessions/nonexistent/context")
        assert response.status_code == 404

    @pytest.mark.anyio
    async def test_get_planning(self, client, make_session):
        session = await make_session("secretary")
        session.planning_logs.append({"phases": {}})
        response = client.get(f"/sessions/{session.id}/planning")
        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == session.id
        assert len(data["logs"]) == 1

    def test_get_planning_not_found(self, client):
        response = client.get("/sessions/nonexistent/planning")
        assert response.status_code == 404

    @pytest.mark.anyio
    async def test_delete_session(self, client, make_session):
        session = await make_session("secretary")
        response = client.delete(f"/sessions/{session.id}")
        assert response.status_code == 204
        assert client.get(f"/sessions/{session.id}").status_code == 404

    def test_delete_session_not_found(self, client):
        response = client.delete("/sessions/nonexistent")
        assert response.status_code == 404


class TestMessages:
    @pytest.mark.anyio
    async def test_send_message(self, client, make_session, monkeypatch):
        session = await make_session("secretary")

        class DummyAgent:
            async def run(self, session_id, user_message, images=None):
                return "Response text"

        # Patch the Agent class in deps so the original get_agent() (captured by
        # Depends() at import time) instantiates our dummy when called.
        monkeypatch.setattr("navi.api.deps.Agent", lambda *a, **kw: DummyAgent())
        response = client.post(f"/sessions/{session.id}/messages", json={"content": "hi"})
        assert response.status_code == 200
        data = response.json()
        assert data["role"] == "assistant"
        assert data["content"] == "Response text"

    def test_send_message_not_found(self, client):
        response = client.post("/sessions/nonexistent/messages", json={"content": "hi"})
        assert response.status_code == 404