animetix-web / tests /api /test_forge_vn_api.py
MissawB's picture
Upload folder using huggingface_hub (part 7)
f39aaec verified
Raw History Blame Contribute Delete
7.57 kB
from unittest.mock import MagicMock
import pytest
from animetix.containers import container
from animetix.models import CreativeFusion
from dependency_injector import providers
from django.contrib.auth.models import User
from django.urls import reverse
from rest_framework import status
@pytest.fixture
def mock_user(db):
user = User.objects.create_user(username="testuser", password="password")
return user
@pytest.fixture
def other_user(db):
user = User.objects.create_user(username="otheruser", password="password")
return user
@pytest.fixture
def sample_fusion(db, mock_user):
return CreativeFusion.objects.create(
title_a="Anime A",
title_b="Anime B",
media_type_a="Anime",
media_type_b="Anime",
scenario_text="Un scénario épique.",
creator=mock_user,
)
@pytest.mark.django_db
class TestTheaterGallery:
"""Public VN gallery (TheaterListView) — bounded, N+1-free, field-whitelisted."""
@staticmethod
def _make_fusions(creator, count, script=True):
return [
CreativeFusion.objects.create(
title_a=f"A{i}",
title_b=f"B{i}",
media_type_a="Anime",
media_type_b="Anime",
scenario_text="Scénario.",
creator=creator,
vn_script={"title": f"S{i}", "scenes": []} if script else None,
)
for i in range(count)
]
def test_gallery_is_bounded(self, api_client, mock_user):
from animetix.api.forge_vn import GALLERY_MAX_ITEMS
self._make_fusions(mock_user, GALLERY_MAX_ITEMS + 5)
url = reverse("api_theater_list")
response = api_client.get(url)
assert response.status_code == status.HTTP_200_OK
assert len(response.data) == GALLERY_MAX_ITEMS
def test_gallery_query_count_does_not_grow_with_items(
self, api_client, mock_user, other_user
):
from django.db import connection
from django.test.utils import CaptureQueriesContext
url = reverse("api_theater_list")
api_client.get(url) # warm-up (middleware get_or_create SiteConfiguration)
for fusion in self._make_fusions(mock_user, 3):
fusion.likes.add(mock_user, other_user)
with CaptureQueriesContext(connection) as small:
response = api_client.get(url)
assert response.status_code == status.HTTP_200_OK
assert all(item["likes_count"] == 2 for item in response.data)
for fusion in self._make_fusions(mock_user, 7):
fusion.likes.add(mock_user, other_user)
with CaptureQueriesContext(connection) as large:
response = api_client.get(url)
assert response.status_code == status.HTTP_200_OK
assert len(response.data) == 10
# Sans prefetch, likes_count/creator_name émettent des requêtes PAR
# LIGNE : passer de 3 à 10 items ferait grossir le décompte.
assert len(large.captured_queries) == len(small.captured_queries)
def test_gallery_does_not_expose_raw_liker_ids(self, api_client, mock_user):
fusion = self._make_fusions(mock_user, 1)[0]
fusion.likes.add(mock_user)
url = reverse("api_theater_list")
response = api_client.get(url)
assert response.status_code == status.HTTP_200_OK
item = response.data[0]
assert "likes" not in item
assert item["likes_count"] == 1
assert item["creator_name"] == mock_user.username
def test_gallery_is_liked_reflects_authenticated_user(
self, api_client, mock_user, other_user
):
liked, not_liked = self._make_fusions(mock_user, 2)
liked.likes.add(other_user)
api_client.force_authenticate(user=other_user)
url = reverse("api_theater_list")
response = api_client.get(url)
assert response.status_code == status.HTTP_200_OK
by_id = {item["id"]: item for item in response.data}
assert by_id[liked.id]["is_liked"] is True
assert by_id[not_liked.id]["is_liked"] is False
@pytest.mark.django_db
class TestForgeVNAPI:
def test_get_vn_script(self, api_client, sample_fusion):
url = reverse("api_forge_vn", kwargs={"fusion_id": sample_fusion.id})
response = api_client.get(url)
assert response.status_code == status.HTTP_200_OK
assert response.data["vn_script"] is None
def test_generate_vn_script_unauthorized(
self, api_client, other_user, sample_fusion
):
api_client.force_authenticate(user=other_user)
url = reverse("api_forge_vn", kwargs={"fusion_id": sample_fusion.id})
data = {"action": "generate"}
response = api_client.post(url, data, format="json")
assert response.status_code == status.HTTP_403_FORBIDDEN
def test_generate_vn_script_success(
self, api_client, mock_user, sample_fusion, mock_container
):
api_client.force_authenticate(user=mock_user)
url = reverse("api_forge_vn", kwargs={"fusion_id": sample_fusion.id})
data = {"action": "generate"}
mock_script_data = {
"title": "Fusion Script",
"scenes": [
{
"character": "Protagonist",
"text": "Hello world",
"mood": "Happy",
"bg_prompt": "Forest",
}
],
}
# Create a mock that behaves like a Pydantic model
class MockScript:
def model_dump(self):
return mock_script_data
# Configure the global mock_container
mock_container.visual_novel_service.generate_script.return_value = MockScript()
mock_container.visual_novel_service.return_value.generate_script.return_value = (
MockScript()
)
mock_guardrail = MagicMock()
mock_guardrail.validate_input.return_value = {"is_safe": True}
mock_guardrail.validate_output.return_value = {"is_safe": True}
mock_usage = MagicMock()
mock_usage.check_quota.return_value = True
with (
container.core.visual_novel_service.override(
providers.Object(mock_container.visual_novel_service)
),
container.core.guardrail_service.override(providers.Object(mock_guardrail)),
container.infrastructure.usage_port.override(providers.Object(mock_usage)),
):
response = api_client.post(url, data, format="json")
assert response.status_code == status.HTTP_200_OK
assert response.data["vn_script"]["title"] == "Fusion Script"
# Verify persistence
sample_fusion.refresh_from_db()
assert sample_fusion.vn_script["title"] == "Fusion Script"
@pytest.mark.integration # exercises the live VN-script generation pipeline (no ollama in CI)
def test_update_vn_script_success(self, api_client, mock_user, sample_fusion):
api_client.force_authenticate(user=mock_user)
url = reverse("api_forge_vn", kwargs={"fusion_id": sample_fusion.id})
new_script = {"title": "Updated Title", "scenes": []}
data = {"action": "update", "vn_script": new_script}
response = api_client.post(url, data, format="json")
assert response.status_code == status.HTTP_200_OK
assert response.data["vn_script"]["title"] == "Updated Title"
sample_fusion.refresh_from_db()
assert sample_fusion.vn_script["title"] == "Updated Title"