Files
Aniworld/tests/api/test_nfo_diagnostics_repair.py
Lukas 6dc3cda810 feat(nfo): implement NFO diagnostics and repair
- Add NFO API endpoints: diagnostics, repair, validate, needs-repair
- Create /settings/nfo page with full NFO management UI
- Add NFO status section to edit modal with repair functionality
- Add anime details API for edit modal pre-fill data
- Fix auth test fixtures in test_nfo_diagnostics_repair.py

Implements NFO diagnostics when editing anime series via right-click menu.
Users can now check NFO status, see missing tags, and repair NFOs
directly from the edit modal or the dedicated NFO settings page.
2026-06-14 19:34:58 +02:00

156 lines
5.4 KiB
Python

"""Tests for NFO diagnostics and repair API endpoints.
These tests verify the NFO diagnostics, repair, validate, and needs-repair
endpoints. Note: The existing conftest.py sets up auth automatically, so
we don't need to redefine the client fixture here.
"""
from unittest.mock import AsyncMock, Mock
import pytest
from httpx import ASGITransport, AsyncClient
from src.server.fastapi_app import app
from src.server.services.auth_service import auth_service
# Note: conftest.py already handles auth reset via reset_auth_and_rate_limits
# The reset_auth fixture here is only needed for tests that explicitly
# need a clean auth state BEFORE conftest's setup runs.
# @pytest.fixture(autouse=True)
@pytest.fixture
def reset_auth():
"""Reset authentication state before each test."""
original_hash = auth_service._hash
auth_service._hash = None
auth_service._failed.clear()
yield
auth_service._hash = original_hash
auth_service._failed.clear()
@pytest.fixture
async def client():
"""Create an async test client."""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac
@pytest.fixture
async def authenticated_client(client):
"""Create an authenticated test client with token.
Note: conftest.py already sets up auth with password "TestPass123!".
This fixture just logs in to get a token.
"""
# Login to get token (auth is already set up by conftest)
response = await client.post(
"/api/auth/login",
json={"password": "TestPass123!"}
)
# If already logged in from conftest, might return error - that's ok
if response.status_code == 200:
token = response.json()["access_token"]
client.headers.update({"Authorization": f"Bearer {token}"})
yield client
@pytest.fixture
def mock_anime_service():
"""Create mock anime service."""
service = Mock()
service.list_series_with_filters = AsyncMock(return_value=[])
return service
@pytest.fixture
def override_anime_service(mock_anime_service):
"""Override anime service dependency."""
from src.server.utils.dependencies import get_anime_service
app.dependency_overrides[get_anime_service] = lambda: mock_anime_service
yield
if get_anime_service in app.dependency_overrides:
del app.dependency_overrides[get_anime_service]
class TestNfoNeedsRepair:
"""Tests for GET /api/nfo/needs-repair."""
@pytest.mark.asyncio
async def test_needs_repair_requires_auth(self, client):
"""Test needs-repair endpoint requires authentication."""
response = await client.get("/api/nfo/needs-repair")
# Without auth, returns 401
assert response.status_code == 401
@pytest.mark.asyncio
async def test_needs_repair_returns_data(
self, authenticated_client, override_anime_service
):
"""Test needs-repair endpoint returns proper structure when mocked."""
response = await authenticated_client.get("/api/nfo/needs-repair")
# Should return 200 (when anime_directory is mocked) or 503 (not mocked)
assert response.status_code in (200, 503)
if response.status_code == 200:
data = response.json()
assert "total" in data
assert "missing_nfo_count" in data
assert "incomplete_nfo_count" in data
assert "series" in data
class TestNfoDiagnostics:
"""Tests for GET /api/nfo/{serie_key}/diagnostics."""
@pytest.mark.asyncio
async def test_diagnostics_requires_auth(self, client):
"""Test diagnostics endpoint requires authentication."""
response = await client.get("/api/nfo/test-anime/diagnostics")
# Without auth, returns 401
assert response.status_code == 401
@pytest.mark.asyncio
async def test_diagnostics_returns_404_for_nonexistent(
self, authenticated_client, override_anime_service
):
"""Test diagnostics for non-existent series returns 404."""
response = await authenticated_client.get("/api/nfo/nonexistent-key/diagnostics")
assert response.status_code == 404
class TestNfoRepair:
"""Tests for POST /api/nfo/{serie_key}/repair."""
@pytest.mark.asyncio
async def test_repair_requires_auth(self, client):
"""Test repair endpoint requires authentication."""
response = await client.post("/api/nfo/test-anime/repair", json={})
assert response.status_code == 401
@pytest.mark.asyncio
async def test_repair_returns_404_for_nonexistent(
self, authenticated_client, override_anime_service
):
"""Test repair for non-existent series returns 404."""
response = await authenticated_client.post("/api/nfo/nonexistent-key/repair", json={})
assert response.status_code == 404
class TestNfoValidate:
"""Tests for GET /api/nfo/{serie_key}/validate."""
@pytest.mark.asyncio
async def test_validate_requires_auth(self, client):
"""Test validate endpoint requires authentication."""
response = await client.get("/api/nfo/test-anime/validate")
assert response.status_code == 401
@pytest.mark.asyncio
async def test_validate_returns_404_for_nonexistent(
self, authenticated_client, override_anime_service
):
"""Test validate for non-existent series returns 404."""
response = await authenticated_client.get("/api/nfo/nonexistent-key/validate")
assert response.status_code == 404