fix: ruff lint + format fixes in tests, ESLint fixes in frontend

This commit is contained in:
2026-07-17 21:28:58 +02:00
parent 341d0c6f38
commit fbb1b39b57
56 changed files with 1399 additions and 721 deletions
+64 -28
View File
@@ -1,7 +1,6 @@
"""Tests for file upload, list, download, delete, MIME validation, size limit, and thumbnail generation."""
import io
import os
import uuid
from pathlib import Path
from unittest.mock import patch
@@ -12,16 +11,14 @@ from httpx import ASGITransport, AsyncClient
from PIL import Image
from app.config import settings
from app.database import Base, get_db
from app.database import get_db
from app.main import app
from app.models.file import File
from app.models.user import User, UserRole
from app.models.vehicle import Vehicle
from app.services.auth_service import hash_password
# ---- Test fixtures ----
@pytest_asyncio.fixture
async def sample_vehicle_data():
"""Valid vehicle data for creation."""
@@ -104,6 +101,7 @@ async def _create_test_vehicle(db_session) -> uuid.UUID:
# ---- Tests: File Upload ----
class TestFileUpload:
"""POST /api/v1/vehicles/:id/files tests."""
@@ -172,7 +170,9 @@ class TestFileUpload:
assert data["thumbnail_path"] is None
@pytest.mark.asyncio
async def test_upload_invalid_mime_type_returns_422(self, admin_client, created_vehicle):
async def test_upload_invalid_mime_type_returns_422(
self, admin_client, created_vehicle
):
"""Upload a file with an unsupported MIME type and verify 422."""
vehicle_id = created_vehicle["id"]
files = {"file": ("malware.exe", b"MZ\x90\x00", "application/x-msdownload")}
@@ -196,7 +196,9 @@ class TestFileUpload:
assert response.status_code == 422, response.text
@pytest.mark.asyncio
async def test_upload_oversized_file_returns_413(self, admin_client, created_vehicle):
async def test_upload_oversized_file_returns_413(
self, admin_client, created_vehicle
):
"""Upload a file larger than 20MB and verify 413."""
vehicle_id = created_vehicle["id"]
# Create a 21MB file (21 * 1024 * 1024 bytes)
@@ -223,7 +225,9 @@ class TestFileUpload:
assert response.status_code == 404, response.text
@pytest.mark.asyncio
async def test_upload_without_auth_returns_401(self, test_session_factory, created_vehicle):
async def test_upload_without_auth_returns_401(
self, test_session_factory, created_vehicle
):
"""Upload without authentication and verify 401."""
vehicle_id = created_vehicle["id"]
image_bytes = _make_image_bytes()
@@ -243,7 +247,9 @@ class TestFileUpload:
app.dependency_overrides[get_db] = _override_get_db
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as unauth_client:
async with AsyncClient(
transport=transport, base_url="http://test"
) as unauth_client:
response = await unauth_client.post(
f"/api/v1/vehicles/{vehicle_id}/files",
files=files,
@@ -254,11 +260,14 @@ class TestFileUpload:
# ---- Tests: File List ----
class TestFileList:
"""GET /api/v1/vehicles/:id/files tests."""
@pytest.mark.asyncio
async def test_list_files_returns_200_with_pagination(self, admin_client, created_vehicle):
async def test_list_files_returns_200_with_pagination(
self, admin_client, created_vehicle
):
"""List files for a vehicle returns 200 with paginated response."""
vehicle_id = created_vehicle["id"]
# Upload a file first
@@ -284,9 +293,7 @@ class TestFileList:
async def test_list_files_empty_returns_200(self, admin_client, created_vehicle):
"""List files for a vehicle with no files returns 200 with empty list."""
vehicle_id = created_vehicle["id"]
response = await admin_client.get(
f"/api/v1/vehicles/{vehicle_id}/files"
)
response = await admin_client.get(f"/api/v1/vehicles/{vehicle_id}/files")
assert response.status_code == 200, response.text
data = response.json()
assert data["total"] == 0
@@ -302,6 +309,7 @@ class TestFileList:
# ---- Tests: File Download ----
class TestFileDownload:
"""GET /api/v1/vehicles/:id/files/:fileId tests."""
@@ -325,7 +333,9 @@ class TestFileDownload:
assert len(response.content) == len(image_bytes)
@pytest.mark.asyncio
async def test_download_nonexistent_file_returns_404(self, admin_client, created_vehicle):
async def test_download_nonexistent_file_returns_404(
self, admin_client, created_vehicle
):
"""Download a non-existent file returns 404."""
vehicle_id = created_vehicle["id"]
fake_file_id = str(uuid.uuid4())
@@ -337,6 +347,7 @@ class TestFileDownload:
# ---- Tests: File Delete ----
class TestFileDelete:
"""DELETE /api/v1/vehicles/:id/files/:fileId tests."""
@@ -361,14 +372,14 @@ class TestFileDelete:
assert data["id"] == file_id
# Verify file is gone from list
list_resp = await admin_client.get(
f"/api/v1/vehicles/{vehicle_id}/files"
)
list_resp = await admin_client.get(f"/api/v1/vehicles/{vehicle_id}/files")
assert list_resp.status_code == 200
assert list_resp.json()["total"] == 0
@pytest.mark.asyncio
async def test_delete_nonexistent_file_returns_404(self, admin_client, created_vehicle):
async def test_delete_nonexistent_file_returns_404(
self, admin_client, created_vehicle
):
"""Delete a non-existent file returns 404."""
vehicle_id = created_vehicle["id"]
fake_file_id = str(uuid.uuid4())
@@ -380,77 +391,96 @@ class TestFileDelete:
# ---- Tests: MIME Type Validation ----
class TestMIMEValidation:
"""Unit tests for MIME type validation."""
def test_validate_jpeg_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("image/jpeg", "photo.jpg") is True
assert validate_mime_type("image/jpeg", "photo.jpeg") is True
def test_validate_png_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("image/png", "photo.png") is True
def test_validate_webp_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("image/webp", "photo.webp") is True
def test_validate_pdf_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("application/pdf", "doc.pdf") is True
def test_validate_doc_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("application/msword", "doc.doc") is True
def test_validate_docx_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"doc.docx",
) is True
assert (
validate_mime_type(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"doc.docx",
)
is True
)
def test_reject_exe_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("application/x-msdownload", "malware.exe") is False
def test_reject_text_mime_type(self):
from app.services.file_service import validate_mime_type
assert validate_mime_type("text/plain", "notes.txt") is False
def test_reject_mismatched_extension(self):
"""MIME type image/jpeg with .png extension should fail."""
from app.services.file_service import validate_mime_type
assert validate_mime_type("image/jpeg", "photo.png") is False
# ---- Tests: File Size Validation ----
class TestFileSizeValidation:
"""Unit tests for file size validation."""
def test_validate_small_file_size(self):
from app.services.file_service import validate_file_size
assert validate_file_size(1024, max_size_mb=20) is True
def test_validate_exact_20mb_file_size(self):
from app.services.file_service import validate_file_size
exact_20mb = 20 * 1024 * 1024
assert validate_file_size(exact_20mb, max_size_mb=20) is True
def test_reject_oversized_file(self):
from app.services.file_service import validate_file_size
over_20mb = 20 * 1024 * 1024 + 1
assert validate_file_size(over_20mb, max_size_mb=20) is False
def test_validate_zero_byte_file(self):
from app.services.file_service import validate_file_size
assert validate_file_size(0, max_size_mb=20) is True
# ---- Tests: Thumbnail Generation ----
class TestThumbnailGeneration:
"""Tests for thumbnail generation utility."""
@@ -534,6 +564,7 @@ class TestThumbnailGeneration:
def test_is_image_mime_type(self):
"""Test is_image_mime_type helper."""
from app.utils.thumbnails import is_image_mime_type
assert is_image_mime_type("image/jpeg") is True
assert is_image_mime_type("image/png") is True
assert is_image_mime_type("image/webp") is True
@@ -543,6 +574,7 @@ class TestThumbnailGeneration:
# ---- Tests: File Service Unit Tests ----
class TestFileServiceUnit:
"""Unit tests for file service functions."""
@@ -622,7 +654,9 @@ class TestFileServiceUnit:
file_path = Path(file_record.file_path)
assert file_path.exists()
deleted = await file_service.delete_file(db_session, vehicle_id, file_record.id)
deleted = await file_service.delete_file(
db_session, vehicle_id, file_record.id
)
assert deleted is not None
assert not file_path.exists()
@@ -631,9 +665,7 @@ class TestFileServiceUnit:
"""Test that delete_file returns None for non-existent file."""
from app.services import file_service
result = await file_service.delete_file(
db_session, uuid.uuid4(), uuid.uuid4()
)
result = await file_service.delete_file(db_session, uuid.uuid4(), uuid.uuid4())
assert result is None
@pytest.mark.asyncio
@@ -682,7 +714,9 @@ class TestFileServiceUnit:
mime_type="image/jpeg",
)
retrieved = await file_service.get_file(db_session, vehicle_id, file_record.id)
retrieved = await file_service.get_file(
db_session, vehicle_id, file_record.id
)
assert retrieved is not None
assert retrieved.id == file_record.id
assert retrieved.original_filename == "test.jpg"
@@ -704,5 +738,7 @@ class TestFileServiceUnit:
)
wrong_vehicle_id = uuid.uuid4()
retrieved = await file_service.get_file(db_session, wrong_vehicle_id, file_record.id)
retrieved = await file_service.get_file(
db_session, wrong_vehicle_id, file_record.id
)
assert retrieved is None