"""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