Spaces:
Paused
Paused
File size: 7,170 Bytes
a5784e9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """
High-quality tests for api_utils/routers/static.py - Static file serving.
Focus: Test static file endpoints with both success and error paths.
Strategy: Mock Path.exists() to control file existence, test actual routing logic.
"""
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
from fastapi import HTTPException
class TestReadIndex:
"""Tests for read_index endpoint."""
@pytest.mark.asyncio
async def test_read_index_react_exists(self):
"""
Test scenario: React index.html exists
Expected: Return FileResponse with React index.html
"""
from api_utils.routers.static import read_index
mock_logger = MagicMock()
with patch.object(Path, "exists", return_value=True):
response = await read_index(logger=mock_logger)
assert response is not None
@pytest.mark.asyncio
async def test_read_index_not_built(self):
"""
Test scenario: React build does not exist
Expected: Return 503 error (Service Unavailable)
"""
from api_utils.routers.static import read_index
mock_logger = MagicMock()
with patch.object(Path, "exists", return_value=False):
with pytest.raises(HTTPException) as exc_info:
await read_index(logger=mock_logger)
assert exc_info.value.status_code == 503
assert "Frontend not built" in exc_info.value.detail
mock_logger.error.assert_called_once()
class TestServeReactAssets:
"""Tests for serve_react_assets endpoint."""
@pytest.mark.asyncio
async def test_serve_react_assets_js(self):
"""
Test scenario: JS asset exists
Expected: Return FileResponse with application/javascript media type
"""
from api_utils.routers.static import serve_react_assets
mock_logger = MagicMock()
with patch.object(Path, "exists", return_value=True):
response = await serve_react_assets("main.js", logger=mock_logger)
assert response is not None
assert response.media_type == "application/javascript"
@pytest.mark.asyncio
async def test_serve_react_assets_css(self):
"""
Test scenario: CSS asset exists
Expected: Return FileResponse with text/css media type
"""
from api_utils.routers.static import serve_react_assets
mock_logger = MagicMock()
with patch.object(Path, "exists", return_value=True):
response = await serve_react_assets("style.css", logger=mock_logger)
assert response is not None
assert response.media_type == "text/css"
@pytest.mark.asyncio
async def test_serve_react_assets_map(self):
"""
Test scenario: Source map asset exists
Expected: Return FileResponse with application/json media type
"""
from api_utils.routers.static import serve_react_assets
mock_logger = MagicMock()
with patch.object(Path, "exists", return_value=True):
response = await serve_react_assets("main.js.map", logger=mock_logger)
assert response is not None
assert response.media_type == "application/json"
@pytest.mark.asyncio
async def test_serve_react_assets_not_found(self):
"""
Test scenario: Asset not found
Expected: Return 404 error
"""
from api_utils.routers.static import serve_react_assets
mock_logger = MagicMock()
with patch.object(Path, "exists", return_value=False):
with pytest.raises(HTTPException) as exc_info:
await serve_react_assets("missing.js", logger=mock_logger)
assert exc_info.value.status_code == 404
assert "missing.js" in exc_info.value.detail
mock_logger.debug.assert_called_once()
class TestGetStaticFilesApp:
"""Tests for get_static_files_app factory function."""
def test_get_static_files_app_exists(self):
"""
Test scenario: Assets directory exists
Expected: Return StaticFiles instance
"""
from api_utils.routers.static import get_static_files_app
with (
patch("api_utils.routers.static.Path.exists", return_value=True),
patch("os.path.isdir", return_value=True),
):
result = get_static_files_app()
assert result is not None
def test_get_static_files_app_not_exists(self):
"""
Test scenario: Assets directory does not exist
Expected: Return None
"""
from api_utils.routers.static import get_static_files_app
with patch.object(Path, "exists", return_value=False):
result = get_static_files_app()
assert result is None
class TestDirectoryTraversalProtection:
"""Tests for directory traversal attack prevention."""
@pytest.mark.asyncio
async def test_serve_react_assets_traversal_blocked(self):
"""
Test scenario: Attempt directory traversal attack
Expected: Return 403 error
"""
from api_utils.routers.static import serve_react_assets
mock_logger = MagicMock()
# First mock: file exists, second mock for resolve().relative_to() failure
with patch.object(Path, "exists", return_value=True):
# When the resolved path is outside assets dir, relative_to raises ValueError
with patch.object(Path, "resolve") as mock_resolve:
mock_resolved = MagicMock()
mock_resolved.relative_to.side_effect = ValueError(
"Not a relative path"
)
mock_resolve.return_value = mock_resolved
with pytest.raises(HTTPException) as exc_info:
await serve_react_assets("../../../etc/passwd", logger=mock_logger)
assert exc_info.value.status_code == 403
assert "Access denied" in exc_info.value.detail
mock_logger.warning.assert_called_once()
@pytest.mark.asyncio
async def test_serve_react_assets_additional_mime_types(self):
"""
Test scenario: Additional MIME type support
Expected: Correctly identify more file types
"""
from api_utils.routers.static import serve_react_assets
mock_logger = MagicMock()
test_cases = [
("image.svg", "image/svg+xml"),
("image.png", "image/png"),
("font.woff2", "font/woff2"),
]
for filename, expected_media_type in test_cases:
with patch.object(Path, "exists", return_value=True):
with patch.object(Path, "resolve") as mock_resolve:
mock_resolved = MagicMock()
mock_resolved.relative_to.return_value = Path(filename)
mock_resolve.return_value = mock_resolved
response = await serve_react_assets(filename, logger=mock_logger)
assert response is not None
assert response.media_type == expected_media_type
|