"""Integration tests for chat API endpoints. Tests all chat endpoints with mocked services. """ from datetime import datetime from unittest.mock import AsyncMock, patch from uuid import uuid4 import pytest from fastapi.testclient import TestClient from notebooklm_agent.api.main import app from notebooklm_agent.api.models.responses import ChatMessage, ChatResponse, SourceReference @pytest.mark.unit class TestSendMessageEndpoint: """Test suite for POST /api/v1/notebooks/{id}/chat endpoint.""" def test_send_message_returns_200(self): """Should return 200 with chat response.""" # Arrange client = TestClient(app) notebook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.chat.ChatService") as mock_service_class: mock_service = AsyncMock() mock_response = ChatResponse( message="This is the answer based on your sources.", sources=[ SourceReference( source_id=str(uuid4()), title="Example Source", snippet="Relevant text", ) ], timestamp=datetime.utcnow(), ) mock_service.send_message.return_value = mock_response mock_service_class.return_value = mock_service # Act response = client.post( f"/api/v1/notebooks/{notebook_id}/chat", json={"message": "What are the key points?", "include_references": True}, ) # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert "answer based on your sources" in data["data"]["message"] assert len(data["data"]["sources"]) == 1 def test_send_message_invalid_notebook_id_returns_400(self): """Should return 400 for invalid notebook ID.""" # Arrange client = TestClient(app) # Act response = client.post( "/api/v1/notebooks/invalid-id/chat", json={"message": "Question?"}, ) # Assert assert response.status_code in [400, 422] def test_send_message_empty_message_returns_400(self): """Should return 400 for empty message.""" # Arrange client = TestClient(app) notebook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.chat.ChatService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import ValidationError mock_service.send_message.side_effect = ValidationError("Message cannot be empty") mock_service_class.return_value = mock_service # Act response = client.post( f"/api/v1/notebooks/{notebook_id}/chat", json={"message": ""}, ) # Assert assert response.status_code in [400, 422] def test_send_message_notebook_not_found_returns_404(self): """Should return 404 when notebook not found.""" # Arrange client = TestClient(app) notebook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.chat.ChatService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import NotFoundError mock_service.send_message.side_effect = NotFoundError("Notebook", notebook_id) mock_service_class.return_value = mock_service # Act response = client.post( f"/api/v1/notebooks/{notebook_id}/chat", json={"message": "Question?"}, ) # Assert assert response.status_code == 404 @pytest.mark.unit class TestGetChatHistoryEndpoint: """Test suite for GET /api/v1/notebooks/{id}/chat/history endpoint.""" def test_get_history_returns_200(self): """Should return 200 with chat history.""" # Arrange client = TestClient(app) notebook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.chat.ChatService") as mock_service_class: mock_service = AsyncMock() mock_message = ChatMessage( id=str(uuid4()), role="user", content="What are the key points?", timestamp=datetime.utcnow(), sources=None, ) mock_service.get_history.return_value = [mock_message] mock_service_class.return_value = mock_service # Act response = client.get(f"/api/v1/notebooks/{notebook_id}/chat/history") # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert len(data["data"]) == 1 assert data["data"][0]["role"] == "user" def test_get_history_empty_returns_empty_list(self): """Should return empty list if no history.""" # Arrange client = TestClient(app) notebook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.chat.ChatService") as mock_service_class: mock_service = AsyncMock() mock_service.get_history.return_value = [] mock_service_class.return_value = mock_service # Act response = client.get(f"/api/v1/notebooks/{notebook_id}/chat/history") # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["data"] == [] def test_get_history_invalid_notebook_id_returns_400(self): """Should return 400 for invalid notebook ID.""" # Arrange client = TestClient(app) # Act response = client.get("/api/v1/notebooks/invalid-id/chat/history") # Assert assert response.status_code in [400, 422] def test_get_history_notebook_not_found_returns_404(self): """Should return 404 when notebook not found.""" # Arrange client = TestClient(app) notebook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.chat.ChatService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import NotFoundError mock_service.get_history.side_effect = NotFoundError("Notebook", notebook_id) mock_service_class.return_value = mock_service # Act response = client.get(f"/api/v1/notebooks/{notebook_id}/chat/history") # Assert assert response.status_code == 404