# tests/integration/middleware/test_theme_loading_flow.py """ Integration tests for theme loading end-to-end flow. These tests verify that vendor themes are correctly loaded and injected into request.state through real HTTP requests. """ from unittest.mock import patch import pytest @pytest.mark.integration @pytest.mark.middleware @pytest.mark.theme class TestThemeLoadingFlow: """Test theme loading through real HTTP requests.""" # ======================================================================== # Basic Theme Loading Tests # ======================================================================== def test_theme_loaded_for_vendor_with_custom_theme(self, client, vendor_with_theme): """Test that custom theme is loaded for vendor with theme.""" from fastapi import Request from main import app @app.get("/test-theme-loading") async def test_theme(request: Request): theme = request.state.theme if hasattr(request.state, "theme") else None return { "has_theme": theme is not None, "theme_data": theme if 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_theme"] is True assert data["theme_data"] is not None assert data["theme_data"]["primary_color"] == "#FF5733" assert data["theme_data"]["secondary_color"] == "#33FF57" def test_default_theme_loaded_for_vendor_without_theme( self, client, vendor_with_subdomain ): """Test that default theme is loaded for vendor without custom theme.""" from fastapi import Request from main import app @app.get("/test-default-theme") async def test_default_theme(request: Request): theme = request.state.theme if hasattr(request.state, "theme") else None return { "has_theme": theme is not None, "theme_data": theme if theme else None, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-default-theme", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["has_theme"] is True # Default theme should have basic structure assert "primary_color" in data["theme_data"] assert "secondary_color" in data["theme_data"] def test_no_theme_loaded_without_vendor(self, client): """Test that no theme is loaded when there's no vendor.""" from fastapi import Request from main import app @app.get("/test-no-theme") async def test_no_theme(request: Request): return { "has_theme": hasattr(request.state, "theme"), "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-no-theme", headers={"host": "platform.com"}) assert response.status_code == 200 data = response.json() assert data["has_vendor"] is False # No vendor means no theme should be loaded assert data["has_theme"] is False # ======================================================================== # Theme Structure Tests # ======================================================================== def test_custom_theme_contains_all_fields(self, client, vendor_with_theme): """Test that custom theme contains all expected fields.""" from fastapi import Request from main import app @app.get("/test-theme-fields") async def test_theme_fields(request: Request): theme = request.state.theme if hasattr(request.state, "theme") else {} return { "primary_color": theme.get("primary_color"), "secondary_color": theme.get("secondary_color"), "logo_url": theme.get("logo_url"), "favicon_url": theme.get("favicon_url"), "custom_css": theme.get("custom_css"), } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-theme-fields", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["primary_color"] == "#FF5733" assert data["secondary_color"] == "#33FF57" assert data["logo_url"] == "/static/vendors/themedvendor/logo.png" assert data["favicon_url"] == "/static/vendors/themedvendor/favicon.ico" assert "background" in data["custom_css"] def test_default_theme_structure(self, client, vendor_with_subdomain): """Test that default theme has expected structure.""" from fastapi import Request from main import app @app.get("/test-default-theme-structure") async def test_default_structure(request: Request): theme = request.state.theme if hasattr(request.state, "theme") else {} return { "has_primary_color": "primary_color" in theme, "has_secondary_color": "secondary_color" in theme, "has_logo_url": "logo_url" in theme, "has_favicon_url": "favicon_url" in theme, "has_custom_css": "custom_css" in theme, } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-default-theme-structure", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() # Default theme should have basic structure assert data["has_primary_color"] is True assert data["has_secondary_color"] is True # ======================================================================== # Theme Loading for Different Contexts Tests # ======================================================================== def test_theme_loaded_in_shop_context(self, client, vendor_with_theme): """Test that theme is loaded in SHOP context.""" from fastapi import Request from main import app @app.get("/shop/test-shop-theme") async def test_shop_theme(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_theme": hasattr(request.state, "theme"), "theme_primary": ( 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( "/shop/test-shop-theme", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "shop" assert data["has_theme"] is True assert data["theme_primary"] == "#FF5733" def test_theme_loaded_in_vendor_dashboard_context(self, client, vendor_with_theme): """Test that theme is loaded in VENDOR_DASHBOARD context.""" from fastapi import Request from main import app @app.get("/vendor/test-dashboard-theme") async def test_dashboard_theme(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_theme": hasattr(request.state, "theme"), "theme_secondary": ( request.state.theme.get("secondary_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( "/vendor/test-dashboard-theme", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "vendor_dashboard" assert data["has_theme"] is True assert data["theme_secondary"] == "#33FF57" def test_theme_loaded_in_api_context_with_vendor(self, client, vendor_with_theme): """Test that theme is loaded in API context when vendor is present.""" from fastapi import Request from main import app @app.get("/api/test-api-theme") async def test_api_theme(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"), } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/api/test-api-theme", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["context_type"] == "api" assert data["has_vendor"] is True # Theme should be loaded even for API context if vendor present assert data["has_theme"] is True def test_no_theme_in_admin_context(self, client): """Test that theme is not loaded in ADMIN context (no vendor).""" from fastapi import Request from main import app @app.get("/admin/test-admin-no-theme") async def test_admin_no_theme(request: Request): return { "context_type": ( request.state.context_type.value if hasattr(request.state, "context_type") else None ), "has_theme": hasattr(request.state, "theme"), } response = client.get("/admin/test-admin-no-theme") assert response.status_code == 200 data = response.json() assert data["context_type"] == "admin" # Admin context has no vendor, so no theme assert data["has_theme"] is False # ======================================================================== # Theme Loading with Different Routing Modes Tests # ======================================================================== def test_theme_loaded_with_subdomain_routing(self, client, vendor_with_theme): """Test theme loading with subdomain routing.""" from fastapi import Request from main import app @app.get("/test-subdomain-theme") async def test_subdomain_theme(request: Request): return { "vendor_code": ( request.state.vendor.code if hasattr(request.state, "vendor") and request.state.vendor else None ), "theme_logo": ( request.state.theme.get("logo_url") 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-subdomain-theme", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["vendor_code"] == vendor_with_theme.code assert data["theme_logo"] == "/static/vendors/themedvendor/logo.png" def test_theme_loaded_with_custom_domain_routing( self, client, vendor_with_custom_domain, db ): """Test theme loading with custom domain routing.""" # Add theme to custom domain vendor from models.database.vendor_theme import VendorTheme theme = VendorTheme( vendor_id=vendor_with_custom_domain.id, primary_color="#123456", secondary_color="#654321", ) db.add(theme) db.commit() from fastapi import Request from main import app @app.get("/test-custom-domain-theme") async def test_custom_domain_theme(request: Request): return { "has_theme": hasattr(request.state, "theme"), "theme_primary": ( 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-custom-domain-theme", headers={"host": "customdomain.com"} ) assert response.status_code == 200 data = response.json() assert data["has_theme"] is True assert data["theme_primary"] == "#123456" # ======================================================================== # Theme Dependency on Vendor Context Tests # ======================================================================== def test_theme_middleware_depends_on_vendor_middleware( self, client, vendor_with_theme ): """Test that theme loading depends on vendor being detected first.""" from fastapi import Request from main import app @app.get("/test-theme-vendor-dependency") async def test_dependency(request: Request): return { "has_vendor": hasattr(request.state, "vendor") and request.state.vendor is not None, "vendor_id": ( request.state.vendor_id if hasattr(request.state, "vendor_id") else None ), "has_theme": hasattr(request.state, "theme"), "vendor_matches_theme": ( request.state.vendor_id == vendor_with_theme.id if hasattr(request.state, "vendor_id") else False ), } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-theme-vendor-dependency", 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["vendor_id"] == vendor_with_theme.id assert data["has_theme"] is True assert data["vendor_matches_theme"] is True # ======================================================================== # Theme Caching and Performance Tests # ======================================================================== def test_theme_loaded_consistently_across_requests(self, client, vendor_with_theme): """Test that theme is loaded consistently across multiple requests.""" from fastapi import Request from main import app @app.get("/test-theme-consistency") async def test_consistency(request: Request): return { "theme_primary": ( 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" # Make multiple requests responses = [] for _ in range(3): response = client.get( "/test-theme-consistency", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) responses.append(response.json()) # All responses should have same theme assert all(r["theme_primary"] == "#FF5733" for r in responses) # ======================================================================== # Edge Cases and Error Handling Tests # ======================================================================== def test_theme_gracefully_handles_missing_theme_fields( self, client, vendor_with_subdomain ): """Test that missing theme fields are handled gracefully.""" from fastapi import Request from main import app @app.get("/test-partial-theme") async def test_partial_theme(request: Request): theme = request.state.theme if hasattr(request.state, "theme") else {} return { "has_theme": bool(theme), "primary_color": theme.get("primary_color", "default"), "logo_url": theme.get("logo_url", "default"), } with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-partial-theme", headers={"host": f"{vendor_with_subdomain.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["has_theme"] is True # Should have defaults for missing fields assert data["primary_color"] is not None assert data["logo_url"] is not None def test_theme_dict_is_mutable(self, client, vendor_with_theme): """Test that theme dict can be accessed and read from.""" from fastapi import Request from main import app @app.get("/test-theme-mutable") async def test_mutable(request: Request): theme = request.state.theme if hasattr(request.state, "theme") else {} # Try to access theme values primary = theme.get("primary_color") return {"can_read": primary is not None, "value": primary} with patch("app.core.config.settings") as mock_settings: mock_settings.platform_domain = "platform.com" response = client.get( "/test-theme-mutable", headers={"host": f"{vendor_with_theme.subdomain}.platform.com"}, ) assert response.status_code == 200 data = response.json() assert data["can_read"] is True assert data["value"] == "#FF5733"