# tests/integration/middleware/test_middleware_stack.py """ Integration tests for the complete middleware stack. These tests verify that all middleware components work together correctly through real HTTP requests, ensuring proper execution order and state injection. """ import pytest from unittest.mock import patch from middleware.context import RequestContext @pytest.mark.integration @pytest.mark.middleware class TestMiddlewareStackIntegration: """Test the full middleware stack with real HTTP requests.""" # ======================================================================== # Admin Context Tests # ======================================================================== def test_admin_path_sets_admin_context(self, client): """Test that /admin/* paths set ADMIN context type.""" # Create a simple endpoint to inspect request state from fastapi import Request from main import app @app.get("/admin/test-context") async def test_admin_context(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, "has_theme": hasattr(request.state, 'theme') } response = client.get("/admin/test-context") assert response.status_code == 200 data = response.json() assert data["context_type"] == "admin" # Admin context typically doesn't require vendor, but might have one # The key assertion is that context_type is correctly set to admin def test_admin_subdomain_sets_admin_context(self, client): """Test that admin.* subdomain sets ADMIN context type.""" from fastapi import Request from main import app @app.get("/test-admin-subdomain") async def test_admin_subdomain(request: Request): return { "context_type": request.state.context_type.value if hasattr(request.state, 'context_type') else None } # Simulate request with admin subdomain with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-admin-subdomain", headers={"host": "admin.platform.com"} ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "admin" # ======================================================================== # API Context Tests # ======================================================================== def test_api_path_sets_api_context(self, client): """Test that /api/* paths set API context type.""" from fastapi import Request from main import app @app.get("/api/test-context") async def test_api_context(request: Request): return { "context_type": request.state.context_type.value if hasattr(request.state, 'context_type') else None } response = client.get("/api/test-context") assert response.status_code == 200 data = response.json() assert data["context_type"] == "api" # ======================================================================== # Vendor Dashboard Context Tests # ======================================================================== def test_vendor_dashboard_path_sets_vendor_context(self, client, vendor_with_subdomain): """Test that /vendor/* paths with vendor set VENDOR_DASHBOARD context.""" from fastapi import Request from main import app @app.get("/vendor/test-context") async def test_vendor_context(request: Request): return { "context_type": request.state.context_type.value if hasattr(request.state, 'context_type') else None, "vendor_id": request.state.vendor_id if hasattr(request.state, 'vendor_id') else None, "vendor_code": request.state.vendor.code if hasattr(request.state, 'vendor') else None } # Request with vendor subdomain with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/vendor/test-context", 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["vendor_id"] == vendor_with_subdomain.id assert data["vendor_code"] == vendor_with_subdomain.code # ======================================================================== # Shop Context Tests # ======================================================================== def test_shop_path_with_subdomain_sets_shop_context(self, client, vendor_with_subdomain): """Test that /shop/* paths with vendor subdomain set SHOP context.""" from fastapi import Request from main import app @app.get("/shop/test-context") async def test_shop_context(request: Request): return { "context_type": request.state.context_type.value if hasattr(request.state, 'context_type') else None, "vendor_id": request.state.vendor_id if hasattr(request.state, 'vendor_id') else None, "has_theme": hasattr(request.state, 'theme') } with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/shop/test-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["vendor_id"] == vendor_with_subdomain.id assert data["has_theme"] is True def test_shop_path_with_custom_domain_sets_shop_context(self, client, vendor_with_custom_domain): """Test that /shop/* paths with custom domain set SHOP context.""" from fastapi import Request from main import app @app.get("/shop/test-custom-domain") async def test_shop_custom_domain(request: Request): return { "context_type": request.state.context_type.value if hasattr(request.state, 'context_type') else None, "vendor_id": request.state.vendor_id if hasattr(request.state, 'vendor_id') else None } with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/shop/test-custom-domain", headers={"host": "customdomain.com"} ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "shop" assert data["vendor_id"] == vendor_with_custom_domain.id # ======================================================================== # Middleware Execution Order Tests # ======================================================================== def test_vendor_context_runs_before_context_detection(self, client, vendor_with_subdomain): """Test that VendorContextMiddleware runs before ContextDetectionMiddleware.""" from fastapi import Request from main import app @app.get("/test-execution-order") async def test_execution_order(request: Request): # If vendor context runs first, clean_path should be available # before context detection uses it return { "has_vendor": hasattr(request.state, 'vendor'), "has_clean_path": hasattr(request.state, 'clean_path'), "has_context_type": hasattr(request.state, 'context_type') } with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-execution-order", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"} ) assert response.status_code == 200 data = response.json() # All middleware should have run and set their state assert data["has_vendor"] is True assert data["has_clean_path"] is True assert data["has_context_type"] is True def test_theme_context_runs_after_vendor_context(self, client, vendor_with_theme): """Test that ThemeContextMiddleware runs after VendorContextMiddleware.""" from fastapi import Request from main import app @app.get("/test-theme-loading") async def test_theme_loading(request: Request): return { "has_vendor": hasattr(request.state, 'vendor'), "has_theme": hasattr(request.state, 'theme'), "theme_primary_color": request.state.theme.get('primary_color') if hasattr(request.state, 'theme') else None } with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-theme-loading", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"} ) assert response.status_code == 200 data = response.json() assert data["has_vendor"] is True assert data["has_theme"] is True assert data["theme_primary_color"] == "#FF5733" # ======================================================================== # Static File Handling Tests # ======================================================================== def test_static_files_skip_vendor_detection(self, client): """Test that static file requests skip vendor detection.""" # Static file requests should not trigger vendor detection response = client.get("/static/css/style.css") # We expect 404 (file doesn't exist) but middleware should have run # The important thing is it doesn't crash trying to detect vendor assert response.status_code in [404, 200] # Depending on if file exists # ======================================================================== # Error Handling Tests # ======================================================================== def test_missing_vendor_graceful_handling(self, client): """Test that missing vendor is handled gracefully.""" from fastapi import Request from main import app @app.get("/test-missing-vendor") async def test_missing_vendor(request: Request): return { "has_vendor": hasattr(request.state, 'vendor'), "vendor": request.state.vendor if hasattr(request.state, 'vendor') else None, "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-missing-vendor", headers={"host": "nonexistent.platform.com"} ) assert response.status_code == 200 data = response.json() # Should handle missing vendor gracefully assert data["has_vendor"] is False or data["vendor"] is None # Should still set a context type (fallback) assert data["context_type"] is not None def test_inactive_vendor_not_loaded(self, client, inactive_vendor): """Test that inactive vendors are not loaded.""" from fastapi import Request from main import app @app.get("/test-inactive-vendor") async def test_inactive_vendor_endpoint(request: Request): return { "has_vendor": hasattr(request.state, 'vendor'), "vendor": request.state.vendor if hasattr(request.state, 'vendor') else None } with patch('app.core.config.settings') as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-inactive-vendor", headers={"host": f"{inactive_vendor.subdomain}.platform.com"} ) assert response.status_code == 200 data = response.json() # Inactive vendor should not be loaded assert data["has_vendor"] is False or data["vendor"] is None