refactor: centralize frontend detection with FrontendDetector
Major architecture change to unify frontend detection: ## Problem Solved - Eliminated code duplication across 3 middleware files - Fixed incomplete path detection (now detects /api/v1/admin/*) - Unified on FrontendType enum (deprecates RequestContext) - Added request.state.frontend_type for all requests ## New Components - app/core/frontend_detector.py: Centralized FrontendDetector class - middleware/frontend_type.py: FrontendTypeMiddleware (replaces ContextMiddleware) - docs/architecture/frontend-detection.md: Complete architecture documentation ## Changes - main.py: Use FrontendTypeMiddleware instead of ContextMiddleware - middleware/context.py: Deprecated (kept for backwards compatibility) - middleware/platform_context.py: Use FrontendDetector.is_admin() - middleware/vendor_context.py: Use FrontendDetector.is_admin() - middleware/language.py: Use FrontendType instead of context_value - app/exceptions/handler.py: Use FrontendType.STOREFRONT - app/exceptions/error_renderer.py: Use FrontendType - Customer routes: Cookie path changed from /shop to /storefront ## Documentation - docs/architecture/frontend-detection.md: New comprehensive docs - docs/architecture/middleware.md: Updated for new system - docs/architecture/request-flow.md: Updated for FrontendType - docs/backend/middleware-reference.md: Updated API reference ## Tests - tests/unit/core/test_frontend_detector.py: 37 new tests - tests/unit/middleware/test_frontend_type.py: 11 new tests - tests/unit/middleware/test_context.py: Updated for compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
256
tests/unit/core/test_frontend_detector.py
Normal file
256
tests/unit/core/test_frontend_detector.py
Normal file
@@ -0,0 +1,256 @@
|
||||
# tests/unit/core/test_frontend_detector.py
|
||||
"""
|
||||
Unit tests for FrontendDetector.
|
||||
|
||||
Tests cover:
|
||||
- Detection for all frontend types (ADMIN, VENDOR, STOREFRONT, PLATFORM)
|
||||
- Path-based detection (dev mode)
|
||||
- Subdomain-based detection (prod mode)
|
||||
- Custom domain detection
|
||||
- Legacy /shop/ path support
|
||||
- Priority order of detection methods
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.core.frontend_detector import FrontendDetector, get_frontend_type
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendDetectorAdmin:
|
||||
"""Test suite for admin frontend detection."""
|
||||
|
||||
def test_detect_admin_from_subdomain(self):
|
||||
"""Test admin detection from admin subdomain."""
|
||||
result = FrontendDetector.detect(host="admin.oms.lu", path="/dashboard")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_detect_admin_from_subdomain_with_port(self):
|
||||
"""Test admin detection from admin subdomain with port."""
|
||||
result = FrontendDetector.detect(host="admin.localhost:8000", path="/dashboard")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_detect_admin_from_path(self):
|
||||
"""Test admin detection from /admin path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/admin/vendors")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_detect_admin_from_api_path(self):
|
||||
"""Test admin detection from /api/v1/admin path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/api/v1/admin/users")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_detect_admin_nested_path(self):
|
||||
"""Test admin detection with nested admin path."""
|
||||
result = FrontendDetector.detect(host="oms.lu", path="/admin/vendors/123/products")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendDetectorVendor:
|
||||
"""Test suite for vendor dashboard frontend detection."""
|
||||
|
||||
def test_detect_vendor_from_path(self):
|
||||
"""Test vendor detection from /vendor/ path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/vendor/settings")
|
||||
assert result == FrontendType.VENDOR
|
||||
|
||||
def test_detect_vendor_from_api_path(self):
|
||||
"""Test vendor detection from /api/v1/vendor path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/api/v1/vendor/products")
|
||||
assert result == FrontendType.VENDOR
|
||||
|
||||
def test_detect_vendor_nested_path(self):
|
||||
"""Test vendor detection with nested vendor path."""
|
||||
result = FrontendDetector.detect(host="oms.lu", path="/vendor/dashboard/analytics")
|
||||
assert result == FrontendType.VENDOR
|
||||
|
||||
def test_vendors_plural_not_vendor_dashboard(self):
|
||||
"""Test that /vendors/ path is NOT vendor dashboard (it's storefront)."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/vendors/wizamart/storefront")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendDetectorStorefront:
|
||||
"""Test suite for storefront frontend detection."""
|
||||
|
||||
def test_detect_storefront_from_path(self):
|
||||
"""Test storefront detection from /storefront path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/storefront/products")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_detect_storefront_from_api_path(self):
|
||||
"""Test storefront detection from /api/v1/storefront path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/api/v1/storefront/cart")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_detect_storefront_from_vendors_path(self):
|
||||
"""Test storefront detection from /vendors/ path (path-based vendor access)."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/vendors/wizamart/products")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_detect_storefront_from_vendor_subdomain(self):
|
||||
"""Test storefront detection from vendor subdomain."""
|
||||
result = FrontendDetector.detect(host="wizamart.oms.lu", path="/products")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_detect_storefront_from_vendor_context(self):
|
||||
"""Test storefront detection when vendor context is set."""
|
||||
result = FrontendDetector.detect(
|
||||
host="mybakery.lu", path="/about", has_vendor_context=True
|
||||
)
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_detect_storefront_legacy_shop_path(self):
|
||||
"""Test storefront detection from legacy /shop path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/shop/products")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_detect_storefront_legacy_shop_api_path(self):
|
||||
"""Test storefront detection from legacy /api/v1/shop path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/api/v1/shop/cart")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendDetectorPlatform:
|
||||
"""Test suite for platform marketing frontend detection."""
|
||||
|
||||
def test_detect_platform_from_root(self):
|
||||
"""Test platform detection from root path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
def test_detect_platform_from_marketing_page(self):
|
||||
"""Test platform detection from marketing page."""
|
||||
result = FrontendDetector.detect(host="oms.lu", path="/pricing")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
def test_detect_platform_from_about(self):
|
||||
"""Test platform detection from about page."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/about")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
def test_detect_platform_from_api_path(self):
|
||||
"""Test platform detection from /api/v1/platform path."""
|
||||
result = FrontendDetector.detect(host="localhost", path="/api/v1/platform/config")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendDetectorPriority:
|
||||
"""Test suite for detection priority order."""
|
||||
|
||||
def test_admin_subdomain_priority_over_path(self):
|
||||
"""Test that admin subdomain takes priority."""
|
||||
result = FrontendDetector.detect(host="admin.oms.lu", path="/storefront/products")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_admin_path_priority_over_vendor_context(self):
|
||||
"""Test that admin path takes priority over vendor context."""
|
||||
result = FrontendDetector.detect(
|
||||
host="localhost", path="/admin/dashboard", has_vendor_context=True
|
||||
)
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_path_priority_over_subdomain(self):
|
||||
"""Test that explicit path takes priority for vendor/storefront."""
|
||||
# /vendor/ path on a vendor subdomain -> VENDOR (path wins)
|
||||
result = FrontendDetector.detect(host="wizamart.oms.lu", path="/vendor/settings")
|
||||
assert result == FrontendType.VENDOR
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendDetectorHelpers:
|
||||
"""Test suite for helper methods."""
|
||||
|
||||
def test_strip_port(self):
|
||||
"""Test port stripping from host."""
|
||||
assert FrontendDetector._strip_port("localhost:8000") == "localhost"
|
||||
assert FrontendDetector._strip_port("oms.lu") == "oms.lu"
|
||||
assert FrontendDetector._strip_port("admin.localhost:9999") == "admin.localhost"
|
||||
|
||||
def test_get_subdomain(self):
|
||||
"""Test subdomain extraction."""
|
||||
assert FrontendDetector._get_subdomain("wizamart.oms.lu") == "wizamart"
|
||||
assert FrontendDetector._get_subdomain("admin.oms.lu") == "admin"
|
||||
assert FrontendDetector._get_subdomain("oms.lu") is None
|
||||
assert FrontendDetector._get_subdomain("localhost") is None
|
||||
assert FrontendDetector._get_subdomain("127.0.0.1") is None
|
||||
|
||||
def test_is_admin(self):
|
||||
"""Test is_admin convenience method."""
|
||||
assert FrontendDetector.is_admin("admin.oms.lu", "/dashboard") is True
|
||||
assert FrontendDetector.is_admin("localhost", "/admin/vendors") is True
|
||||
assert FrontendDetector.is_admin("localhost", "/vendor/settings") is False
|
||||
|
||||
def test_is_vendor(self):
|
||||
"""Test is_vendor convenience method."""
|
||||
assert FrontendDetector.is_vendor("localhost", "/vendor/settings") is True
|
||||
assert FrontendDetector.is_vendor("localhost", "/api/v1/vendor/products") is True
|
||||
assert FrontendDetector.is_vendor("localhost", "/admin/dashboard") is False
|
||||
|
||||
def test_is_storefront(self):
|
||||
"""Test is_storefront convenience method."""
|
||||
assert FrontendDetector.is_storefront("localhost", "/storefront/products") is True
|
||||
assert FrontendDetector.is_storefront("wizamart.oms.lu", "/products") is True
|
||||
assert FrontendDetector.is_storefront("localhost", "/admin/dashboard") is False
|
||||
|
||||
def test_is_platform(self):
|
||||
"""Test is_platform convenience method."""
|
||||
assert FrontendDetector.is_platform("localhost", "/") is True
|
||||
assert FrontendDetector.is_platform("oms.lu", "/pricing") is True
|
||||
assert FrontendDetector.is_platform("localhost", "/admin/dashboard") is False
|
||||
|
||||
def test_is_api_request(self):
|
||||
"""Test is_api_request convenience method."""
|
||||
assert FrontendDetector.is_api_request("/api/v1/vendors") is True
|
||||
assert FrontendDetector.is_api_request("/api/v1/admin/users") is True
|
||||
assert FrontendDetector.is_api_request("/admin/dashboard") is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetFrontendTypeFunction:
|
||||
"""Test suite for get_frontend_type convenience function."""
|
||||
|
||||
def test_get_frontend_type_admin(self):
|
||||
"""Test get_frontend_type returns admin."""
|
||||
result = get_frontend_type("localhost", "/admin/dashboard")
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_get_frontend_type_vendor(self):
|
||||
"""Test get_frontend_type returns vendor."""
|
||||
result = get_frontend_type("localhost", "/vendor/settings")
|
||||
assert result == FrontendType.VENDOR
|
||||
|
||||
def test_get_frontend_type_storefront(self):
|
||||
"""Test get_frontend_type returns storefront."""
|
||||
result = get_frontend_type("localhost", "/storefront/products")
|
||||
assert result == FrontendType.STOREFRONT
|
||||
|
||||
def test_get_frontend_type_platform(self):
|
||||
"""Test get_frontend_type returns platform."""
|
||||
result = get_frontend_type("localhost", "/pricing")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestReservedSubdomains:
|
||||
"""Test suite for reserved subdomain handling."""
|
||||
|
||||
def test_www_subdomain_not_storefront(self):
|
||||
"""Test that www subdomain is not treated as vendor storefront."""
|
||||
result = FrontendDetector.detect(host="www.oms.lu", path="/")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
def test_api_subdomain_not_storefront(self):
|
||||
"""Test that api subdomain is not treated as vendor storefront."""
|
||||
result = FrontendDetector.detect(host="api.oms.lu", path="/v1/products")
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
def test_portal_subdomain_not_storefront(self):
|
||||
"""Test that portal subdomain is not treated as vendor storefront."""
|
||||
result = FrontendDetector.detect(host="portal.oms.lu", path="/")
|
||||
assert result == FrontendType.PLATFORM
|
||||
@@ -1,31 +1,31 @@
|
||||
# tests/unit/middleware/test_context.py
|
||||
"""
|
||||
Comprehensive unit tests for ContextMiddleware and ContextManager.
|
||||
DEPRECATED: Tests for backward compatibility of middleware.context module.
|
||||
|
||||
Tests cover:
|
||||
- Context detection for API, Admin, Vendor Dashboard, Shop, and Fallback
|
||||
- Clean path usage for correct context detection
|
||||
- Host and path-based context determination
|
||||
- Middleware state injection
|
||||
- Edge cases and error handling
|
||||
The ContextMiddleware and ContextManager classes have been replaced by:
|
||||
- FrontendTypeMiddleware (middleware/frontend_type.py)
|
||||
- FrontendDetector (app/core/frontend_detector.py)
|
||||
|
||||
These tests verify the backward compatibility layer still works for code
|
||||
that uses the deprecated RequestContext enum and get_request_context() function.
|
||||
|
||||
For new tests, see:
|
||||
- tests/unit/core/test_frontend_detector.py
|
||||
- tests/unit/middleware/test_frontend_type.py
|
||||
"""
|
||||
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
import warnings
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from fastapi import Request
|
||||
|
||||
from middleware.context import (
|
||||
ContextManager,
|
||||
ContextMiddleware,
|
||||
RequestContext,
|
||||
get_request_context,
|
||||
)
|
||||
from middleware.context import RequestContext, get_request_context
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestRequestContextEnum:
|
||||
"""Test suite for RequestContext enum."""
|
||||
class TestRequestContextEnumBackwardCompatibility:
|
||||
"""Test suite for deprecated RequestContext enum."""
|
||||
|
||||
def test_request_context_values(self):
|
||||
"""Test RequestContext enum has correct values."""
|
||||
@@ -42,554 +42,90 @@ class TestRequestContextEnum:
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestContextManagerDetection:
|
||||
"""Test suite for ContextManager.detect_context()."""
|
||||
class TestGetRequestContextBackwardCompatibility:
|
||||
"""Test suite for deprecated get_request_context() function."""
|
||||
|
||||
# ========================================================================
|
||||
# API Context Tests (Highest Priority)
|
||||
# ========================================================================
|
||||
|
||||
def test_detect_api_context(self):
|
||||
"""Test API context detection."""
|
||||
def test_get_request_context_returns_api_for_api_paths(self):
|
||||
"""Test get_request_context returns API for /api/ paths."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/v1/vendors")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/api/v1/vendors")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_detect_api_context_nested_path(self):
|
||||
"""Test API context detection with nested path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/v1/vendors/123/products")
|
||||
request.headers = {"host": "platform.com"}
|
||||
request.state = Mock(clean_path="/api/v1/vendors/123/products")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_detect_api_context_with_clean_path(self):
|
||||
"""Test API context detection uses clean_path when available."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/testvendor/api/products")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/api/products")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.API
|
||||
|
||||
# ========================================================================
|
||||
# Admin Context Tests
|
||||
# ========================================================================
|
||||
|
||||
def test_detect_admin_context_from_subdomain(self):
|
||||
"""Test admin context detection from subdomain."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/dashboard")
|
||||
request.headers = {"host": "admin.platform.com"}
|
||||
request.state = Mock(clean_path="/dashboard")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
def test_detect_admin_context_from_path(self):
|
||||
"""Test admin context detection from path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.headers = {"host": "platform.com"}
|
||||
request.state = Mock(clean_path="/admin/dashboard")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
def test_detect_admin_context_with_port(self):
|
||||
"""Test admin context detection with port number."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/dashboard")
|
||||
request.headers = {"host": "admin.localhost:8000"}
|
||||
request.state = Mock(clean_path="/dashboard")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
def test_detect_admin_context_nested_path(self):
|
||||
"""Test admin context detection with nested admin path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/vendors/list")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/admin/vendors/list")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
# ========================================================================
|
||||
# Vendor Dashboard Context Tests
|
||||
# ========================================================================
|
||||
|
||||
def test_detect_vendor_dashboard_context(self):
|
||||
"""Test vendor dashboard context detection."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/testvendor/dashboard")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/dashboard")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
def test_detect_vendor_dashboard_context_direct_path(self):
|
||||
"""Test vendor dashboard with direct /vendor/ path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/settings")
|
||||
request.headers = {"host": "testvendor.platform.com"}
|
||||
request.state = Mock(clean_path="/vendor/settings")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
def test_not_detect_vendors_plural_as_dashboard(self):
|
||||
"""Test that /vendors/ path is not detected as vendor dashboard."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendors/testvendor/shop")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/shop")
|
||||
|
||||
# Should not be vendor dashboard
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context != RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
# ========================================================================
|
||||
# Shop Context Tests
|
||||
# ========================================================================
|
||||
|
||||
def test_detect_shop_context_with_vendor_state(self):
|
||||
"""Test shop context detection when vendor exists in request state."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/products")
|
||||
request.headers = {"host": "testvendor.platform.com"}
|
||||
mock_vendor = Mock()
|
||||
mock_vendor.name = "Test Vendor"
|
||||
request.state = Mock(clean_path="/products", vendor=mock_vendor)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.SHOP
|
||||
|
||||
def test_detect_shop_context_from_shop_path(self):
|
||||
"""Test shop context detection from /shop/ path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/shop/products")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/shop/products", vendor=None)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.SHOP
|
||||
|
||||
def test_detect_shop_context_custom_domain(self):
|
||||
"""Test shop context with custom domain and vendor."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/products")
|
||||
request.headers = {"host": "customdomain.com"}
|
||||
mock_vendor = Mock(name="Custom Vendor")
|
||||
request.state = Mock(clean_path="/products", vendor=mock_vendor)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.SHOP
|
||||
|
||||
# ========================================================================
|
||||
# Fallback Context Tests
|
||||
# ========================================================================
|
||||
|
||||
def test_detect_fallback_context(self):
|
||||
"""Test fallback context for unknown paths."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/random/path")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/random/path", vendor=None)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.FALLBACK
|
||||
|
||||
def test_detect_fallback_context_root(self):
|
||||
"""Test fallback context for root path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/")
|
||||
request.headers = {"host": "platform.com"}
|
||||
request.state = Mock(clean_path="/", vendor=None)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.FALLBACK
|
||||
|
||||
def test_detect_fallback_context_no_vendor(self):
|
||||
"""Test fallback context when no vendor context exists."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/about")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/about", vendor=None)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.FALLBACK
|
||||
|
||||
# ========================================================================
|
||||
# Clean Path Tests
|
||||
# ========================================================================
|
||||
|
||||
def test_uses_clean_path_when_available(self):
|
||||
"""Test that clean_path is used over original path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/testvendor/api/products")
|
||||
request.headers = {"host": "localhost"}
|
||||
# clean_path shows the rewritten path
|
||||
request.state = Mock(clean_path="/api/products")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
# Should detect as API based on clean_path, not original path
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_falls_back_to_original_path(self):
|
||||
"""Test falls back to original path when clean_path not set."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/vendors")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(spec=[]) # No clean_path attribute
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.API
|
||||
|
||||
# ========================================================================
|
||||
# Priority Order Tests
|
||||
# ========================================================================
|
||||
|
||||
def test_api_has_highest_priority(self):
|
||||
"""Test API context takes precedence over admin."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/admin/users")
|
||||
request.headers = {"host": "admin.platform.com"}
|
||||
request.state = Mock(clean_path="/api/admin/users")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
# API should win even though it's admin subdomain
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_admin_has_priority_over_shop(self):
|
||||
"""Test admin context takes precedence over shop."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/shops")
|
||||
request.headers = {"host": "localhost"}
|
||||
mock_vendor = Mock()
|
||||
request.state = Mock(clean_path="/admin/shops", vendor=mock_vendor)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
# Admin should win even though vendor exists
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
def test_vendor_dashboard_priority_over_shop(self):
|
||||
"""Test vendor dashboard takes precedence over shop."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/settings")
|
||||
request.headers = {"host": "testvendor.platform.com"}
|
||||
mock_vendor = Mock()
|
||||
request.state = Mock(clean_path="/vendor/settings", vendor=mock_vendor)
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestContextManagerHelpers:
|
||||
"""Test suite for ContextManager helper methods."""
|
||||
|
||||
def test_is_admin_context_from_subdomain(self):
|
||||
"""Test _is_admin_context with admin subdomain."""
|
||||
request = Mock()
|
||||
assert (
|
||||
ContextManager._is_admin_context(
|
||||
request, "admin.platform.com", "/dashboard"
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
def test_is_admin_context_from_path(self):
|
||||
"""Test _is_admin_context with admin path."""
|
||||
request = Mock()
|
||||
assert (
|
||||
ContextManager._is_admin_context(request, "localhost", "/admin/users")
|
||||
is True
|
||||
)
|
||||
|
||||
def test_is_admin_context_both(self):
|
||||
"""Test _is_admin_context with both subdomain and path."""
|
||||
request = Mock()
|
||||
assert (
|
||||
ContextManager._is_admin_context(
|
||||
request, "admin.platform.com", "/admin/users"
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
def test_is_not_admin_context(self):
|
||||
"""Test _is_admin_context returns False for non-admin."""
|
||||
request = Mock()
|
||||
assert (
|
||||
ContextManager._is_admin_context(request, "vendor.platform.com", "/shop")
|
||||
is False
|
||||
)
|
||||
|
||||
def test_is_vendor_dashboard_context(self):
|
||||
"""Test _is_vendor_dashboard_context with /vendor/ path."""
|
||||
assert ContextManager._is_vendor_dashboard_context("/vendor/settings") is True
|
||||
|
||||
def test_is_vendor_dashboard_context_nested(self):
|
||||
"""Test _is_vendor_dashboard_context with nested vendor path."""
|
||||
assert (
|
||||
ContextManager._is_vendor_dashboard_context("/vendor/products/list") is True
|
||||
)
|
||||
|
||||
def test_is_not_vendor_dashboard_context_vendors_plural(self):
|
||||
"""Test _is_vendor_dashboard_context excludes /vendors/ path."""
|
||||
assert (
|
||||
ContextManager._is_vendor_dashboard_context("/vendors/shop123/products")
|
||||
is False
|
||||
)
|
||||
|
||||
def test_is_not_vendor_dashboard_context(self):
|
||||
"""Test _is_vendor_dashboard_context returns False for non-vendor paths."""
|
||||
assert ContextManager._is_vendor_dashboard_context("/shop/products") is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestContextMiddleware:
|
||||
"""Test suite for ContextMiddleware."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_context(self):
|
||||
"""Test middleware successfully sets context in request state."""
|
||||
middleware = ContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/vendors")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/api/vendors", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert hasattr(request.state, "context_type")
|
||||
assert request.state.context_type == RequestContext.API
|
||||
call_next.assert_called_once_with(request)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_admin_context(self):
|
||||
"""Test middleware sets admin context."""
|
||||
middleware = ContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/admin/dashboard")
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.context_type == RequestContext.ADMIN
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_vendor_dashboard_context(self):
|
||||
"""Test middleware sets vendor dashboard context."""
|
||||
middleware = ContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/settings")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/vendor/settings")
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.context_type == RequestContext.VENDOR_DASHBOARD
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_shop_context(self):
|
||||
"""Test middleware sets shop context."""
|
||||
middleware = ContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/products")
|
||||
request.headers = {"host": "shop.platform.com"}
|
||||
mock_vendor = Mock()
|
||||
request.state = Mock(clean_path="/products", vendor=mock_vendor)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.context_type == RequestContext.SHOP
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_fallback_context(self):
|
||||
"""Test middleware sets fallback context."""
|
||||
middleware = ContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/random")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/random", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.context_type == RequestContext.FALLBACK
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_returns_response(self):
|
||||
"""Test middleware returns response from call_next."""
|
||||
middleware = ContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/test")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/api/test")
|
||||
|
||||
expected_response = Mock()
|
||||
call_next = AsyncMock(return_value=expected_response)
|
||||
|
||||
response = await middleware.dispatch(request, call_next)
|
||||
|
||||
assert response is expected_response
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetRequestContextHelper:
|
||||
"""Test suite for get_request_context helper function."""
|
||||
|
||||
def test_get_request_context_exists(self):
|
||||
"""Test getting request context when it exists."""
|
||||
request = Mock(spec=Request)
|
||||
request.state.context_type = RequestContext.API
|
||||
|
||||
context = get_request_context(request)
|
||||
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_get_request_context_default(self):
|
||||
"""Test getting request context returns FALLBACK as default."""
|
||||
request = Mock(spec=Request)
|
||||
request.state = Mock(spec=[]) # No context_type attribute
|
||||
|
||||
context = get_request_context(request)
|
||||
|
||||
assert context == RequestContext.FALLBACK
|
||||
|
||||
def test_get_request_context_for_all_types(self):
|
||||
"""Test getting all context types."""
|
||||
for expected_context in RequestContext:
|
||||
request = Mock(spec=Request)
|
||||
request.state.context_type = expected_context
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = None
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
context = get_request_context(request)
|
||||
|
||||
assert context == expected_context
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_get_request_context_deprecation_warning(self):
|
||||
"""Test get_request_context raises DeprecationWarning."""
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestEdgeCases:
|
||||
"""Test suite for edge cases and error scenarios."""
|
||||
|
||||
def test_detect_context_empty_path(self):
|
||||
"""Test context detection with empty path."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="", vendor=None)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = FrontendType.ADMIN
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
with pytest.warns(DeprecationWarning, match="get_request_context.*deprecated"):
|
||||
get_request_context(request)
|
||||
|
||||
assert context == RequestContext.FALLBACK
|
||||
def test_get_request_context_maps_admin(self):
|
||||
"""Test get_request_context maps FrontendType.ADMIN to RequestContext.ADMIN."""
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
def test_detect_context_missing_host(self):
|
||||
"""Test context detection with missing host header."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/shop/products")
|
||||
request.headers = {}
|
||||
request.state = Mock(clean_path="/shop/products", vendor=None)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = FrontendType.ADMIN
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
context = get_request_context(request)
|
||||
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
def test_get_request_context_maps_vendor(self):
|
||||
"""Test get_request_context maps FrontendType.VENDOR to RequestContext.VENDOR_DASHBOARD."""
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/settings")
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = FrontendType.VENDOR
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
context = get_request_context(request)
|
||||
|
||||
assert context == RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
def test_get_request_context_maps_storefront(self):
|
||||
"""Test get_request_context maps FrontendType.STOREFRONT to RequestContext.SHOP."""
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/storefront/products")
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = FrontendType.STOREFRONT
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
context = get_request_context(request)
|
||||
|
||||
assert context == RequestContext.SHOP
|
||||
|
||||
def test_detect_context_case_sensitivity(self):
|
||||
"""Test that context detection is case-sensitive for paths."""
|
||||
def test_get_request_context_maps_platform_to_fallback(self):
|
||||
"""Test get_request_context maps FrontendType.PLATFORM to RequestContext.FALLBACK."""
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/API/vendors") # Uppercase
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/API/vendors")
|
||||
request.url = Mock(path="/pricing")
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = FrontendType.PLATFORM
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
context = get_request_context(request)
|
||||
|
||||
# Should NOT match /api/ because it's case-sensitive
|
||||
assert context != RequestContext.API
|
||||
|
||||
def test_detect_context_path_with_query_params(self):
|
||||
"""Test context detection handles path with query parameters."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/vendors?page=1")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/api/vendors?page=1")
|
||||
|
||||
# path.startswith should still work
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
assert context == RequestContext.API
|
||||
|
||||
def test_detect_context_admin_substring(self):
|
||||
"""Test that 'admin' substring doesn't trigger false positive."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/administration/docs")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/administration/docs")
|
||||
|
||||
context = ContextManager.detect_context(request)
|
||||
|
||||
# Should match because path starts with /admin
|
||||
assert context == RequestContext.ADMIN
|
||||
|
||||
def test_detect_context_no_state_attribute(self):
|
||||
"""Test context detection when request has no state."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/api/vendors")
|
||||
request.headers = {"host": "localhost"}
|
||||
# No state attribute at all
|
||||
delattr(request, "state")
|
||||
|
||||
# Should still work, falling back to url.path
|
||||
with pytest.raises(AttributeError):
|
||||
# This will raise because we're trying to access request.state
|
||||
ContextManager.detect_context(request)
|
||||
assert context == RequestContext.FALLBACK
|
||||
|
||||
195
tests/unit/middleware/test_frontend_type.py
Normal file
195
tests/unit/middleware/test_frontend_type.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# tests/unit/middleware/test_frontend_type.py
|
||||
"""
|
||||
Unit tests for FrontendTypeMiddleware.
|
||||
|
||||
Tests cover:
|
||||
- Middleware sets frontend_type in request state
|
||||
- All frontend types are correctly detected
|
||||
- get_frontend_type helper function
|
||||
"""
|
||||
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from fastapi import Request
|
||||
|
||||
from app.modules.enums import FrontendType
|
||||
from middleware.frontend_type import FrontendTypeMiddleware, get_frontend_type
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFrontendTypeMiddleware:
|
||||
"""Test suite for FrontendTypeMiddleware."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_admin_frontend_type(self):
|
||||
"""Test middleware sets ADMIN frontend type."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/admin/dashboard", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert hasattr(request.state, "frontend_type")
|
||||
assert request.state.frontend_type == FrontendType.ADMIN
|
||||
call_next.assert_called_once_with(request)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_vendor_frontend_type(self):
|
||||
"""Test middleware sets VENDOR frontend type."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendor/settings")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/vendor/settings", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.frontend_type == FrontendType.VENDOR
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_storefront_frontend_type(self):
|
||||
"""Test middleware sets STOREFRONT frontend type."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/storefront/products")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/storefront/products", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.frontend_type == FrontendType.STOREFRONT
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_storefront_with_vendor_context(self):
|
||||
"""Test middleware sets STOREFRONT when vendor exists in state."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/products")
|
||||
request.headers = {"host": "wizamart.oms.lu"}
|
||||
mock_vendor = Mock()
|
||||
mock_vendor.name = "Test Vendor"
|
||||
request.state = Mock(clean_path="/products", vendor=mock_vendor)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.frontend_type == FrontendType.STOREFRONT
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_sets_platform_frontend_type(self):
|
||||
"""Test middleware sets PLATFORM frontend type."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/pricing")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/pricing", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.frontend_type == FrontendType.PLATFORM
|
||||
call_next.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_returns_response(self):
|
||||
"""Test middleware returns response from call_next."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/test")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/admin/test")
|
||||
|
||||
expected_response = Mock()
|
||||
call_next = AsyncMock(return_value=expected_response)
|
||||
|
||||
response = await middleware.dispatch(request, call_next)
|
||||
|
||||
assert response is expected_response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_uses_clean_path_when_available(self):
|
||||
"""Test middleware uses clean_path when available."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/vendors/wizamart/vendor/settings")
|
||||
request.headers = {"host": "localhost"}
|
||||
# clean_path shows the rewritten path
|
||||
request.state = Mock(clean_path="/vendor/settings", vendor=None)
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
# Should detect as VENDOR based on clean_path
|
||||
assert request.state.frontend_type == FrontendType.VENDOR
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_falls_back_to_url_path(self):
|
||||
"""Test middleware falls back to url.path when clean_path not set."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.headers = {"host": "localhost"}
|
||||
# No clean_path attribute
|
||||
request.state = Mock(spec=[])
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.frontend_type == FrontendType.ADMIN
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetFrontendTypeHelper:
|
||||
"""Test suite for get_frontend_type helper function."""
|
||||
|
||||
def test_get_frontend_type_exists(self):
|
||||
"""Test getting frontend type when it exists."""
|
||||
request = Mock(spec=Request)
|
||||
request.state.frontend_type = FrontendType.ADMIN
|
||||
|
||||
result = get_frontend_type(request)
|
||||
|
||||
assert result == FrontendType.ADMIN
|
||||
|
||||
def test_get_frontend_type_default(self):
|
||||
"""Test getting frontend type returns PLATFORM as default."""
|
||||
request = Mock(spec=Request)
|
||||
request.state = Mock(spec=[]) # No frontend_type attribute
|
||||
|
||||
result = get_frontend_type(request)
|
||||
|
||||
assert result == FrontendType.PLATFORM
|
||||
|
||||
def test_get_frontend_type_for_all_types(self):
|
||||
"""Test getting all frontend types."""
|
||||
for expected_type in FrontendType:
|
||||
request = Mock(spec=Request)
|
||||
request.state.frontend_type = expected_type
|
||||
|
||||
result = get_frontend_type(request)
|
||||
|
||||
assert result == expected_type
|
||||
Reference in New Issue
Block a user