- test_media_utils.py: 29 tests for check_media_files, get_media_file_paths, has_all_images, count_video_files, has_video_files, constants - test_nfo_factory.py: 11 tests for NFOServiceFactory.create, create_optional, get_nfo_factory singleton, create_nfo_service convenience - test_series_manager_service.py: 15 tests for init, from_settings, process_nfo_for_series, scan_and_process_nfo, close - test_templates_utils.py: 4 tests for TEMPLATES_DIR path resolution - test_error_controller.py: 7 tests for 404/500 handlers (API vs HTML)
111 lines
3.9 KiB
Python
111 lines
3.9 KiB
Python
"""Unit tests for error controller module.
|
|
|
|
Tests custom 404 and 500 error handlers for both API and HTML responses.
|
|
"""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
from fastapi import HTTPException, Request
|
|
|
|
from src.server.controllers.error_controller import (
|
|
not_found_handler,
|
|
server_error_handler,
|
|
)
|
|
|
|
|
|
def _make_request(path: str = "/") -> MagicMock:
|
|
"""Create a mock Request object with a given path."""
|
|
request = MagicMock(spec=Request)
|
|
url = MagicMock()
|
|
url.path = path
|
|
request.url = url
|
|
return request
|
|
|
|
|
|
class TestNotFoundHandler:
|
|
"""Tests for the 404 not_found_handler."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_api_path_returns_json(self):
|
|
"""API paths get a JSON response with 404 status."""
|
|
request = _make_request("/api/anime/123")
|
|
exc = HTTPException(status_code=404)
|
|
resp = await not_found_handler(request, exc)
|
|
assert resp.status_code == 404
|
|
assert resp.body is not None
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("src.server.controllers.error_controller.render_template")
|
|
async def test_web_path_renders_template(self, mock_render):
|
|
"""Non-API paths render the error template."""
|
|
mock_render.return_value = MagicMock(status_code=404)
|
|
request = _make_request("/anime/details")
|
|
exc = HTTPException(status_code=404)
|
|
await not_found_handler(request, exc)
|
|
mock_render.assert_called_once_with(
|
|
"error.html",
|
|
request,
|
|
context={"error": "Page not found", "status_code": 404},
|
|
title="404 - Not Found",
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_api_json_structure(self):
|
|
"""API 404 response has 'detail' field."""
|
|
import json
|
|
request = _make_request("/api/missing")
|
|
exc = HTTPException(status_code=404)
|
|
resp = await not_found_handler(request, exc)
|
|
body = json.loads(resp.body)
|
|
assert body["detail"] == "API endpoint not found"
|
|
|
|
|
|
class TestServerErrorHandler:
|
|
"""Tests for the 500 server_error_handler."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_api_path_returns_json(self):
|
|
"""API paths get a JSON response with 500 status."""
|
|
request = _make_request("/api/download")
|
|
exc = RuntimeError("crash")
|
|
resp = await server_error_handler(request, exc)
|
|
assert resp.status_code == 500
|
|
|
|
@pytest.mark.asyncio
|
|
@patch("src.server.controllers.error_controller.render_template")
|
|
async def test_web_path_renders_template(self, mock_render):
|
|
"""Non-API paths render the error template."""
|
|
mock_render.return_value = MagicMock(status_code=500)
|
|
request = _make_request("/settings")
|
|
exc = RuntimeError("crash")
|
|
await server_error_handler(request, exc)
|
|
mock_render.assert_called_once_with(
|
|
"error.html",
|
|
request,
|
|
context={"error": "Internal server error", "status_code": 500},
|
|
title="500 - Server Error",
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_api_error_does_not_expose_stack_trace(self):
|
|
"""API 500 response doesn't contain the actual error message."""
|
|
import json
|
|
request = _make_request("/api/vulnerable")
|
|
exc = RuntimeError("secret database credentials")
|
|
resp = await server_error_handler(request, exc)
|
|
body = json.loads(resp.body)
|
|
assert "secret" not in body["detail"]
|
|
assert body["detail"] == "Internal server error"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_api_path_detection(self):
|
|
"""Correctly distinguishes API vs web paths."""
|
|
api_request = _make_request("/api/test")
|
|
web_request = _make_request("/dashboard")
|
|
|
|
# API path returns JSONResponse
|
|
from fastapi.responses import JSONResponse
|
|
resp = await server_error_handler(api_request, Exception("err"))
|
|
assert isinstance(resp, JSONResponse)
|