Files
orion/tests/integration/middleware/test_theme_loading_flow.py
Samir Boulahtit d7a0ff8818 refactor: complete module-driven architecture migration
This commit completes the migration to a fully module-driven architecture:

## Models Migration
- Moved all domain models from models/database/ to their respective modules:
  - tenancy: User, Admin, Vendor, Company, Platform, VendorDomain, etc.
  - cms: MediaFile, VendorTheme
  - messaging: Email, VendorEmailSettings, VendorEmailTemplate
  - core: AdminMenuConfig
- models/database/ now only contains Base and TimestampMixin (infrastructure)

## Schemas Migration
- Moved all domain schemas from models/schema/ to their respective modules:
  - tenancy: company, vendor, admin, team, vendor_domain
  - cms: media, image, vendor_theme
  - messaging: email
- models/schema/ now only contains base.py and auth.py (infrastructure)

## Routes Migration
- Moved admin routes from app/api/v1/admin/ to modules:
  - menu_config.py -> core module
  - modules.py -> tenancy module
  - module_config.py -> tenancy module
- app/api/v1/admin/ now only aggregates auto-discovered module routes

## Menu System
- Implemented module-driven menu system with MenuDiscoveryService
- Extended FrontendType enum: PLATFORM, ADMIN, VENDOR, STOREFRONT
- Added MenuItemDefinition and MenuSectionDefinition dataclasses
- Each module now defines its own menu items in definition.py
- MenuService integrates with MenuDiscoveryService for template rendering

## Documentation
- Updated docs/architecture/models-structure.md
- Updated docs/architecture/menu-management.md
- Updated architecture validation rules for new exceptions

## Architecture Validation
- Updated MOD-019 rule to allow base.py in models/schema/
- Created core module exceptions.py and schemas/ directory
- All validation errors resolved (only warnings remain)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:02:56 +01:00

268 lines
11 KiB
Python

# 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.
Note: These tests use pre-registered routes in middleware_test_routes.py.
The conftest patches get_db and settings.platform_domain for proper testing.
"""
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."""
response = client.get(
"/middleware-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
# Colors are flattened to root level by the route
assert data["primary_color"] == "#FF5733"
assert 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."""
response = client.get(
"/middleware-test/theme-default",
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 data["theme_data"] is not None
# Colors are flattened to root level by the route
assert data["primary_color"] is not None
assert data["secondary_color"] is not None
def test_no_theme_loaded_without_vendor(self, client):
"""Test that no theme is loaded when there's no vendor."""
response = client.get(
"/middleware-test/theme-no-vendor", headers={"host": "platform.com"}
)
assert response.status_code == 200
data = response.json()
assert data["has_vendor"] is False
# No vendor means middleware doesn't set theme (or sets default)
# The actual behavior depends on ThemeContextMiddleware implementation
# ========================================================================
# Theme Structure Tests
# ========================================================================
def test_custom_theme_contains_all_fields(self, client, vendor_with_theme):
"""Test that custom theme contains all expected fields."""
response = client.get(
"/middleware-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."""
response = client.get(
"/middleware-test/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."""
response = client.get(
"/shop/middleware-test/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"
# Note: Theme loading in vendor dashboard context is tested via unit tests in
# tests/unit/middleware/test_theme_context.py since /vendor/* integration test
# routes are shadowed by the catch-all /vendor/{vendor_code}/{slug} route.
def test_theme_loaded_in_api_context_with_vendor(self, client, vendor_with_theme):
"""Test API context detection and theme behavior with vendor subdomain.
Note: For API context, vendor detection from subdomain may be skipped
depending on middleware configuration. This test verifies the context
is correctly set to 'api' regardless of vendor detection.
"""
response = client.get(
"/api/middleware-test/theme",
headers={"host": f"{vendor_with_theme.subdomain}.platform.com"},
)
assert response.status_code == 200
data = response.json()
assert data["context_type"] == "api"
# Vendor and theme detection for API routes depends on middleware config
# The important assertion is that context_type is correctly set to 'api'
def test_no_theme_in_admin_context(self, client):
"""Test theme behavior in ADMIN context (no vendor)."""
response = client.get("/admin/middleware-test/no-theme")
assert response.status_code == 200
data = response.json()
assert data["context_type"] == "admin"
# Admin context has no vendor - theme behavior depends on middleware config
# ========================================================================
# 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."""
response = client.get(
"/middleware-test/theme-fields",
headers={"host": f"{vendor_with_theme.subdomain}.platform.com"},
)
assert response.status_code == 200
data = response.json()
assert data["logo_url"] == "/static/vendors/themedvendor/logo.png"
def test_theme_loaded_with_custom_domain_routing(
self, client, vendor_with_custom_domain, db
):
"""Test theme loading behavior with custom domain routing.
Note: Custom domain detection requires the domain to be verified in the
database. This test verifies the theme loading mechanism when a custom
domain is used.
"""
from app.modules.cms.models import VendorTheme
# Add theme to custom domain vendor
theme = VendorTheme(
vendor_id=vendor_with_custom_domain.id,
colors={
"primary": "#123456",
"secondary": "#654321",
"accent": "#ec4899",
"background": "#ffffff",
"text": "#1f2937",
"border": "#e5e7eb",
},
)
db.add(theme)
db.commit()
response = client.get(
"/middleware-test/theme-loading", headers={"host": "customdomain.com"}
)
assert response.status_code == 200
data = response.json()
# Custom domain vendor detection depends on domain verification status
# If vendor is detected and has custom theme, verify it's loaded
# Otherwise, default theme may be applied
# ========================================================================
# 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."""
response = client.get(
"/middleware-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
# ========================================================================
# 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."""
# Make multiple requests
responses = []
for _ in range(3):
response = client.get(
"/middleware-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."""
response = client.get(
"/middleware-test/theme-partial",
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 (route provides "default" fallback)
assert data["primary_color"] is not None
def test_theme_dict_is_mutable(self, client, vendor_with_theme):
"""Test that theme dict can be accessed and read from."""
response = client.get(
"/middleware-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"