refactor: remove all backward compatibility code across 70 files
Some checks failed
CI / ruff (push) Successful in 11s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has started running

Clean up 28 backward compatibility instances identified in the codebase.
The app is not live, so all shims are replaced with the target architecture:

- Remove legacy Inventory.location column (use bin_location exclusively)
- Remove dashboard _extract_metric_value helper (use flat metrics dict)
- Remove legacy stat field duplicates (total_stores, total_imports, etc.)
- Remove 13 re-export shims and class aliases across modules
- Remove module-enabling JSON fallback (use PlatformModule junction table)
- Remove menu_to_legacy_format() conversion (return dataclasses directly)
- Remove title/description from MarketplaceProductBase schema
- Clean billing convenience method docstrings
- Clean test fixtures and backward-compat comments
- Add PlatformModule seeding to init_production.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 13:20:29 +01:00
parent b0db8133a0
commit aad18c27ab
70 changed files with 501 additions and 841 deletions

View File

@@ -49,7 +49,7 @@ def test_admin(db, auth_manager):
hashed_password=hashed_password,
role="admin",
is_active=True,
is_super_admin=True, # Default to super admin for backward compatibility
is_super_admin=True, # Full platform access
)
db.add(admin)
db.commit()
@@ -130,7 +130,7 @@ def another_admin(db, auth_manager):
hashed_password=hashed_password,
role="admin",
is_active=True,
is_super_admin=True, # Super admin for backward compatibility
is_super_admin=True, # Full platform access
)
db.add(admin)
db.commit()

View File

@@ -192,7 +192,6 @@ def test_inventory(db, test_product):
store_id=test_product.store_id,
warehouse="strassen",
bin_location=f"SA-10-{unique_id[:2]}",
location=f"WAREHOUSE_A_{unique_id}",
quantity=100,
reserved_quantity=10,
gtin=test_product.marketplace_product.gtin,
@@ -213,7 +212,6 @@ def multiple_inventory_entries(db, multiple_products, test_store):
gtin=product.gtin,
warehouse="strassen",
bin_location=f"SA-{i:02d}-01",
location=f"LOC_{i}",
quantity=10 + (i * 5),
reserved_quantity=i,
store_id=test_store.id,

View File

@@ -7,7 +7,7 @@ requests, including:
- Merchant domain → platform resolved → store resolved
- Store-specific domain overrides merchant domain
- Merchant domain resolves to first active store
- Existing StoreDomain and subdomain routing still work (backward compatibility)
- Existing StoreDomain and subdomain routing still work
"""
import uuid
@@ -95,7 +95,7 @@ class TestMerchantDomainFlow:
assert data["store_code"] == store.store_code
def test_subdomain_routing_still_works(self, client, store_with_subdomain):
"""Test backward compatibility: subdomain routing still works."""
"""Test that subdomain routing still works."""
response = client.get(
"/middleware-test/subdomain-detection",
headers={

View File

@@ -12,7 +12,7 @@ Tests cover:
import pytest
from app.core.frontend_detector import FrontendDetector, get_frontend_type
from app.core.frontend_detector import FrontendDetector
from app.modules.enums import FrontendType
@@ -22,7 +22,7 @@ class TestFrontendDetectorAdmin:
def test_detect_admin_from_subdomain(self):
"""Test admin detection from admin subdomain."""
result = FrontendDetector.detect(host="admin.oms.lu", path="/dashboard")
result = FrontendDetector.detect(host="admin.omsflow.lu", path="/dashboard")
assert result == FrontendType.ADMIN
def test_detect_admin_from_subdomain_with_port(self):
@@ -42,7 +42,7 @@ class TestFrontendDetectorAdmin:
def test_detect_admin_nested_path(self):
"""Test admin detection with nested admin path."""
result = FrontendDetector.detect(host="oms.lu", path="/admin/stores/123/products")
result = FrontendDetector.detect(host="omsflow.lu", path="/admin/stores/123/products")
assert result == FrontendType.ADMIN
@@ -62,7 +62,7 @@ class TestFrontendDetectorStore:
def test_detect_store_nested_path(self):
"""Test store detection with nested store path."""
result = FrontendDetector.detect(host="oms.lu", path="/store/dashboard/analytics")
result = FrontendDetector.detect(host="omsflow.lu", path="/store/dashboard/analytics")
assert result == FrontendType.STORE
def test_stores_plural_not_store_dashboard(self):
@@ -92,7 +92,7 @@ class TestFrontendDetectorStorefront:
def test_detect_storefront_from_store_subdomain(self):
"""Test storefront detection from store subdomain."""
result = FrontendDetector.detect(host="orion.oms.lu", path="/products")
result = FrontendDetector.detect(host="orion.omsflow.lu", path="/products")
assert result == FrontendType.STOREFRONT
def test_detect_storefront_from_store_context(self):
@@ -115,7 +115,7 @@ class TestFrontendDetectorPlatform:
def test_detect_platform_from_marketing_page(self):
"""Test platform detection from marketing page."""
result = FrontendDetector.detect(host="oms.lu", path="/pricing")
result = FrontendDetector.detect(host="omsflow.lu", path="/pricing")
assert result == FrontendType.PLATFORM
def test_detect_platform_from_about(self):
@@ -135,7 +135,7 @@ class TestFrontendDetectorPriority:
def test_admin_subdomain_priority_over_path(self):
"""Test that admin subdomain takes priority."""
result = FrontendDetector.detect(host="admin.oms.lu", path="/storefront/products")
result = FrontendDetector.detect(host="admin.omsflow.lu", path="/storefront/products")
assert result == FrontendType.ADMIN
def test_admin_path_priority_over_store_context(self):
@@ -148,7 +148,7 @@ class TestFrontendDetectorPriority:
def test_path_priority_over_subdomain(self):
"""Test that explicit path takes priority for store/storefront."""
# /store/ path on a store subdomain -> STORE (path wins)
result = FrontendDetector.detect(host="orion.oms.lu", path="/store/settings")
result = FrontendDetector.detect(host="orion.omsflow.lu", path="/store/settings")
assert result == FrontendType.STORE
@@ -159,20 +159,20 @@ class TestFrontendDetectorHelpers:
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("omsflow.lu") == "omsflow.lu"
assert FrontendDetector._strip_port("admin.localhost:9999") == "admin.localhost"
def test_get_subdomain(self):
"""Test subdomain extraction."""
assert FrontendDetector._get_subdomain("orion.oms.lu") == "orion"
assert FrontendDetector._get_subdomain("admin.oms.lu") == "admin"
assert FrontendDetector._get_subdomain("oms.lu") is None
assert FrontendDetector._get_subdomain("orion.omsflow.lu") == "orion"
assert FrontendDetector._get_subdomain("admin.omsflow.lu") == "admin"
assert FrontendDetector._get_subdomain("omsflow.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("admin.omsflow.lu", "/dashboard") is True
assert FrontendDetector.is_admin("localhost", "/admin/stores") is True
assert FrontendDetector.is_admin("localhost", "/store/settings") is False
@@ -185,13 +185,13 @@ class TestFrontendDetectorHelpers:
def test_is_storefront(self):
"""Test is_storefront convenience method."""
assert FrontendDetector.is_storefront("localhost", "/storefront/products") is True
assert FrontendDetector.is_storefront("orion.oms.lu", "/products") is True
assert FrontendDetector.is_storefront("orion.omsflow.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("omsflow.lu", "/pricing") is True
assert FrontendDetector.is_platform("localhost", "/admin/dashboard") is False
def test_is_api_request(self):
@@ -201,46 +201,21 @@ class TestFrontendDetectorHelpers:
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_store(self):
"""Test get_frontend_type returns store."""
result = get_frontend_type("localhost", "/store/settings")
assert result == FrontendType.STORE
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 store storefront."""
result = FrontendDetector.detect(host="www.oms.lu", path="/")
result = FrontendDetector.detect(host="www.omsflow.lu", path="/")
assert result == FrontendType.PLATFORM
def test_api_subdomain_not_storefront(self):
"""Test that api subdomain is not treated as store storefront."""
result = FrontendDetector.detect(host="api.oms.lu", path="/v1/products")
result = FrontendDetector.detect(host="api.omsflow.lu", path="/v1/products")
assert result == FrontendType.PLATFORM
def test_portal_subdomain_not_storefront(self):
"""Test that portal subdomain is not treated as store storefront."""
result = FrontendDetector.detect(host="portal.oms.lu", path="/")
result = FrontendDetector.detect(host="portal.omsflow.lu", path="/")
assert result == FrontendType.PLATFORM

View File

@@ -80,7 +80,7 @@ class TestFrontendTypeMiddleware:
request = Mock(spec=Request)
request.url = Mock(path="/products")
request.headers = {"host": "orion.oms.lu"}
request.headers = {"host": "orion.omsflow.lu"}
mock_store = Mock()
mock_store.name = "Test Store"
request.state = Mock(clean_path="/products", store=mock_store)

View File

@@ -13,7 +13,7 @@ Tests cover:
URL Structure:
- Main marketing site: localhost:9999/ (no prefix) -> 'main' platform
- Platform sites: localhost:9999/platforms/{code}/ -> specific platform
- Production: domain-based (oms.lu, loyalty.lu)
- Production: domain-based (omsflow.lu, rewardflow.lu)
"""
from unittest.mock import AsyncMock, MagicMock, Mock, patch
@@ -42,35 +42,35 @@ class TestPlatformContextManager:
# ========================================================================
def test_detect_domain_based_platform(self):
"""Test domain-based platform detection for production (e.g., oms.lu)."""
"""Test domain-based platform detection for production (e.g., omsflow.lu)."""
request = Mock(spec=Request)
request.headers = {"host": "oms.lu"}
request.headers = {"host": "omsflow.lu"}
request.url = Mock(path="/pricing")
context = PlatformContextManager.detect_platform_context(request)
assert context is not None
assert context["detection_method"] == "domain"
assert context["domain"] == "oms.lu"
assert context["host"] == "oms.lu"
assert context["domain"] == "omsflow.lu"
assert context["host"] == "omsflow.lu"
assert context["original_path"] == "/pricing"
def test_detect_domain_with_port(self):
"""Test domain detection with port number."""
request = Mock(spec=Request)
request.headers = {"host": "loyalty.lu:8443"}
request.headers = {"host": "rewardflow.lu:8443"}
request.url = Mock(path="/features")
context = PlatformContextManager.detect_platform_context(request)
assert context is not None
assert context["detection_method"] == "domain"
assert context["domain"] == "loyalty.lu"
assert context["domain"] == "rewardflow.lu"
def test_detect_domain_three_level_not_detected(self):
"""Test that three-level domains (subdomains) are not detected as platform domains."""
request = Mock(spec=Request)
request.headers = {"host": "store.oms.lu"}
request.headers = {"host": "store.omsflow.lu"}
request.url = Mock(path="/shop")
context = PlatformContextManager.detect_platform_context(request)
@@ -295,7 +295,7 @@ class TestPlatformContextManager:
mock_db.query.return_value.filter.return_value.filter.return_value.first.return_value = mock_platform
context = {"detection_method": "domain", "domain": "oms.lu"}
context = {"detection_method": "domain", "domain": "omsflow.lu"}
platform = PlatformContextManager.get_platform_from_context(mock_db, context)
@@ -398,7 +398,7 @@ class TestPlatformContextManager:
request = Mock(spec=Request)
request.url = Mock(path="/pricing")
context = {"detection_method": "domain", "domain": "oms.lu"}
context = {"detection_method": "domain", "domain": "omsflow.lu"}
clean_path = PlatformContextManager.extract_clean_path(request, context)
@@ -598,7 +598,7 @@ class TestPlatformContextMiddleware:
scope = {
"type": "http",
"path": "/pricing",
"headers": [(b"host", b"oms.lu")],
"headers": [(b"host", b"omsflow.lu")],
}
receive = AsyncMock()
@@ -918,7 +918,7 @@ class TestEdgeCases:
def test_admin_subdomain_with_production_domain(self):
"""Test admin subdomain detection for production domains."""
assert FrontendDetector.is_admin("admin.oms.lu", "/dashboard") is True
assert FrontendDetector.is_admin("admin.omsflow.lu", "/dashboard") is True
def test_static_file_case_insensitive(self):
"""Test static file detection is case-insensitive."""
@@ -945,7 +945,7 @@ class TestURLRoutingSummary:
- Main marketing: localhost:9999/ -> 'main' platform, path unchanged
- OMS platform: localhost:9999/platforms/oms/pricing -> 'oms' platform, path=/pricing
- Loyalty platform: localhost:9999/platforms/loyalty/features -> 'loyalty' platform, path=/features
- Production OMS: oms.lu/pricing -> 'oms' platform, path=/pricing (no rewrite)
- Production OMS: omsflow.lu/pricing -> 'oms' platform, path=/pricing (no rewrite)
"""
def test_main_marketing_site_routing(self):
@@ -987,11 +987,11 @@ class TestURLRoutingSummary:
def test_production_domain_routing(self):
"""Document: Production domains don't rewrite path."""
request = Mock(spec=Request)
request.headers = {"host": "oms.lu"}
request.headers = {"host": "omsflow.lu"}
request.url = Mock(path="/pricing")
context = PlatformContextManager.detect_platform_context(request)
assert context["detection_method"] == "domain"
assert context["domain"] == "oms.lu"
assert context["domain"] == "omsflow.lu"
# clean_path not set for domain detection - uses original path

View File

@@ -203,17 +203,17 @@ class TestAdminService:
"""Test getting store statistics"""
stats = stats_service.get_store_statistics(db)
assert "total_stores" in stats
assert "active_stores" in stats
assert "verified_stores" in stats
assert "total" in stats
assert "verified" in stats
assert "pending" in stats
assert "inactive" in stats
assert "verification_rate" in stats
assert isinstance(stats["total_stores"], int)
assert isinstance(stats["active_stores"], int)
assert isinstance(stats["verified_stores"], int)
assert isinstance(stats["total"], int)
assert isinstance(stats["verified"], int)
assert isinstance(stats["verification_rate"], int | float)
assert stats["total_stores"] >= 1
assert stats["total"] >= 1
# Error Handling Tests
def test_get_all_users_database_error(self, db_with_error, test_admin):
@@ -259,9 +259,9 @@ class TestAdminService:
"""Test store statistics when no stores exist"""
stats = stats_service.get_store_statistics(empty_db)
assert stats["total_stores"] == 0
assert stats["active_stores"] == 0
assert stats["verified_stores"] == 0
assert stats["total"] == 0
assert stats["verified"] == 0
assert stats["inactive"] == 0
assert stats["verification_rate"] == 0

View File

@@ -362,21 +362,20 @@ class TestStatsService:
"""Test getting store statistics for admin dashboard."""
stats = self.service.get_store_statistics(db)
assert "total_stores" in stats
assert "active_stores" in stats
assert "inactive_stores" in stats
assert "verified_stores" in stats
assert "total" in stats
assert "verified" in stats
assert "pending" in stats
assert "inactive" in stats
assert "verification_rate" in stats
assert stats["total_stores"] >= 1
assert stats["active_stores"] >= 1
assert stats["total"] >= 1
def test_get_store_statistics_calculates_rates(self, db, test_store):
"""Test store statistics calculates rates correctly."""
stats = self.service.get_store_statistics(db)
if stats["total_stores"] > 0:
expected_rate = stats["verified_stores"] / stats["total_stores"] * 100
if stats["total"] > 0:
expected_rate = stats["verified"] / stats["total"] * 100
assert abs(stats["verification_rate"] - expected_rate) < 0.01
def test_get_store_statistics_database_error(self, db):
@@ -422,9 +421,9 @@ class TestStatsService:
"""Test getting import statistics."""
stats = self.service.get_import_statistics(db)
assert "total_imports" in stats
assert "completed_imports" in stats
assert "failed_imports" in stats
assert "total" in stats
assert "completed" in stats
assert "failed" in stats
assert "success_rate" in stats
def test_get_import_statistics_with_jobs(
@@ -433,15 +432,15 @@ class TestStatsService:
"""Test import statistics with existing jobs."""
stats = self.service.get_import_statistics(db)
assert stats["total_imports"] >= 1
assert stats["completed_imports"] >= 1 # test job has completed status
assert stats["total"] >= 1
assert stats["completed"] >= 1 # test job has completed status
def test_get_import_statistics_calculates_rate(self, db):
"""Test import statistics calculates success rate."""
stats = self.service.get_import_statistics(db)
if stats["total_imports"] > 0:
expected_rate = stats["completed_imports"] / stats["total_imports"] * 100
if stats["total"] > 0:
expected_rate = stats["completed"] / stats["total"] * 100
assert abs(stats["success_rate"] - expected_rate) < 0.01
else:
assert stats["success_rate"] == 0
@@ -452,9 +451,9 @@ class TestStatsService:
stats = self.service.get_import_statistics(db)
# Should return default values, not raise exception
assert stats["total_imports"] == 0
assert stats["completed_imports"] == 0
assert stats["failed_imports"] == 0
assert stats["total"] == 0
assert stats["completed"] == 0
assert stats["failed"] == 0
assert stats["success_rate"] == 0
# ==================== Private Helper Method Tests ====================
@@ -538,7 +537,6 @@ class TestStatsService:
gtin=f"123456789{unique_id[:4]}",
warehouse="strassen",
bin_location=f"ST-{unique_id[:2]}-01",
location=f"LOCATION2_{unique_id}",
quantity=25,
reserved_quantity=5,
store_id=test_inventory.store_id,