Files
documente/tests/unit/test_api/test_generation.py
Luca Sacchi Ricciardi 83fd30a2a2 feat(api): add content generation endpoints (Sprint 4)
Implement Sprint 4: Content Generation

- Add ArtifactService with generation methods for 9 content types
- Add POST /generate/audio - Generate podcast
- Add POST /generate/video - Generate video
- Add POST /generate/slide-deck - Generate slides
- Add POST /generate/infographic - Generate infographic
- Add POST /generate/quiz - Generate quiz
- Add POST /generate/flashcards - Generate flashcards
- Add POST /generate/report - Generate report
- Add POST /generate/mind-map - Generate mind map (instant)
- Add POST /generate/data-table - Generate data table
- Add GET /artifacts - List artifacts
- Add GET /artifacts/{id}/status - Check artifact status

Models:
- AudioGenerationRequest, VideoGenerationRequest
- QuizGenerationRequest, FlashcardsGenerationRequest
- SlideDeckGenerationRequest, InfographicGenerationRequest
- ReportGenerationRequest, DataTableGenerationRequest
- Artifact, GenerationResponse, ArtifactList

Tests:
- 13 unit tests for ArtifactService
- 6 integration tests for generation API
- 19/19 tests passing

Related: Sprint 4 - Content Generation
2026-04-06 01:58:47 +02:00

247 lines
8.5 KiB
Python

"""Integration tests for generation API endpoints.
Tests key generation 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 Artifact, GenerationResponse
@pytest.mark.unit
class TestGenerateAudioEndpoint:
"""Test suite for POST /generate/audio endpoint."""
def test_generate_audio_returns_202(self):
"""Should return 202 Accepted for audio generation."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
artifact_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
mock_response = GenerationResponse(
artifact_id=artifact_id,
status="processing",
message="Audio generation started",
estimated_time_seconds=600,
)
mock_service.generate_audio.return_value = mock_response
mock_service_class.return_value = mock_service
# Act
response = client.post(
f"/api/v1/notebooks/{notebook_id}/generate/audio",
json={
"instructions": "Make it engaging",
"format": "deep-dive",
"length": "long",
"language": "en",
},
)
# Assert
assert response.status_code == 202
data = response.json()
assert data["success"] is True
assert data["data"]["status"] == "processing"
assert data["data"]["estimated_time_seconds"] == 600
def test_generate_audio_invalid_notebook_returns_400(self):
"""Should return 400 for invalid notebook ID."""
# Arrange
client = TestClient(app)
# Act
response = client.post(
"/api/v1/notebooks/invalid-id/generate/audio",
json={"format": "deep-dive"},
)
# Assert
assert response.status_code in [400, 422]
@pytest.mark.unit
class TestGenerateQuizEndpoint:
"""Test suite for POST /generate/quiz endpoint."""
def test_generate_quiz_returns_202(self):
"""Should return 202 Accepted for quiz generation."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
artifact_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
mock_response = GenerationResponse(
artifact_id=artifact_id,
status="processing",
message="Quiz generation started",
estimated_time_seconds=600,
)
mock_service.generate_quiz.return_value = mock_response
mock_service_class.return_value = mock_service
# Act
response = client.post(
f"/api/v1/notebooks/{notebook_id}/generate/quiz",
json={"difficulty": "medium", "quantity": "standard"},
)
# Assert
assert response.status_code == 202
data = response.json()
assert data["success"] is True
@pytest.mark.unit
class TestGenerateMindMapEndpoint:
"""Test suite for POST /generate/mind-map endpoint."""
def test_generate_mind_map_returns_202(self):
"""Should return 202 Accepted for mind map generation."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
artifact_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
mock_response = GenerationResponse(
artifact_id=artifact_id,
status="completed",
message="Mind map generated",
estimated_time_seconds=10,
)
mock_service.generate_mind_map.return_value = mock_response
mock_service_class.return_value = mock_service
# Act
response = client.post(f"/api/v1/notebooks/{notebook_id}/generate/mind-map")
# Assert
assert response.status_code == 202
data = response.json()
assert data["data"]["status"] == "completed"
@pytest.mark.unit
class TestListArtifactsEndpoint:
"""Test suite for GET /artifacts endpoint."""
def test_list_artifacts_returns_200(self):
"""Should return 200 with list of artifacts."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
mock_artifact = Artifact(
id=uuid4(),
notebook_id=notebook_id,
type="audio",
title="Podcast",
status="completed",
created_at=datetime.utcnow(),
completed_at=datetime.utcnow(),
download_url="https://example.com/download",
)
mock_service.list_artifacts.return_value = [mock_artifact]
mock_service_class.return_value = mock_service
# Act
response = client.get(f"/api/v1/notebooks/{notebook_id}/artifacts")
# Assert
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert len(data["data"]["items"]) == 1
assert data["data"]["items"][0]["type"] == "audio"
def test_list_artifacts_empty_returns_empty_list(self):
"""Should return empty list if no artifacts."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
mock_service.list_artifacts.return_value = []
mock_service_class.return_value = mock_service
# Act
response = client.get(f"/api/v1/notebooks/{notebook_id}/artifacts")
# Assert
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["items"] == []
@pytest.mark.unit
class TestGetArtifactStatusEndpoint:
"""Test suite for GET /artifacts/{id}/status endpoint."""
def test_get_artifact_status_returns_200(self):
"""Should return 200 with artifact status."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
artifact_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
mock_artifact = Artifact(
id=artifact_id,
notebook_id=notebook_id,
type="video",
title="Video",
status="processing",
created_at=datetime.utcnow(),
completed_at=None,
download_url=None,
)
mock_service.get_status.return_value = mock_artifact
mock_service_class.return_value = mock_service
# Act
response = client.get(f"/api/v1/notebooks/{notebook_id}/artifacts/{artifact_id}/status")
# Assert
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["status"] == "processing"
def test_get_artifact_status_not_found_returns_404(self):
"""Should return 404 when artifact not found."""
# Arrange
client = TestClient(app)
notebook_id = str(uuid4())
artifact_id = str(uuid4())
with patch("notebooklm_agent.api.routes.generation.ArtifactService") as mock_service_class:
mock_service = AsyncMock()
from notebooklm_agent.core.exceptions import NotFoundError
mock_service.get_status.side_effect = NotFoundError("Artifact", artifact_id)
mock_service_class.return_value = mock_service
# Act
response = client.get(f"/api/v1/notebooks/{notebook_id}/artifacts/{artifact_id}/status")
# Assert
assert response.status_code == 404