fix: storefront login 403, cookie path, double-storefront URLs, and auth redirects
- Extract store/platform context from Referer header for storefront API requests
(StoreContextMiddleware and PlatformContextMiddleware) so login POST works in
dev mode where API paths lack /platforms/{code}/ prefix
- Set customer token cookie path to "/" for cross-route compatibility
- Fix double storefront in URLs: replace {{ base_url }}storefront/ with {{ base_url }}
across all 24 storefront templates
- Fix auth error redirect to include platform prefix and use store_code
- Update seed script to output correct storefront login URLs
- Add 20 new unit tests covering all fixes; fix 9 pre-existing test failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
44
tests/unit/api/test_storefront_auth.py
Normal file
44
tests/unit/api/test_storefront_auth.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# tests/unit/api/test_storefront_auth.py
|
||||
"""
|
||||
Unit tests for storefront auth cookie handling.
|
||||
|
||||
Tests that customer_token cookie is set with path='/' so it works
|
||||
across all URL patterns (dev mode with /platforms/ prefix, prod mode with subdomains).
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestCustomerTokenCookiePath:
|
||||
"""Verify cookie path is set correctly for cross-routing compatibility."""
|
||||
|
||||
def test_login_sets_cookie_with_root_path(self):
|
||||
"""
|
||||
The customer_token cookie must use path='/' to work with all URL patterns.
|
||||
|
||||
Previously the cookie path was calculated as '/storefront/{subdomain}/storefront'
|
||||
which didn't match the actual page URLs (/platforms/{code}/storefront/{store_code}/...).
|
||||
"""
|
||||
import inspect
|
||||
|
||||
from app.modules.customers.routes.api.storefront import customer_login
|
||||
|
||||
source = inspect.getsource(customer_login)
|
||||
# Verify the cookie is set with path="/"
|
||||
assert 'path="/"' in source or "path='/'" in source, (
|
||||
"customer_login must set cookie with path='/'. "
|
||||
"Other paths like '/storefront' or '/storefront/{subdomain}/storefront' "
|
||||
"don't match dev mode URLs (/platforms/{code}/storefront/{store_code}/...)"
|
||||
)
|
||||
|
||||
def test_logout_deletes_cookie_with_root_path(self):
|
||||
"""The customer_token cookie must be deleted with path='/' to match the set path."""
|
||||
import inspect
|
||||
|
||||
from app.modules.customers.routes.api.storefront import customer_logout
|
||||
|
||||
source = inspect.getsource(customer_logout)
|
||||
assert 'path="/"' in source or "path='/'" in source, (
|
||||
"customer_logout must delete cookie with path='/' to match how it was set"
|
||||
)
|
||||
@@ -29,7 +29,9 @@ class TestFrontendTypeMiddleware:
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/admin/dashboard")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/admin/dashboard", store=None)
|
||||
request.state = Mock(spec=[])
|
||||
request.state.platform_clean_path = "/admin/dashboard"
|
||||
request.state.store = None
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
@@ -47,7 +49,9 @@ class TestFrontendTypeMiddleware:
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/store/settings")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/store/settings", store=None)
|
||||
request.state = Mock(spec=[])
|
||||
request.state.platform_clean_path = "/store/settings"
|
||||
request.state.store = None
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
@@ -64,7 +68,9 @@ class TestFrontendTypeMiddleware:
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/storefront/products")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/storefront/products", store=None)
|
||||
request.state = Mock(spec=[])
|
||||
request.state.platform_clean_path = "/storefront/products"
|
||||
request.state.store = None
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
@@ -83,7 +89,9 @@ class TestFrontendTypeMiddleware:
|
||||
request.headers = {"host": "orion.omsflow.lu"}
|
||||
mock_store = Mock()
|
||||
mock_store.name = "Test Store"
|
||||
request.state = Mock(clean_path="/products", store=mock_store)
|
||||
request.state = Mock(spec=[])
|
||||
request.state.platform_clean_path = "/products"
|
||||
request.state.store = mock_store
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
@@ -100,7 +108,9 @@ class TestFrontendTypeMiddleware:
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/pricing")
|
||||
request.headers = {"host": "localhost"}
|
||||
request.state = Mock(clean_path="/pricing", store=None)
|
||||
request.state = Mock(spec=[])
|
||||
request.state.platform_clean_path = "/pricing"
|
||||
request.state.store = None
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
@@ -127,21 +137,23 @@ class TestFrontendTypeMiddleware:
|
||||
assert response is expected_response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_uses_clean_path_when_available(self):
|
||||
"""Test middleware uses clean_path when available."""
|
||||
async def test_middleware_uses_platform_clean_path_when_available(self):
|
||||
"""Test middleware uses platform_clean_path when available."""
|
||||
middleware = FrontendTypeMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path="/stores/orion/store/settings")
|
||||
request.url = Mock(path="/platforms/main/store/settings")
|
||||
request.headers = {"host": "localhost"}
|
||||
# clean_path shows the rewritten path
|
||||
request.state = Mock(clean_path="/store/settings", store=None)
|
||||
# platform_clean_path shows the path with platform prefix stripped
|
||||
request.state = Mock(spec=[])
|
||||
request.state.platform_clean_path = "/store/settings"
|
||||
request.state.store = None
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
# Should detect as STORE based on clean_path
|
||||
# Should detect as STORE based on platform_clean_path
|
||||
assert request.state.frontend_type == FrontendType.STORE
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -132,6 +132,7 @@ class TestLanguageDispatchStore:
|
||||
mock_resolve.assert_called_once_with(
|
||||
user_preferred="de",
|
||||
store_dashboard="fr",
|
||||
cookie_language=None,
|
||||
)
|
||||
assert request.state.language == "de"
|
||||
|
||||
@@ -154,6 +155,7 @@ class TestLanguageDispatchStore:
|
||||
mock_resolve.assert_called_once_with(
|
||||
user_preferred=None,
|
||||
store_dashboard=None,
|
||||
cookie_language=None,
|
||||
)
|
||||
|
||||
|
||||
@@ -356,6 +358,7 @@ class TestLanguageHelpers:
|
||||
max_age=60 * 60 * 24 * 365,
|
||||
httponly=False,
|
||||
samesite="lax",
|
||||
path="/",
|
||||
)
|
||||
assert result is response
|
||||
|
||||
@@ -372,7 +375,7 @@ class TestLanguageHelpers:
|
||||
response = Mock(spec=Response)
|
||||
result = delete_language_cookie(response)
|
||||
|
||||
response.delete_cookie.assert_called_once_with(key="lang")
|
||||
response.delete_cookie.assert_called_once_with(key="lang", path="/")
|
||||
assert result is response
|
||||
|
||||
def test_get_user_language_from_token_with_pref(self):
|
||||
|
||||
@@ -995,3 +995,88 @@ class TestURLRoutingSummary:
|
||||
assert context["detection_method"] == "domain"
|
||||
assert context["domain"] == "omsflow.lu"
|
||||
# clean_path not set for domain detection - uses original path
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.middleware
|
||||
class TestExtractPlatformFromReferer:
|
||||
"""Tests for Referer-based platform detection for storefront API requests."""
|
||||
|
||||
def test_extract_platform_from_referer_with_platforms_prefix(self):
|
||||
"""Extract platform code from Referer with /platforms/{code}/ path."""
|
||||
middleware = PlatformContextMiddleware(app=None)
|
||||
result = middleware._extract_platform_from_referer(
|
||||
"http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/login"
|
||||
)
|
||||
assert result is not None
|
||||
assert result["path_prefix"] == "loyalty"
|
||||
assert result["detection_method"] == "path"
|
||||
|
||||
def test_extract_platform_from_referer_no_platforms_prefix(self):
|
||||
"""Referer without /platforms/ returns None."""
|
||||
middleware = PlatformContextMiddleware(app=None)
|
||||
result = middleware._extract_platform_from_referer(
|
||||
"http://localhost:8000/storefront/FASHIONHUB/products"
|
||||
)
|
||||
assert result is None
|
||||
|
||||
def test_extract_platform_from_referer_empty_string(self):
|
||||
"""Empty referer returns None."""
|
||||
middleware = PlatformContextMiddleware(app=None)
|
||||
result = middleware._extract_platform_from_referer("")
|
||||
assert result is None
|
||||
|
||||
def test_extract_platform_from_referer_oms_platform(self):
|
||||
"""Extract OMS platform from Referer."""
|
||||
middleware = PlatformContextMiddleware(app=None)
|
||||
result = middleware._extract_platform_from_referer(
|
||||
"http://localhost:8000/platforms/oms/store/WIZATECH/dashboard"
|
||||
)
|
||||
assert result is not None
|
||||
assert result["path_prefix"] == "oms"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.middleware
|
||||
class TestPlatformContextMiddlewareReferer:
|
||||
"""Test PlatformContextMiddleware __call__ with Referer-based platform detection."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_storefront_api_uses_referer_platform(self):
|
||||
"""Test storefront API requests on localhost extract platform from Referer."""
|
||||
mock_app = AsyncMock()
|
||||
middleware = PlatformContextMiddleware(app=mock_app)
|
||||
|
||||
mock_platform = Mock()
|
||||
mock_platform.id = 3
|
||||
mock_platform.code = "loyalty"
|
||||
mock_platform.name = "Loyalty Platform"
|
||||
|
||||
scope = {
|
||||
"type": "http",
|
||||
"path": "/api/v1/storefront/auth/login",
|
||||
"headers": [
|
||||
(b"host", b"localhost:8000"),
|
||||
(b"referer", b"http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/login"),
|
||||
],
|
||||
}
|
||||
|
||||
receive = AsyncMock()
|
||||
send = AsyncMock()
|
||||
|
||||
mock_db = MagicMock()
|
||||
|
||||
with patch(
|
||||
"middleware.platform_context.get_db", return_value=iter([mock_db])
|
||||
), patch.object(
|
||||
PlatformContextManager,
|
||||
"get_platform_from_context",
|
||||
return_value=mock_platform,
|
||||
):
|
||||
await middleware(scope, receive, send)
|
||||
|
||||
# Platform should be detected from Referer
|
||||
assert scope["state"]["platform"] is mock_platform
|
||||
assert scope["state"]["platform_context"]["path_prefix"] == "loyalty"
|
||||
# API path should NOT be rewritten to the Referer's path
|
||||
assert scope["path"] == "/api/v1/storefront/auth/login"
|
||||
|
||||
@@ -733,6 +733,115 @@ class TestStoreContextMiddleware:
|
||||
assert request.state.clean_path == "/random/path"
|
||||
call_next.assert_called_once_with(request)
|
||||
|
||||
# ========================================================================
|
||||
# Storefront API Referer Tests
|
||||
# ========================================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_storefront_api_uses_referer(self):
|
||||
"""Test storefront API requests get store context from Referer header."""
|
||||
middleware = StoreContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.headers = {
|
||||
"host": "localhost",
|
||||
"referer": "http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/login",
|
||||
}
|
||||
request.url = Mock(path="/api/v1/storefront/auth/login")
|
||||
request.state = Mock()
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
mock_store = Mock()
|
||||
mock_store.id = 4
|
||||
mock_store.name = "Fashion Hub"
|
||||
mock_store.subdomain = "fashionhub"
|
||||
|
||||
referer_context = {
|
||||
"subdomain": "FASHIONHUB",
|
||||
"detection_method": "path",
|
||||
"path_prefix": "/storefront/FASHIONHUB",
|
||||
"full_prefix": "/storefront/",
|
||||
"host": "localhost",
|
||||
"referer": "http://localhost:8000/platforms/loyalty/storefront/FASHIONHUB/account/login",
|
||||
}
|
||||
|
||||
mock_db = MagicMock()
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
StoreContextManager,
|
||||
"is_api_request",
|
||||
return_value=True,
|
||||
),
|
||||
patch.object(
|
||||
StoreContextManager,
|
||||
"extract_store_from_referer",
|
||||
return_value=referer_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 is referer_context
|
||||
call_next.assert_called_once_with(request)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_storefront_api_no_referer(self):
|
||||
"""Test storefront API request without Referer header sets store to None."""
|
||||
middleware = StoreContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.headers = {"host": "localhost"}
|
||||
request.url = Mock(path="/api/v1/storefront/auth/login")
|
||||
request.state = Mock()
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
StoreContextManager,
|
||||
"is_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
|
||||
call_next.assert_called_once_with(request)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_middleware_non_storefront_api_still_skips(self):
|
||||
"""Test non-storefront API requests still skip store detection."""
|
||||
middleware = StoreContextMiddleware(app=None)
|
||||
|
||||
request = Mock(spec=Request)
|
||||
request.headers = {"host": "localhost"}
|
||||
request.url = Mock(path="/api/v1/admin/stores")
|
||||
request.state = Mock()
|
||||
|
||||
call_next = AsyncMock(return_value=Mock())
|
||||
|
||||
with patch.object(
|
||||
StoreContextManager, "is_api_request", return_value=True
|
||||
):
|
||||
await middleware.dispatch(request, call_next)
|
||||
|
||||
assert request.state.store is None
|
||||
call_next.assert_called_once_with(request)
|
||||
|
||||
# ========================================================================
|
||||
# System Path Skipping Tests
|
||||
# ========================================================================
|
||||
|
||||
125
tests/unit/test_exception_handler.py
Normal file
125
tests/unit/test_exception_handler.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# tests/unit/test_exception_handler.py
|
||||
"""
|
||||
Unit tests for exception handler login redirect.
|
||||
|
||||
Tests the _redirect_to_login function to ensure correct URL generation
|
||||
for storefront contexts, preventing double 'storefront/' in redirects.
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from fastapi import Request
|
||||
|
||||
from app.exceptions.handler import _redirect_to_login
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
|
||||
def _make_request(
|
||||
path="/storefront/FASHIONHUB/account/dashboard",
|
||||
frontend_type=FrontendType.STOREFRONT,
|
||||
store=None,
|
||||
store_context=None,
|
||||
platform=None,
|
||||
platform_original_path=None,
|
||||
):
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path=path)
|
||||
request.method = "GET"
|
||||
request.headers = {"accept": "text/html"}
|
||||
request.state = Mock()
|
||||
request.state.frontend_type = frontend_type
|
||||
request.state.store = store
|
||||
request.state.store_context = store_context
|
||||
request.state.platform = platform
|
||||
request.state.platform_original_path = platform_original_path
|
||||
return request
|
||||
|
||||
|
||||
def _make_store(store_code="FASHIONHUB", subdomain="fashionhub"):
|
||||
store = Mock()
|
||||
store.store_code = store_code
|
||||
store.subdomain = subdomain
|
||||
return store
|
||||
|
||||
|
||||
def _make_platform(code="loyalty"):
|
||||
platform = Mock()
|
||||
platform.code = code
|
||||
return platform
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestRedirectToLogin:
|
||||
"""Tests for _redirect_to_login storefront URL generation."""
|
||||
|
||||
def test_storefront_redirect_dev_mode_no_double_storefront(self):
|
||||
"""Dev mode redirect should not have double 'storefront/' in URL."""
|
||||
store = _make_store(store_code="FASHIONHUB")
|
||||
platform = _make_platform(code="loyalty")
|
||||
store_context = {"detection_method": "path", "full_prefix": "/storefront/"}
|
||||
|
||||
request = _make_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
platform=platform,
|
||||
platform_original_path="/platforms/loyalty/storefront/FASHIONHUB/account/dashboard",
|
||||
)
|
||||
|
||||
response = _redirect_to_login(request)
|
||||
|
||||
assert response.status_code == 302
|
||||
location = response.headers["location"]
|
||||
assert location == "/platforms/loyalty/storefront/FASHIONHUB/account/login"
|
||||
assert "/storefront/storefront/" not in location
|
||||
|
||||
def test_storefront_redirect_prod_mode(self):
|
||||
"""Prod mode redirect should use /account/login."""
|
||||
store = _make_store()
|
||||
store_context = {"detection_method": "subdomain", "subdomain": "fashionhub"}
|
||||
|
||||
request = _make_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
)
|
||||
|
||||
response = _redirect_to_login(request)
|
||||
|
||||
assert response.status_code == 302
|
||||
location = response.headers["location"]
|
||||
assert location == "/account/login"
|
||||
|
||||
def test_storefront_redirect_uses_store_code_not_subdomain(self):
|
||||
"""Redirect URL should use store_code (uppercase), not subdomain (lowercase)."""
|
||||
store = _make_store(store_code="FASHIONHUB", subdomain="fashionhub")
|
||||
platform = _make_platform(code="loyalty")
|
||||
store_context = {"detection_method": "path", "full_prefix": "/storefront/"}
|
||||
|
||||
request = _make_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
platform=platform,
|
||||
platform_original_path="/platforms/loyalty/storefront/FASHIONHUB/dashboard",
|
||||
)
|
||||
|
||||
response = _redirect_to_login(request)
|
||||
|
||||
location = response.headers["location"]
|
||||
assert "FASHIONHUB" in location
|
||||
assert "fashionhub" not in location
|
||||
|
||||
def test_admin_redirect(self):
|
||||
"""Admin requests redirect to /admin/login."""
|
||||
request = _make_request(frontend_type=FrontendType.ADMIN)
|
||||
|
||||
response = _redirect_to_login(request)
|
||||
|
||||
assert response.headers["location"] == "/admin/login"
|
||||
|
||||
def test_merchant_redirect(self):
|
||||
"""Merchant requests redirect to /merchants/login."""
|
||||
request = _make_request(frontend_type=FrontendType.MERCHANT)
|
||||
|
||||
response = _redirect_to_login(request)
|
||||
|
||||
assert response.headers["location"] == "/merchants/login"
|
||||
146
tests/unit/utils/test_page_context.py
Normal file
146
tests/unit/utils/test_page_context.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# tests/unit/utils/test_page_context.py
|
||||
"""
|
||||
Unit tests for page context utilities.
|
||||
|
||||
Tests cover:
|
||||
- base_url calculation for storefront pages
|
||||
- Dev mode (path-based) with platform prefix
|
||||
- Prod mode (subdomain/custom domain)
|
||||
- Ensures no double 'storefront/' in URLs
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from fastapi import Request
|
||||
|
||||
from app.modules.core.utils.page_context import get_storefront_context
|
||||
|
||||
|
||||
def _make_storefront_request(
|
||||
path="/storefront/TESTSHOP/products",
|
||||
store=None,
|
||||
store_context=None,
|
||||
platform=None,
|
||||
platform_original_path=None,
|
||||
theme=None,
|
||||
clean_path="/products",
|
||||
language="en",
|
||||
):
|
||||
"""Create a mock request with storefront state."""
|
||||
request = Mock(spec=Request)
|
||||
request.url = Mock(path=path)
|
||||
request.method = "GET"
|
||||
request.headers = {"host": "localhost:8000"}
|
||||
request.state = Mock()
|
||||
request.state.store = store
|
||||
request.state.store_context = store_context
|
||||
request.state.clean_path = clean_path
|
||||
request.state.platform = platform
|
||||
request.state.platform_original_path = platform_original_path
|
||||
request.state.theme = theme
|
||||
request.state.language = language
|
||||
request.state.subscription = None
|
||||
request.state.subscription_tier = None
|
||||
request.state.frontend_type = None
|
||||
return request
|
||||
|
||||
|
||||
def _make_store(store_code="TESTSHOP", subdomain="testshop"):
|
||||
store = Mock()
|
||||
store.store_code = store_code
|
||||
store.subdomain = subdomain
|
||||
store.id = 1
|
||||
store.name = "Test Shop"
|
||||
store.merchant_id = 1
|
||||
return store
|
||||
|
||||
|
||||
def _make_platform(code="loyalty", platform_id=3):
|
||||
platform = Mock()
|
||||
platform.code = code
|
||||
platform.id = platform_id
|
||||
return platform
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestBaseUrlCalculation:
|
||||
"""Tests for base_url in storefront context -- prevents double 'storefront/' regression."""
|
||||
|
||||
def test_base_url_dev_mode_with_platform_prefix(self):
|
||||
"""Dev mode: base_url includes /platforms/{code}/storefront/{store_code}/."""
|
||||
store = _make_store(store_code="FASHIONHUB")
|
||||
platform = _make_platform(code="loyalty")
|
||||
store_context = {"detection_method": "path", "full_prefix": "/storefront/"}
|
||||
|
||||
request = _make_storefront_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
platform=platform,
|
||||
platform_original_path="/platforms/loyalty/storefront/FASHIONHUB/products",
|
||||
)
|
||||
|
||||
# Mock the module-level context building to avoid DB calls
|
||||
# We only need to test the base_url logic, so patch what's needed
|
||||
context = get_storefront_context(request)
|
||||
|
||||
assert context["base_url"] == "/platforms/loyalty/storefront/FASHIONHUB/"
|
||||
# Verify no double storefront when building links
|
||||
assert "/storefront/storefront/" not in context["base_url"]
|
||||
|
||||
def test_base_url_dev_mode_without_platform_prefix(self):
|
||||
"""Dev mode without /platforms/ prefix: base_url = /storefront/{store_code}/."""
|
||||
store = _make_store(store_code="TESTSHOP")
|
||||
store_context = {"detection_method": "path", "full_prefix": "/storefront/"}
|
||||
|
||||
request = _make_storefront_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
platform=None,
|
||||
platform_original_path=None,
|
||||
)
|
||||
|
||||
context = get_storefront_context(request)
|
||||
|
||||
assert context["base_url"] == "/storefront/TESTSHOP/"
|
||||
assert "/storefront/storefront/" not in context["base_url"]
|
||||
|
||||
def test_base_url_prod_mode_subdomain(self):
|
||||
"""Prod mode with subdomain: base_url = /."""
|
||||
store = _make_store()
|
||||
store_context = {"detection_method": "subdomain", "subdomain": "testshop"}
|
||||
|
||||
request = _make_storefront_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
)
|
||||
|
||||
context = get_storefront_context(request)
|
||||
|
||||
assert context["base_url"] == "/"
|
||||
|
||||
def test_base_url_no_store_context(self):
|
||||
"""No store context: base_url = /."""
|
||||
request = _make_storefront_request(store=None, store_context=None)
|
||||
|
||||
context = get_storefront_context(request)
|
||||
|
||||
assert context["base_url"] == "/"
|
||||
|
||||
def test_base_url_uses_store_code_not_subdomain(self):
|
||||
"""base_url should use store_code (uppercase) not subdomain (lowercase)."""
|
||||
store = _make_store(store_code="FASHIONHUB", subdomain="fashionhub")
|
||||
platform = _make_platform(code="loyalty")
|
||||
store_context = {"detection_method": "path", "full_prefix": "/storefront/"}
|
||||
|
||||
request = _make_storefront_request(
|
||||
store=store,
|
||||
store_context=store_context,
|
||||
platform=platform,
|
||||
platform_original_path="/platforms/loyalty/storefront/FASHIONHUB/products",
|
||||
)
|
||||
|
||||
context = get_storefront_context(request)
|
||||
|
||||
assert "FASHIONHUB" in context["base_url"]
|
||||
assert "fashionhub" not in context["base_url"]
|
||||
Reference in New Issue
Block a user