refactor: modernize code quality tooling with Ruff

- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter)
- Add comprehensive pyproject.toml configuration
- Simplify Makefile code quality targets
- Configure exclusions for venv/.venv in pyproject.toml
- Auto-fix 1,359 linting issues across codebase

Benefits:
- Much faster builds (Ruff is written in Rust)
- Single tool replaces multiple tools
- More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q)
- All configuration centralized in pyproject.toml
- Better import sorting and formatting consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-28 19:37:38 +01:00
parent 21c13ca39b
commit 238c1ec9b8
169 changed files with 2183 additions and 1784 deletions

View File

@@ -12,17 +12,21 @@ Tests cover:
- Error handling and edge cases
"""
from datetime import datetime, timedelta, timezone
from unittest.mock import MagicMock, Mock, patch
from datetime import UTC, datetime, timedelta
from unittest.mock import Mock, patch
import pytest
from fastapi import HTTPException
from jose import jwt
from app.exceptions import (AdminRequiredException,
InsufficientPermissionsException,
InvalidCredentialsException, InvalidTokenException,
TokenExpiredException, UserNotActiveException)
from app.exceptions import (
AdminRequiredException,
InsufficientPermissionsException,
InvalidCredentialsException,
InvalidTokenException,
TokenExpiredException,
UserNotActiveException,
)
from middleware.auth import AuthManager
from models.database.user import User
@@ -316,7 +320,7 @@ class TestJWTTokenVerification:
# Create token without 'sub' field
payload = {
"username": "testuser",
"exp": datetime.now(timezone.utc) + timedelta(minutes=30),
"exp": datetime.now(UTC) + timedelta(minutes=30),
}
token = jwt.encode(
payload, auth_manager.secret_key, algorithm=auth_manager.algorithm
@@ -349,7 +353,7 @@ class TestJWTTokenVerification:
payload = {
"sub": "1",
"username": "testuser",
"exp": datetime.now(timezone.utc) + timedelta(minutes=30),
"exp": datetime.now(UTC) + timedelta(minutes=30),
}
# Create token with different algorithm
token = jwt.encode(payload, auth_manager.secret_key, algorithm="HS512")
@@ -362,7 +366,7 @@ class TestJWTTokenVerification:
auth_manager = AuthManager()
# Create a token with expiration in the past
past_time = datetime.now(timezone.utc) - timedelta(minutes=1)
past_time = datetime.now(UTC) - timedelta(minutes=1)
payload = {"sub": "1", "username": "testuser", "exp": past_time.timestamp()}
token = jwt.encode(
payload, auth_manager.secret_key, algorithm=auth_manager.algorithm
@@ -661,8 +665,8 @@ class TestEdgeCases:
payload = {
"sub": "1",
"username": "testuser",
"iat": datetime.now(timezone.utc) + timedelta(hours=1), # Future time
"exp": datetime.now(timezone.utc) + timedelta(hours=2),
"iat": datetime.now(UTC) + timedelta(hours=1), # Future time
"exp": datetime.now(UTC) + timedelta(hours=2),
}
token = jwt.encode(
payload, auth_manager.secret_key, algorithm=auth_manager.algorithm

View File

@@ -10,13 +10,17 @@ Tests cover:
- Edge cases and error handling
"""
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, Mock
import pytest
from fastapi import Request
from middleware.context import (ContextManager, ContextMiddleware,
RequestContext, get_request_context)
from middleware.context import (
ContextManager,
ContextMiddleware,
RequestContext,
get_request_context,
)
@pytest.mark.unit

View File

@@ -12,7 +12,6 @@ Tests cover:
- Edge cases and isolation
"""
from unittest.mock import Mock
import pytest

View File

@@ -134,8 +134,9 @@ class TestLoggingMiddleware:
call_next = AsyncMock(side_effect=Exception("Test error"))
with patch("middleware.logging.logger") as mock_logger, pytest.raises(
Exception
with (
patch("middleware.logging.logger") as mock_logger,
pytest.raises(Exception),
):
await middleware.dispatch(request, call_next)

View File

@@ -12,8 +12,7 @@ Tests cover:
"""
from collections import deque
from datetime import datetime, timedelta, timezone
from unittest.mock import Mock, patch
from datetime import UTC, datetime, timedelta
import pytest
@@ -53,7 +52,7 @@ class TestRateLimiterBasic:
# Make 10 requests (at the limit)
for i in range(max_requests):
result = limiter.allow_request(client_id, max_requests, 3600)
assert result is True, f"Request {i+1} should be allowed"
assert result is True, f"Request {i + 1} should be allowed"
assert len(limiter.clients[client_id]) == max_requests
@@ -109,7 +108,7 @@ class TestRateLimiterSlidingWindow:
window_seconds = 10
# Manually add old requests
old_time = datetime.now(timezone.utc) - timedelta(seconds=15)
old_time = datetime.now(UTC) - timedelta(seconds=15)
limiter.clients[client_id].append(old_time)
limiter.clients[client_id].append(old_time)
@@ -127,7 +126,7 @@ class TestRateLimiterSlidingWindow:
window_seconds = 60
# Add recent requests
recent_time = datetime.now(timezone.utc) - timedelta(seconds=30)
recent_time = datetime.now(UTC) - timedelta(seconds=30)
limiter.clients[client_id].append(recent_time)
limiter.clients[client_id].append(recent_time)
@@ -147,12 +146,12 @@ class TestRateLimiterSlidingWindow:
window_seconds = 30
# Add old requests (outside window)
old_time = datetime.now(timezone.utc) - timedelta(seconds=60)
old_time = datetime.now(UTC) - timedelta(seconds=60)
limiter.clients[client_id].append(old_time)
limiter.clients[client_id].append(old_time)
# Add recent request (within window)
recent_time = datetime.now(timezone.utc) - timedelta(seconds=10)
recent_time = datetime.now(UTC) - timedelta(seconds=10)
limiter.clients[client_id].append(recent_time)
# Old requests removed, only 1 recent request, so 2 more allowed
@@ -170,7 +169,7 @@ class TestRateLimiterSlidingWindow:
window_seconds = 1 # 1 second window
# Add old request
old_time = datetime.now(timezone.utc) - timedelta(seconds=2)
old_time = datetime.now(UTC) - timedelta(seconds=2)
limiter.clients[client_id].append(old_time)
# Should allow request because old one is outside 1-second window
@@ -188,12 +187,12 @@ class TestRateLimiterCleanup:
limiter = RateLimiter()
# Add clients with old requests
old_time = datetime.now(timezone.utc) - timedelta(hours=25)
old_time = datetime.now(UTC) - timedelta(hours=25)
limiter.clients["old_client_1"].append(old_time)
limiter.clients["old_client_2"].append(old_time)
# Add client with recent requests
recent_time = datetime.now(timezone.utc) - timedelta(hours=1)
recent_time = datetime.now(UTC) - timedelta(hours=1)
limiter.clients["recent_client"].append(recent_time)
# Run cleanup
@@ -215,7 +214,7 @@ class TestRateLimiterCleanup:
limiter.clients["empty_client_2"] = deque()
# Add client with requests
limiter.clients["active_client"].append(datetime.now(timezone.utc))
limiter.clients["active_client"].append(datetime.now(UTC))
# Run cleanup
limiter._cleanup_old_entries()
@@ -233,12 +232,12 @@ class TestRateLimiterCleanup:
client_id = "mixed_client"
# Add old requests
old_time = datetime.now(timezone.utc) - timedelta(hours=30)
old_time = datetime.now(UTC) - timedelta(hours=30)
limiter.clients[client_id].append(old_time)
limiter.clients[client_id].append(old_time)
# Add recent requests
recent_time = datetime.now(timezone.utc) - timedelta(hours=1)
recent_time = datetime.now(UTC) - timedelta(hours=1)
limiter.clients[client_id].append(recent_time)
limiter.clients[client_id].append(recent_time)
@@ -255,10 +254,10 @@ class TestRateLimiterCleanup:
limiter.cleanup_interval = 0 # Force immediate cleanup
# Set last_cleanup to past
limiter.last_cleanup = datetime.now(timezone.utc) - timedelta(hours=2)
limiter.last_cleanup = datetime.now(UTC) - timedelta(hours=2)
# Add old client
old_time = datetime.now(timezone.utc) - timedelta(hours=25)
old_time = datetime.now(UTC) - timedelta(hours=25)
limiter.clients["old_client"].append(old_time)
# Make request (should trigger cleanup)
@@ -272,7 +271,7 @@ class TestRateLimiterCleanup:
limiter = RateLimiter()
# Add multiple active clients
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
for i in range(5):
limiter.clients[f"client_{i}"].append(now - timedelta(hours=i))
@@ -305,7 +304,7 @@ class TestRateLimiterStatistics:
client_id = "active_client"
# Add requests at different times
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
limiter.clients[client_id].append(now - timedelta(minutes=30)) # Within hour
limiter.clients[client_id].append(now - timedelta(hours=2)) # Within day
limiter.clients[client_id].append(now - timedelta(hours=12)) # Within day
@@ -322,7 +321,7 @@ class TestRateLimiterStatistics:
client_id = "old_requests_client"
# Add very old requests
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
limiter.clients[client_id].append(now - timedelta(days=2))
limiter.clients[client_id].append(now - timedelta(days=3))
@@ -347,7 +346,7 @@ class TestRateLimiterStatistics:
limiter = RateLimiter()
client_id = "boundary_client"
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
# Exactly 1 hour ago (should be included)
limiter.clients[client_id].append(now - timedelta(hours=1, seconds=1))
@@ -485,7 +484,7 @@ class TestRateLimiterEdgeCases:
limiter = RateLimiter()
# Set last_cleanup to past to ensure cleanup triggers
old_cleanup_time = datetime.now(timezone.utc) - timedelta(hours=2)
old_cleanup_time = datetime.now(UTC) - timedelta(hours=2)
limiter.last_cleanup = old_cleanup_time
limiter.cleanup_interval = 0 # Force cleanup on next request
@@ -523,7 +522,7 @@ class TestRateLimiterMemoryManagement:
client_id = "efficiency_test"
# Add many old requests
old_time = datetime.now(timezone.utc) - timedelta(hours=2)
old_time = datetime.now(UTC) - timedelta(hours=2)
for _ in range(1000):
limiter.clients[client_id].append(old_time)

View File

@@ -16,9 +16,11 @@ from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from fastapi import Request
from middleware.theme_context import (ThemeContextManager,
ThemeContextMiddleware,
get_current_theme)
from middleware.theme_context import (
ThemeContextManager,
ThemeContextMiddleware,
get_current_theme,
)
@pytest.mark.unit
@@ -144,12 +146,12 @@ class TestThemeContextMiddleware:
mock_db = MagicMock()
mock_theme = {"theme_name": "test_theme"}
with patch(
"middleware.theme_context.get_db", return_value=iter([mock_db])
), patch.object(
ThemeContextManager, "get_vendor_theme", return_value=mock_theme
with (
patch("middleware.theme_context.get_db", return_value=iter([mock_db])),
patch.object(
ThemeContextManager, "get_vendor_theme", return_value=mock_theme
),
):
await middleware.dispatch(request, call_next)
assert request.state.theme == mock_theme
@@ -184,12 +186,14 @@ class TestThemeContextMiddleware:
mock_db = MagicMock()
with patch(
"middleware.theme_context.get_db", return_value=iter([mock_db])
), patch.object(
ThemeContextManager, "get_vendor_theme", side_effect=Exception("DB Error")
with (
patch("middleware.theme_context.get_db", return_value=iter([mock_db])),
patch.object(
ThemeContextManager,
"get_vendor_theme",
side_effect=Exception("DB Error"),
),
):
await middleware.dispatch(request, call_next)
# Should fallback to default theme

View File

@@ -17,10 +17,12 @@ import pytest
from fastapi import HTTPException, Request
from sqlalchemy.orm import Session
from middleware.vendor_context import (VendorContextManager,
VendorContextMiddleware,
get_current_vendor,
require_vendor_context)
from middleware.vendor_context import (
VendorContextManager,
VendorContextMiddleware,
get_current_vendor,
require_vendor_context,
)
@pytest.mark.unit
@@ -197,9 +199,7 @@ class TestVendorContextManager:
mock_vendor.is_active = True
mock_vendor_domain.vendor = mock_vendor
mock_db.query.return_value.filter.return_value.filter.return_value.filter.return_value.first.return_value = (
mock_vendor_domain
)
mock_db.query.return_value.filter.return_value.filter.return_value.filter.return_value.first.return_value = mock_vendor_domain
context = {"detection_method": "custom_domain", "domain": "customdomain1.com"}
@@ -216,9 +216,7 @@ class TestVendorContextManager:
mock_vendor.is_active = False
mock_vendor_domain.vendor = mock_vendor
mock_db.query.return_value.filter.return_value.filter.return_value.filter.return_value.first.return_value = (
mock_vendor_domain
)
mock_db.query.return_value.filter.return_value.filter.return_value.filter.return_value.first.return_value = mock_vendor_domain
context = {"detection_method": "custom_domain", "domain": "customdomain1.com"}
@@ -229,9 +227,7 @@ class TestVendorContextManager:
def test_get_vendor_from_custom_domain_not_found(self):
"""Test custom domain not found in database."""
mock_db = Mock(spec=Session)
mock_db.query.return_value.filter.return_value.filter.return_value.filter.return_value.first.return_value = (
None
)
mock_db.query.return_value.filter.return_value.filter.return_value.filter.return_value.first.return_value = None
context = {"detection_method": "custom_domain", "domain": "nonexistent.com"}
@@ -245,9 +241,7 @@ class TestVendorContextManager:
mock_vendor = Mock()
mock_vendor.is_active = True
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = (
mock_vendor
)
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = mock_vendor
context = {"detection_method": "subdomain", "subdomain": "vendor1"}
@@ -261,9 +255,7 @@ class TestVendorContextManager:
mock_vendor = Mock()
mock_vendor.is_active = True
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = (
mock_vendor
)
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = mock_vendor
context = {"detection_method": "path", "subdomain": "vendor1"}
@@ -285,9 +277,7 @@ class TestVendorContextManager:
mock_vendor = Mock()
mock_vendor.is_active = True
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = (
mock_vendor
)
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = mock_vendor
context = {"detection_method": "subdomain", "subdomain": "VENDOR1"} # Uppercase
@@ -533,16 +523,24 @@ class TestVendorContextMiddleware:
mock_db = MagicMock()
with patch.object(
VendorContextManager, "detect_vendor_context", return_value=vendor_context
), patch.object(
VendorContextManager, "get_vendor_from_context", return_value=mock_vendor
), patch.object(
VendorContextManager, "extract_clean_path", return_value="/shop/products"
), patch(
"middleware.vendor_context.get_db", return_value=iter([mock_db])
with (
patch.object(
VendorContextManager,
"detect_vendor_context",
return_value=vendor_context,
),
patch.object(
VendorContextManager,
"get_vendor_from_context",
return_value=mock_vendor,
),
patch.object(
VendorContextManager,
"extract_clean_path",
return_value="/shop/products",
),
patch("middleware.vendor_context.get_db", return_value=iter([mock_db])),
):
await middleware.dispatch(request, call_next)
assert request.state.vendor is mock_vendor
@@ -566,14 +564,17 @@ class TestVendorContextMiddleware:
mock_db = MagicMock()
with patch.object(
VendorContextManager, "detect_vendor_context", return_value=vendor_context
), patch.object(
VendorContextManager, "get_vendor_from_context", return_value=None
), patch(
"middleware.vendor_context.get_db", return_value=iter([mock_db])
with (
patch.object(
VendorContextManager,
"detect_vendor_context",
return_value=vendor_context,
),
patch.object(
VendorContextManager, "get_vendor_from_context", return_value=None
),
patch("middleware.vendor_context.get_db", return_value=iter([mock_db])),
):
await middleware.dispatch(request, call_next)
assert request.state.vendor is None