117 lines
3.9 KiB
Python
117 lines
3.9 KiB
Python
"""Pytest configuration and shared fixtures for all tests."""
|
|
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from src.server.services.auth_service import auth_service
|
|
|
|
|
|
def pytest_configure(config):
|
|
"""Register custom pytest marks."""
|
|
config.addinivalue_line(
|
|
"markers",
|
|
"performance: mark test as a performance test"
|
|
)
|
|
config.addinivalue_line(
|
|
"markers",
|
|
"security: mark test as a security test"
|
|
)
|
|
config.addinivalue_line(
|
|
"markers",
|
|
"requires_clean_auth: test requires auth to NOT be configured initially"
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_auth_and_rate_limits(request):
|
|
"""Reset authentication state and rate limits before each test.
|
|
|
|
This ensures:
|
|
1. Auth service state doesn't leak between tests
|
|
2. Rate limit window is reset for test client IP
|
|
3. Auth is configured with a default test password UNLESS the test
|
|
is marked with @pytest.mark.requires_clean_auth
|
|
Applied to all tests automatically via autouse=True.
|
|
"""
|
|
# Reset auth service state
|
|
auth_service._hash = None # noqa: SLF001
|
|
auth_service._failed.clear() # noqa: SLF001
|
|
|
|
# Check if test requires clean (unconfigured) auth state
|
|
requires_clean_auth = request.node.get_closest_marker("requires_clean_auth")
|
|
|
|
# Configure auth with a default test password so middleware allows requests
|
|
# This prevents the SetupRedirectMiddleware from blocking all test requests
|
|
# Skip this if the test explicitly needs clean auth state
|
|
if not requires_clean_auth:
|
|
try:
|
|
auth_service.setup_master_password("TestPass123!")
|
|
except Exception:
|
|
# If setup fails (e.g., already set), that's okay
|
|
pass
|
|
|
|
# Reset rate limiter - clear rate limit dict if middleware exists
|
|
# This prevents tests from hitting rate limits on auth endpoints
|
|
try:
|
|
from src.server.fastapi_app import app
|
|
|
|
# Try to find and clear the rate limiter dict
|
|
# Middleware is stored in app.middleware_stack or accessible
|
|
# through app's internal structure
|
|
if hasattr(app, 'middleware_stack'):
|
|
# Try to find AuthMiddleware in the stack
|
|
stack = app.middleware_stack
|
|
while stack is not None:
|
|
if hasattr(stack, 'cls'):
|
|
# This is a middleware class
|
|
pass
|
|
if hasattr(stack, 'app') and hasattr(
|
|
stack, '_rate'
|
|
): # noqa: SLF001
|
|
# Found a potential AuthMiddleware instance
|
|
stack._rate.clear() # noqa: SLF001
|
|
stack = getattr(stack, 'app', None)
|
|
except BaseException:
|
|
# If middleware reset fails, tests might hit rate limits
|
|
# but we continue anyway - they're not critical
|
|
pass
|
|
|
|
|
|
yield
|
|
|
|
# Clean up after test
|
|
auth_service._hash = None # noqa: SLF001
|
|
auth_service._failed.clear() # noqa: SLF001
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def mock_series_app_download(monkeypatch):
|
|
"""Mock SeriesApp loader download to prevent real downloads in tests.
|
|
|
|
This fixture automatically mocks all download operations to prevent
|
|
tests from performing real network downloads.
|
|
Applied to all tests automatically via autouse=True.
|
|
"""
|
|
# Mock the loader download method
|
|
try:
|
|
from src.core.SeriesApp import SeriesApp
|
|
|
|
# Patch the loader.download method for all SeriesApp instances
|
|
original_init = SeriesApp.__init__
|
|
|
|
def patched_init(self, *args, **kwargs):
|
|
original_init(self, *args, **kwargs)
|
|
# Mock the loader's download method
|
|
if hasattr(self, 'loader'):
|
|
self.loader.download = Mock(return_value=True)
|
|
|
|
monkeypatch.setattr(SeriesApp, '__init__', patched_init)
|
|
|
|
except ImportError:
|
|
# If imports fail, tests will continue but may perform downloads
|
|
pass
|
|
|
|
yield
|
|
|