refactor: remove legacy /shop and /api/v1/shop dead code

After the storefront migration, no live routes mount under /api/v1/shop/.
Remove all dead code that detected/handled shop API requests: the
is_shop_api_request() method, the shop API dispatch branch in middleware,
the RequestContext.SHOP enum member (renamed to STOREFRONT), legacy path
prefixes in FrontendDetector, and all associated tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 13:16:43 +01:00
parent 874e254c11
commit 9173448645
10 changed files with 76 additions and 333 deletions

View File

@@ -26,7 +26,7 @@ from main import app
from tests.integration.middleware.middleware_test_routes import (
admin_router,
api_router,
shop_router,
storefront_router,
store_router,
)
from tests.integration.middleware.middleware_test_routes import (
@@ -39,7 +39,7 @@ if not any(r.path.startswith("/middleware-test") for r in app.routes if hasattr(
app.include_router(api_router)
app.include_router(admin_router)
app.include_router(store_router)
app.include_router(shop_router)
app.include_router(storefront_router)
@pytest.fixture

View File

@@ -10,7 +10,7 @@ IMPORTANT: Routes are organized by prefix to avoid conflicts:
- /api/middleware-test/* - API context testing
- /admin/middleware-test/* - Admin context testing
- /store/middleware-test/* - Store dashboard context testing
- /shop/middleware-test/* - Shop context testing
- /storefront/middleware-test/* - Storefront context testing
"""
from fastapi import APIRouter, Request
@@ -531,15 +531,15 @@ async def test_store_dashboard_theme(request: Request):
# =============================================================================
# Shop Context Test Router
# Storefront Context Test Router
# =============================================================================
shop_router = APIRouter(prefix="/shop/middleware-test")
storefront_router = APIRouter(prefix="/storefront/middleware-test")
@shop_router.get("/context")
async def test_shop_context(request: Request):
"""Test shop context detection."""
@storefront_router.get("/context")
async def test_storefront_context(request: Request):
"""Test storefront context detection."""
context_type = getattr(request.state, "context_type", None)
store = getattr(request.state, "store", None)
theme = getattr(request.state, "theme", None)
@@ -552,9 +552,9 @@ async def test_shop_context(request: Request):
}
@shop_router.get("/custom-domain-context")
async def test_shop_custom_domain_context(request: Request):
"""Test shop context with custom domain."""
@storefront_router.get("/custom-domain-context")
async def test_storefront_custom_domain_context(request: Request):
"""Test storefront context with custom domain."""
context_type = getattr(request.state, "context_type", None)
store = getattr(request.state, "store", None)
return {
@@ -564,9 +564,9 @@ async def test_shop_custom_domain_context(request: Request):
}
@shop_router.get("/theme")
async def test_shop_theme(request: Request):
"""Test theme in shop context."""
@storefront_router.get("/theme")
async def test_storefront_theme(request: Request):
"""Test theme in storefront context."""
context_type = getattr(request.state, "context_type", None)
theme = getattr(request.state, "theme", None)
colors = theme.get("colors", {}) if theme else {}

View File

@@ -7,7 +7,6 @@ Tests cover:
- Path-based detection (dev mode)
- Subdomain-based detection (prod mode)
- Custom domain detection
- Legacy /shop/ path support
- Priority order of detection methods
"""
@@ -103,15 +102,6 @@ class TestFrontendDetectorStorefront:
)
assert result == FrontendType.STOREFRONT
def test_detect_storefront_legacy_shop_path(self):
"""Test storefront detection from legacy /shop path."""
result = FrontendDetector.detect(host="localhost", path="/shop/products")
assert result == FrontendType.STOREFRONT
def test_detect_storefront_legacy_shop_api_path(self):
"""Test storefront detection from legacy /api/v1/shop path."""
result = FrontendDetector.detect(host="localhost", path="/api/v1/shop/cart")
assert result == FrontendType.STOREFRONT
@pytest.mark.unit

View File

@@ -32,7 +32,7 @@ class TestRequestContextEnumBackwardCompatibility:
assert RequestContext.API.value == "api"
assert RequestContext.ADMIN.value == "admin"
assert RequestContext.STORE_DASHBOARD.value == "store"
assert RequestContext.SHOP.value == "shop"
assert RequestContext.STOREFRONT.value == "storefront"
assert RequestContext.FALLBACK.value == "fallback"
def test_request_context_types(self):
@@ -101,7 +101,7 @@ class TestGetRequestContextBackwardCompatibility:
assert context == RequestContext.STORE_DASHBOARD
def test_get_request_context_maps_storefront(self):
"""Test get_request_context maps FrontendType.STOREFRONT to RequestContext.SHOP."""
"""Test get_request_context maps FrontendType.STOREFRONT to RequestContext.STOREFRONT."""
from app.modules.enums import FrontendType
request = Mock(spec=Request)
@@ -113,7 +113,7 @@ class TestGetRequestContextBackwardCompatibility:
warnings.simplefilter("ignore", DeprecationWarning)
context = get_request_context(request)
assert context == RequestContext.SHOP
assert context == RequestContext.STOREFRONT
def test_get_request_context_maps_platform_to_fallback(self):
"""Test get_request_context maps FrontendType.PLATFORM to RequestContext.FALLBACK."""

View File

@@ -102,10 +102,10 @@ class TestStoreContextManager:
"""Test path-based detection with /store/ prefix."""
request = Mock(spec=Request)
request.headers = {"host": "localhost"}
request.url = Mock(path="/store/store1/shop")
request.url = Mock(path="/store/store1/storefront")
# Set platform_clean_path to simulate PlatformContextMiddleware output
request.state = Mock()
request.state.platform_clean_path = "/store/store1/shop"
request.state.platform_clean_path = "/store/store1/storefront"
context = StoreContextManager.detect_store_context(request)
@@ -119,10 +119,10 @@ class TestStoreContextManager:
"""Test path-based detection with /stores/ prefix."""
request = Mock(spec=Request)
request.headers = {"host": "localhost"}
request.url = Mock(path="/stores/store1/shop")
request.url = Mock(path="/stores/store1/storefront")
# Set platform_clean_path to simulate PlatformContextMiddleware output
request.state = Mock()
request.state.platform_clean_path = "/stores/store1/shop"
request.state.platform_clean_path = "/stores/store1/storefront"
context = StoreContextManager.detect_store_context(request)
@@ -310,24 +310,24 @@ class TestStoreContextManager:
def test_extract_clean_path_from_store_path(self):
"""Test extracting clean path from /store/ prefix."""
request = Mock(spec=Request)
request.url = Mock(path="/store/store1/shop/products")
request.url = Mock(path="/store/store1/storefront/products")
store_context = {"detection_method": "path", "path_prefix": "/store/store1"}
clean_path = StoreContextManager.extract_clean_path(request, store_context)
assert clean_path == "/shop/products"
assert clean_path == "/storefront/products"
def test_extract_clean_path_from_stores_path(self):
"""Test extracting clean path from /stores/ prefix."""
request = Mock(spec=Request)
request.url = Mock(path="/stores/store1/shop/products")
request.url = Mock(path="/stores/store1/storefront/products")
store_context = {"detection_method": "path", "path_prefix": "/stores/store1"}
clean_path = StoreContextManager.extract_clean_path(request, store_context)
assert clean_path == "/shop/products"
assert clean_path == "/storefront/products"
def test_extract_clean_path_root(self):
"""Test extracting clean path when result is empty (should return /)."""
@@ -343,22 +343,22 @@ class TestStoreContextManager:
def test_extract_clean_path_no_path_context(self):
"""Test extracting clean path for non-path detection methods."""
request = Mock(spec=Request)
request.url = Mock(path="/shop/products")
request.url = Mock(path="/storefront/products")
store_context = {"detection_method": "subdomain", "subdomain": "store1"}
clean_path = StoreContextManager.extract_clean_path(request, store_context)
assert clean_path == "/shop/products"
assert clean_path == "/storefront/products"
def test_extract_clean_path_no_context(self):
"""Test extracting clean path with no store context."""
request = Mock(spec=Request)
request.url = Mock(path="/shop/products")
request.url = Mock(path="/storefront/products")
clean_path = StoreContextManager.extract_clean_path(request, None)
assert clean_path == "/shop/products"
assert clean_path == "/storefront/products"
# ========================================================================
# Request Type Detection Tests
@@ -392,7 +392,7 @@ class TestStoreContextManager:
"""Test non-admin request."""
request = Mock(spec=Request)
request.headers = {"host": "store1.platform.com"}
request.url = Mock(path="/shop")
request.url = Mock(path="/storefront")
assert StoreContextManager.is_admin_request(request) is False
@@ -406,49 +406,10 @@ class TestStoreContextManager:
def test_is_not_api_request(self):
"""Test non-API request."""
request = Mock(spec=Request)
request.url = Mock(path="/shop/products")
request.url = Mock(path="/storefront/products")
assert StoreContextManager.is_api_request(request) is False
# ========================================================================
# Shop API Request Detection Tests
# ========================================================================
def test_is_shop_api_request(self):
"""Test shop API request detection."""
request = Mock(spec=Request)
request.url = Mock(path="/api/v1/shop/products")
assert StoreContextManager.is_shop_api_request(request) is True
def test_is_shop_api_request_cart(self):
"""Test shop API request detection for cart endpoint."""
request = Mock(spec=Request)
request.url = Mock(path="/api/v1/shop/cart")
assert StoreContextManager.is_shop_api_request(request) is True
def test_is_not_shop_api_request_admin(self):
"""Test non-shop API request (admin API)."""
request = Mock(spec=Request)
request.url = Mock(path="/api/v1/admin/stores")
assert StoreContextManager.is_shop_api_request(request) is False
def test_is_not_shop_api_request_store(self):
"""Test non-shop API request (store API)."""
request = Mock(spec=Request)
request.url = Mock(path="/api/v1/store/products")
assert StoreContextManager.is_shop_api_request(request) is False
def test_is_not_shop_api_request_non_api(self):
"""Test non-shop API request (non-API path)."""
request = Mock(spec=Request)
request.url = Mock(path="/shop/products")
assert StoreContextManager.is_shop_api_request(request) is False
# ========================================================================
# Extract Store From Referer Tests
# ========================================================================
@@ -457,7 +418,7 @@ class TestStoreContextManager:
"""Test extracting store from referer with /stores/ path."""
request = Mock(spec=Request)
request.headers = {
"referer": "http://localhost:8000/stores/wizamart/shop/products"
"referer": "http://localhost:8000/stores/wizamart/storefront/products"
}
context = StoreContextManager.extract_store_from_referer(request)
@@ -472,7 +433,7 @@ class TestStoreContextManager:
"""Test extracting store from referer with /store/ path."""
request = Mock(spec=Request)
request.headers = {
"referer": "http://localhost:8000/store/myshop/shop/products"
"referer": "http://localhost:8000/store/myshop/storefront/products"
}
context = StoreContextManager.extract_store_from_referer(request)
@@ -486,7 +447,7 @@ class TestStoreContextManager:
def test_extract_store_from_referer_subdomain(self):
"""Test extracting store from referer with subdomain."""
request = Mock(spec=Request)
request.headers = {"referer": "http://wizamart.platform.com/shop/products"}
request.headers = {"referer": "http://wizamart.platform.com/storefront/products"}
with patch("middleware.store_context.settings") as mock_settings:
mock_settings.platform_domain = "platform.com"
@@ -501,7 +462,7 @@ class TestStoreContextManager:
def test_extract_store_from_referer_custom_domain(self):
"""Test extracting store from referer with custom domain."""
request = Mock(spec=Request)
request.headers = {"referer": "http://my-custom-shop.com/shop/products"}
request.headers = {"referer": "http://my-custom-shop.com/storefront/products"}
with patch("middleware.store_context.settings") as mock_settings:
mock_settings.platform_domain = "platform.com"
@@ -525,7 +486,7 @@ class TestStoreContextManager:
def test_extract_store_from_referer_origin_header(self):
"""Test extracting store from origin header when referer is missing."""
request = Mock(spec=Request)
request.headers = {"origin": "http://localhost:8000/stores/testshop/shop"}
request.headers = {"origin": "http://localhost:8000/stores/testshop/storefront"}
context = StoreContextManager.extract_store_from_referer(request)
@@ -548,7 +509,7 @@ class TestStoreContextManager:
def test_extract_store_from_referer_ignores_www_subdomain(self):
"""Test that www subdomain is not extracted from referer."""
request = Mock(spec=Request)
request.headers = {"referer": "http://www.platform.com/shop"}
request.headers = {"referer": "http://www.platform.com/storefront"}
with patch("middleware.store_context.settings") as mock_settings:
mock_settings.platform_domain = "platform.com"
@@ -560,7 +521,7 @@ class TestStoreContextManager:
def test_extract_store_from_referer_localhost_not_custom_domain(self):
"""Test that localhost is not treated as custom domain."""
request = Mock(spec=Request)
request.headers = {"referer": "http://localhost:8000/shop"}
request.headers = {"referer": "http://localhost:8000/storefront"}
with patch("middleware.store_context.settings") as mock_settings:
mock_settings.platform_domain = "platform.com"
@@ -601,7 +562,7 @@ class TestStoreContextManager:
@pytest.mark.parametrize(
"path",
[
"/shop/products",
"/storefront/products",
"/admin/dashboard",
"/api/stores",
"/about",
@@ -686,7 +647,7 @@ class TestStoreContextMiddleware:
request = Mock(spec=Request)
request.headers = {"host": "store1.platform.com"}
request.url = Mock(path="/shop/products")
request.url = Mock(path="/storefront/products")
request.state = Mock()
call_next = AsyncMock(return_value=Mock())
@@ -714,7 +675,7 @@ class TestStoreContextMiddleware:
patch.object(
StoreContextManager,
"extract_clean_path",
return_value="/shop/products",
return_value="/storefront/products",
),
patch("middleware.store_context.get_db", return_value=iter([mock_db])),
):
@@ -722,7 +683,7 @@ class TestStoreContextMiddleware:
assert request.state.store is mock_store
assert request.state.store_context == store_context
assert request.state.clean_path == "/shop/products"
assert request.state.clean_path == "/storefront/products"
call_next.assert_called_once_with(request)
@pytest.mark.asyncio
@@ -732,7 +693,7 @@ class TestStoreContextMiddleware:
request = Mock(spec=Request)
request.headers = {"host": "nonexistent.platform.com"}
request.url = Mock(path="/shop")
request.url = Mock(path="/storefront")
request.state = Mock()
call_next = AsyncMock(return_value=Mock())
@@ -756,7 +717,7 @@ class TestStoreContextMiddleware:
assert request.state.store is None
assert request.state.store_context == store_context
assert request.state.clean_path == "/shop"
assert request.state.clean_path == "/storefront"
call_next.assert_called_once_with(request)
@pytest.mark.asyncio
@@ -820,146 +781,6 @@ class TestStoreContextMiddleware:
assert request.state.clean_path == path
call_next.assert_called_once_with(request)
# ========================================================================
# Shop API Request Handling Tests
# ========================================================================
@pytest.mark.asyncio
async def test_middleware_shop_api_with_referer_store_found(self):
"""Test middleware handles shop API request with store from Referer."""
middleware = StoreContextMiddleware(app=None)
request = Mock(spec=Request)
request.headers = {
"host": "localhost",
"referer": "http://localhost:8000/stores/wizamart/shop/products",
}
request.url = Mock(path="/api/v1/shop/cart")
request.state = Mock()
call_next = AsyncMock(return_value=Mock())
mock_store = Mock()
mock_store.id = 1
mock_store.name = "Wizamart"
mock_store.subdomain = "wizamart"
store_context = {
"subdomain": "wizamart",
"detection_method": "path",
"path_prefix": "/stores/wizamart",
"full_prefix": "/stores/",
}
mock_db = MagicMock()
with (
patch.object(StoreContextManager, "is_admin_request", return_value=False),
patch.object(
StoreContextManager, "is_static_file_request", return_value=False
),
patch.object(
StoreContextManager, "is_shop_api_request", return_value=True
),
patch.object(
StoreContextManager,
"extract_store_from_referer",
return_value=store_context,
),
patch.object(
StoreContextManager,
"get_store_from_context",
return_value=mock_store,
),
patch("middleware.store_context.get_db", return_value=iter([mock_db])),
):
await middleware.dispatch(request, call_next)
assert request.state.store is mock_store
assert request.state.store_context == store_context
assert request.state.clean_path == "/api/v1/shop/cart"
call_next.assert_called_once_with(request)
@pytest.mark.asyncio
async def test_middleware_shop_api_with_referer_store_not_found(self):
"""Test middleware handles shop API when store from Referer not in database."""
middleware = StoreContextMiddleware(app=None)
request = Mock(spec=Request)
request.headers = {
"host": "localhost",
"referer": "http://localhost:8000/stores/nonexistent/shop/products",
}
request.url = Mock(path="/api/v1/shop/cart")
request.state = Mock()
call_next = AsyncMock(return_value=Mock())
store_context = {
"subdomain": "nonexistent",
"detection_method": "path",
"path_prefix": "/stores/nonexistent",
"full_prefix": "/stores/",
}
mock_db = MagicMock()
with (
patch.object(StoreContextManager, "is_admin_request", return_value=False),
patch.object(
StoreContextManager, "is_static_file_request", return_value=False
),
patch.object(
StoreContextManager, "is_shop_api_request", return_value=True
),
patch.object(
StoreContextManager,
"extract_store_from_referer",
return_value=store_context,
),
patch.object(
StoreContextManager, "get_store_from_context", return_value=None
),
patch("middleware.store_context.get_db", return_value=iter([mock_db])),
):
await middleware.dispatch(request, call_next)
assert request.state.store is None
assert request.state.store_context == store_context
assert request.state.clean_path == "/api/v1/shop/cart"
call_next.assert_called_once_with(request)
@pytest.mark.asyncio
async def test_middleware_shop_api_without_referer(self):
"""Test middleware handles shop API request without Referer header."""
middleware = StoreContextMiddleware(app=None)
request = Mock(spec=Request)
request.headers = {"host": "localhost"}
request.url = Mock(path="/api/v1/shop/products")
request.state = Mock()
call_next = AsyncMock(return_value=Mock())
with (
patch.object(StoreContextManager, "is_admin_request", return_value=False),
patch.object(
StoreContextManager, "is_static_file_request", return_value=False
),
patch.object(
StoreContextManager, "is_shop_api_request", return_value=True
),
patch.object(
StoreContextManager, "extract_store_from_referer", return_value=None
),
):
await middleware.dispatch(request, call_next)
assert request.state.store is None
assert request.state.store_context is None
assert request.state.clean_path == "/api/v1/shop/products"
call_next.assert_called_once_with(request)
@pytest.mark.unit
@pytest.mark.stores