# tests/integration/middleware/test_context_detection_flow.py """ Integration tests for request context detection end-to-end flow. These tests verify that context type (API, ADMIN, VENDOR_DASHBOARD, SHOP, FALLBACK) is correctly detected through real HTTP requests. """ from unittest.mock import patch import pytest from middleware.context import RequestContext @pytest.mark.integration @pytest.mark.middleware @pytest.mark.context class TestContextDetectionFlow: """Test context type detection through real HTTP requests.""" # ======================================================================== # API Context Detection Tests # ======================================================================== def test_api_path_detected_as_api_context(self, client): """Test that /api/* paths are detected as API context.""" from fastapi import Request from main import app @app.get("/api/test-api-context") async def test_api(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "context_enum": ( request.state.context_type.name if hasattr(request.state, "context_type") else None ), } response = client.get("/api/test-api-context") assert response.status_code == 200 data = response.json() assert data["context_type"] == "api" assert data["context_enum"] == "API" def test_nested_api_path_detected_as_api_context(self, client): """Test that nested /api/ paths are detected as API context.""" from fastapi import Request from main import app @app.get("/api/v1/vendor/products") async def test_nested_api(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ) } response = client.get("/api/v1/vendor/products") assert response.status_code == 200 data = response.json() assert data["context_type"] == "api" # ======================================================================== # Admin Context Detection Tests # ======================================================================== def test_admin_path_detected_as_admin_context(self, client): """Test that /admin/* paths are detected as ADMIN context.""" from fastapi import Request from main import app @app.get("/admin/test-admin-context") async def test_admin(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "context_enum": ( request.state.context_type.name if hasattr(request.state, "context_type") else None ), } response = client.get("/admin/test-admin-context") assert response.status_code == 200 data = response.json() assert data["context_type"] == "admin" assert data["context_enum"] == "ADMIN" def test_admin_subdomain_detected_as_admin_context(self, client): """Test that admin.* subdomain is detected as ADMIN context.""" from fastapi import Request from main import app @app.get("/test-admin-subdomain-context") async def test_admin_subdomain(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ) } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-admin-subdomain-context", headers={"host": "admin.platform.com"} ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "admin" def test_nested_admin_path_detected_as_admin_context(self, client): """Test that nested /admin/ paths are detected as ADMIN context.""" from fastapi import Request from main import app @app.get("/admin/vendors/123/edit") async def test_nested_admin(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ) } response = client.get("/admin/vendors/123/edit") assert response.status_code == 200 data = response.json() assert data["context_type"] == "admin" # ======================================================================== # Vendor Dashboard Context Detection Tests # ======================================================================== def test_vendor_dashboard_path_detected(self, client, vendor_with_subdomain): """Test that /vendor/* paths are detected as VENDOR_DASHBOARD context.""" from fastapi import Request from main import app @app.get("/vendor/test-vendor-dashboard") async def test_vendor_dashboard(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "context_enum": ( request.state.context_type.name if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/vendor/test-vendor-dashboard", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "vendor_dashboard" assert data["context_enum"] == "VENDOR_DASHBOARD" assert data["has_vendor"] is True def test_nested_vendor_dashboard_path_detected(self, client, vendor_with_subdomain): """Test that nested /vendor/ paths are detected as VENDOR_DASHBOARD context.""" from fastapi import Request from main import app @app.get("/vendor/products/123/edit") async def test_nested_vendor(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ) } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/vendor/products/123/edit", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "vendor_dashboard" # ======================================================================== # Shop Context Detection Tests # ======================================================================== def test_shop_path_with_vendor_detected_as_shop( self, client, vendor_with_subdomain ): """Test that /shop/* paths with vendor are detected as SHOP context.""" from fastapi import Request from main import app @app.get("/shop/test-shop-context") async def test_shop(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "context_enum": ( request.state.context_type.name if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/shop/test-shop-context", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "shop" assert data["context_enum"] == "SHOP" assert data["has_vendor"] is True def test_root_path_with_vendor_detected_as_shop( self, client, vendor_with_subdomain ): """Test that root path with vendor is detected as SHOP context.""" from fastapi import Request from main import app @app.get("/test-root-shop") async def test_root_shop(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-root-shop", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() # Root path with vendor should be SHOP context assert data["context_type"] == "shop" assert data["has_vendor"] is True def test_custom_domain_shop_detected(self, client, vendor_with_custom_domain): """Test that custom domain shop is detected as SHOP context.""" from fastapi import Request from main import app @app.get("/products") async def test_custom_domain_shop(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "vendor_code": ( request.state.vendor.code if hasattr(request.state, "vendor") and request.state.vendor else None ), } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get("/products", headers={"host": "customdomain.com"}) assert response.status_code == 200 data = response.json() assert data["context_type"] == "shop" assert data["vendor_code"] == vendor_with_custom_domain.code # ======================================================================== # Fallback Context Detection Tests # ======================================================================== def test_unknown_path_without_vendor_fallback_context(self, client): """Test that unknown paths without vendor get FALLBACK context.""" from fastapi import Request from main import app @app.get("/test-fallback-context") async def test_fallback(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "context_enum": ( request.state.context_type.name if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-fallback-context", headers={"host": "platform.com"} ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "fallback" assert data["context_enum"] == "FALLBACK" assert data["has_vendor"] is False # ======================================================================== # Context Priority Tests (Path takes precedence) # ======================================================================== def test_api_path_overrides_vendor_context(self, client, vendor_with_subdomain): """Test that /api/* path sets API context even with vendor.""" from fastapi import Request from main import app @app.get("/api/test-api-priority") async def test_api_priority(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/api/test-api-priority", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() # API path should override vendor context assert data["context_type"] == "api" # But vendor should still be detected assert data["has_vendor"] is True def test_admin_path_overrides_vendor_context(self, client, vendor_with_subdomain): """Test that /admin/* path sets ADMIN context even with vendor.""" from fastapi import Request from main import app @app.get("/admin/test-admin-priority") async def test_admin_priority(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/admin/test-admin-priority", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() # Admin path should override vendor context assert data["context_type"] == "admin" def test_vendor_dashboard_overrides_shop_context( self, client, vendor_with_subdomain ): """Test that /vendor/* path sets VENDOR_DASHBOARD, not SHOP.""" from fastapi import Request from main import app @app.get("/vendor/test-priority") async def test_vendor_priority(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ) } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/vendor/test-priority", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() # Should be VENDOR_DASHBOARD, not SHOP assert data["context_type"] == "vendor_dashboard" # ======================================================================== # Context Detection with Clean Path Tests # ======================================================================== def test_context_uses_clean_path_for_detection(self, client, vendor_with_subdomain): """Test that context detection uses clean_path, not original path.""" from fastapi import Request from main import app @app.get("/vendors/{vendor_code}/shop/products") async def test_clean_path_context(vendor_code: str, request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "clean_path": ( request.state.clean_path if hasattr(request.state, "clean_path") else None ), "original_path": request.url.path, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( f"/vendors/{vendor_with_subdomain.code}/shop/products", headers={"host": "localhost:8000"}, ) assert response.status_code == 200 data = response.json() # Should detect SHOP context based on clean_path (/shop/products) # not original path (/vendors/{code}/shop/products) assert data["context_type"] == "shop" assert "/shop/products" in data["clean_path"] # ======================================================================== # Context Enum Value Tests # ======================================================================== def test_context_type_is_enum_instance(self, client): """Test that context_type is a RequestContext enum instance.""" from fastapi import Request from main import app @app.get("/api/test-enum") async def test_enum(request: Request): context = ( request.state.context_type if hasattr(request.state, "context_type") else None ) return { "is_enum": isinstance(context, RequestContext) if context else False, "enum_name": context.name if context else None, "enum_value": context.value if context else None, } response = client.get("/api/test-enum") assert response.status_code == 200 data = response.json() assert data["is_enum"] is True assert data["enum_name"] == "API" assert data["enum_value"] == "api" # ======================================================================== # Edge Cases # ======================================================================== def test_empty_path_with_vendor_detected_as_shop( self, client, vendor_with_subdomain ): """Test that empty/root path with vendor is detected as SHOP.""" from fastapi import Request from main import app @app.get("/") async def test_root(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"} ) assert response.status_code in [200, 404] # Might not have root handler if response.status_code == 200: data = response.json() assert data["context_type"] == "shop" assert data["has_vendor"] is True def test_case_insensitive_context_detection(self, client): """Test that context detection is case insensitive for paths.""" from fastapi import Request from main import app @app.get("/API/test-case") @app.get("/api/test-case") async def test_case(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ) } # Test uppercase response = client.get("/API/test-case") if response.status_code == 200: data = response.json() # Should still detect as API (depending on implementation) assert data["context_type"] in ["api", "fallback"]