Files
orion/tests/unit/middleware/test_language.py
Samir Boulahtit 6c5969e4e1 test: add 42 unit tests for middleware/cloudflare.py and middleware/language.py
Completes middleware test coverage (11/11 files) with 19 cloudflare tests
(dispatch, get_real_client_ip, get_client_country) and 23 language tests
(admin/store/storefront/platform dispatch, helpers, private methods).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 21:55:47 +01:00

492 lines
18 KiB
Python

# tests/unit/middleware/test_language.py
"""
Unit tests for LanguageMiddleware, set_language_cookie, and delete_language_cookie.
Tests cover:
- Admin frontend: user pref used, falls back to "en"
- Store dashboard: calls resolve_store_dashboard_language, no-store fallback
- Storefront: calls resolve_storefront_language, no-store fallback
- General: platform frontend, fallback/unknown type, unsupported language
validation, request.state.language set, request.state.language_info populated,
cookie language read
- Helpers: _get_user_language_from_token, _get_customer_language_from_token,
set_language_cookie (valid/invalid), delete_language_cookie
- Private methods: user with pref, user without pref, no user, customer with pref
"""
from unittest.mock import AsyncMock, Mock, patch
import pytest
from starlette.requests import Request
from starlette.responses import Response
from app.modules.enums import FrontendType
from middleware.language import (
LanguageMiddleware,
delete_language_cookie,
set_language_cookie,
)
def _make_request(
frontend_type=None,
store=None,
cookies=None,
headers=None,
current_user=None,
current_customer=None,
):
"""Build a mock Request with the given state attributes."""
request = Mock(spec=Request)
request.state = Mock(spec=[])
request.cookies = cookies or {}
request.headers = headers or {}
if frontend_type is not None:
request.state.frontend_type = frontend_type
if store is not None:
request.state.store = store
if current_user is not None:
request.state.current_user = current_user
if current_customer is not None:
request.state.current_customer = current_customer
return request
def _make_call_next():
"""Return (call_next, response) pair."""
response = Mock(status_code=200, headers={})
call_next = AsyncMock(return_value=response)
return call_next, response
# ---------------------------------------------------------------------------
# TestLanguageDispatchAdmin
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestLanguageDispatchAdmin:
"""Admin frontend language resolution."""
@pytest.mark.asyncio
async def test_admin_user_preferred_language_used(self):
"""Admin: user's preferred_language is used when set."""
middleware = LanguageMiddleware(app=None)
user = Mock()
user.preferred_language = "de"
request = _make_request(
frontend_type=FrontendType.ADMIN,
current_user=user,
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "de"
@pytest.mark.asyncio
async def test_admin_falls_back_to_en(self):
"""Admin: falls back to 'en' when no user preference."""
middleware = LanguageMiddleware(app=None)
request = _make_request(frontend_type=FrontendType.ADMIN)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "en"
# ---------------------------------------------------------------------------
# TestLanguageDispatchStore
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestLanguageDispatchStore:
"""Store dashboard language resolution."""
@pytest.mark.asyncio
async def test_store_calls_resolve_store_dashboard_language(self):
"""Store: resolve_store_dashboard_language is called with correct args."""
middleware = LanguageMiddleware(app=None)
user = Mock()
user.preferred_language = "de"
store = Mock()
store.dashboard_language = "fr"
request = _make_request(
frontend_type=FrontendType.STORE,
store=store,
current_user=user,
)
call_next, _ = _make_call_next()
with (
patch("middleware.language.parse_accept_language", return_value=None),
patch(
"middleware.language.resolve_store_dashboard_language",
return_value="de",
) as mock_resolve,
):
await middleware.dispatch(request, call_next)
mock_resolve.assert_called_once_with(
user_preferred="de",
store_dashboard="fr",
)
assert request.state.language == "de"
@pytest.mark.asyncio
async def test_store_no_store_passes_none_dashboard(self):
"""Store: store_dashboard is None when request.state has no store."""
middleware = LanguageMiddleware(app=None)
request = _make_request(frontend_type=FrontendType.STORE)
call_next, _ = _make_call_next()
with (
patch("middleware.language.parse_accept_language", return_value=None),
patch(
"middleware.language.resolve_store_dashboard_language",
return_value="fr",
) as mock_resolve,
):
await middleware.dispatch(request, call_next)
mock_resolve.assert_called_once_with(
user_preferred=None,
store_dashboard=None,
)
# ---------------------------------------------------------------------------
# TestLanguageDispatchStorefront
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestLanguageDispatchStorefront:
"""Storefront language resolution."""
@pytest.mark.asyncio
async def test_storefront_calls_resolve_storefront_language(self):
"""Storefront: resolve_storefront_language called with correct args."""
middleware = LanguageMiddleware(app=None)
customer = Mock()
customer.preferred_language = "lb"
store = Mock()
store.storefront_language = "fr"
store.storefront_languages = ["fr", "de", "lb"]
request = _make_request(
frontend_type=FrontendType.STOREFRONT,
store=store,
current_customer=customer,
cookies={"lang": "de"},
headers={"accept-language": "en-US,en;q=0.9"},
)
call_next, _ = _make_call_next()
with (
patch(
"middleware.language.parse_accept_language", return_value="en"
),
patch(
"middleware.language.resolve_storefront_language",
return_value="lb",
) as mock_resolve,
):
await middleware.dispatch(request, call_next)
mock_resolve.assert_called_once_with(
customer_preferred="lb",
session_language="de",
store_storefront="fr",
browser_language="en",
enabled_languages=["fr", "de", "lb"],
)
assert request.state.language == "lb"
@pytest.mark.asyncio
async def test_storefront_no_store_passes_none(self):
"""Storefront: store fields are None when no store in state."""
middleware = LanguageMiddleware(app=None)
request = _make_request(frontend_type=FrontendType.STOREFRONT)
call_next, _ = _make_call_next()
with (
patch("middleware.language.parse_accept_language", return_value=None),
patch(
"middleware.language.resolve_storefront_language",
return_value="fr",
) as mock_resolve,
):
await middleware.dispatch(request, call_next)
mock_resolve.assert_called_once_with(
customer_preferred=None,
session_language=None,
store_storefront=None,
browser_language=None,
enabled_languages=None,
)
# ---------------------------------------------------------------------------
# TestLanguageDispatchGeneral
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestLanguageDispatchGeneral:
"""Platform, fallback/unknown, validation, state, and cookie tests."""
@pytest.mark.asyncio
async def test_platform_uses_cookie_language(self):
"""Platform: cookie language is used first."""
middleware = LanguageMiddleware(app=None)
request = _make_request(
frontend_type=FrontendType.PLATFORM,
cookies={"lang": "de"},
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value="en"):
await middleware.dispatch(request, call_next)
assert request.state.language == "de"
@pytest.mark.asyncio
async def test_platform_falls_back_to_browser_language(self):
"""Platform: browser language used when no cookie."""
middleware = LanguageMiddleware(app=None)
request = _make_request(frontend_type=FrontendType.PLATFORM)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value="de"):
await middleware.dispatch(request, call_next)
assert request.state.language == "de"
@pytest.mark.asyncio
async def test_fallback_unknown_frontend_type(self):
"""Unknown/None frontend type uses cookie → browser → default."""
middleware = LanguageMiddleware(app=None)
request = _make_request() # no frontend_type
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
# DEFAULT_LANGUAGE is "fr"
assert request.state.language == "fr"
@pytest.mark.asyncio
async def test_unsupported_language_falls_back_to_default(self):
"""Unsupported language code is replaced by DEFAULT_LANGUAGE."""
middleware = LanguageMiddleware(app=None)
request = _make_request(
frontend_type=FrontendType.PLATFORM,
cookies={"lang": "xx"}, # unsupported
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "fr"
@pytest.mark.asyncio
async def test_request_state_language_is_set(self):
"""request.state.language is always set after dispatch."""
middleware = LanguageMiddleware(app=None)
request = _make_request(frontend_type=FrontendType.PLATFORM)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value="en"):
await middleware.dispatch(request, call_next)
assert hasattr(request.state, "language")
assert request.state.language == "en"
@pytest.mark.asyncio
async def test_request_state_language_info_populated(self):
"""request.state.language_info contains code, cookie, browser, frontend_type."""
middleware = LanguageMiddleware(app=None)
request = _make_request(
frontend_type=FrontendType.PLATFORM,
cookies={"lang": "en"},
headers={"accept-language": "de"},
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value="de"):
await middleware.dispatch(request, call_next)
info = request.state.language_info
assert info["code"] == "en"
assert info["cookie"] == "en"
assert info["browser"] == "de"
assert info["frontend_type"] == "platform"
@pytest.mark.asyncio
async def test_cookie_language_read_from_request(self):
"""The 'lang' cookie is read and considered in resolution."""
middleware = LanguageMiddleware(app=None)
request = _make_request(
frontend_type=FrontendType.PLATFORM,
cookies={"lang": "lb"},
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "lb"
# ---------------------------------------------------------------------------
# TestLanguageHelpers
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestLanguageHelpers:
"""Tests for set_language_cookie and delete_language_cookie."""
def test_set_language_cookie_valid(self):
"""set_language_cookie sets cookie for supported language."""
response = Mock(spec=Response)
result = set_language_cookie(response, "de")
response.set_cookie.assert_called_once_with(
key="lang",
value="de",
max_age=60 * 60 * 24 * 365,
httponly=False,
samesite="lax",
)
assert result is response
def test_set_language_cookie_invalid_language(self):
"""set_language_cookie does NOT call set_cookie for unsupported lang."""
response = Mock(spec=Response)
result = set_language_cookie(response, "xx")
response.set_cookie.assert_not_called()
assert result is response
def test_delete_language_cookie(self):
"""delete_language_cookie calls delete_cookie with correct key."""
response = Mock(spec=Response)
result = delete_language_cookie(response)
response.delete_cookie.assert_called_once_with(key="lang")
assert result is response
def test_get_user_language_from_token_with_pref(self):
"""_get_user_language_from_token returns preferred_language."""
middleware = LanguageMiddleware(app=None)
request = Mock(spec=Request)
request.state = Mock()
request.state.current_user = Mock()
request.state.current_user.preferred_language = "de"
assert middleware._get_user_language_from_token(request) == "de"
def test_get_user_language_from_token_no_user(self):
"""_get_user_language_from_token returns None with no user."""
middleware = LanguageMiddleware(app=None)
request = Mock(spec=Request)
request.state = Mock(spec=[]) # no current_user
assert middleware._get_user_language_from_token(request) is None
def test_get_customer_language_from_token_with_pref(self):
"""_get_customer_language_from_token returns preferred_language."""
middleware = LanguageMiddleware(app=None)
request = Mock(spec=Request)
request.state = Mock()
request.state.current_customer = Mock()
request.state.current_customer.preferred_language = "lb"
assert middleware._get_customer_language_from_token(request) == "lb"
# ---------------------------------------------------------------------------
# TestLanguageDispatchPrivateMethods
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestLanguageDispatchPrivateMethods:
"""Integration-style tests exercising private helpers through dispatch."""
@pytest.mark.asyncio
async def test_admin_user_with_preferred_language(self):
"""Admin + user with pref → uses that preference."""
middleware = LanguageMiddleware(app=None)
user = Mock()
user.preferred_language = "lb"
request = _make_request(
frontend_type=FrontendType.ADMIN,
current_user=user,
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "lb"
@pytest.mark.asyncio
async def test_admin_user_without_preferred_language(self):
"""Admin + user without preferred_language → falls back to 'en'."""
middleware = LanguageMiddleware(app=None)
user = Mock(spec=[]) # no preferred_language attr
request = _make_request(
frontend_type=FrontendType.ADMIN,
current_user=user,
)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "en"
@pytest.mark.asyncio
async def test_admin_no_user_at_all(self):
"""Admin + no user at all → falls back to 'en'."""
middleware = LanguageMiddleware(app=None)
request = _make_request(frontend_type=FrontendType.ADMIN)
call_next, _ = _make_call_next()
with patch("middleware.language.parse_accept_language", return_value=None):
await middleware.dispatch(request, call_next)
assert request.state.language == "en"
@pytest.mark.asyncio
async def test_storefront_customer_with_preferred_language(self):
"""Storefront + customer with pref → passed to resolve function."""
middleware = LanguageMiddleware(app=None)
customer = Mock()
customer.preferred_language = "de"
store = Mock()
store.storefront_language = "fr"
store.storefront_languages = ["fr", "de"]
request = _make_request(
frontend_type=FrontendType.STOREFRONT,
store=store,
current_customer=customer,
)
call_next, _ = _make_call_next()
with (
patch("middleware.language.parse_accept_language", return_value=None),
patch(
"middleware.language.resolve_storefront_language",
return_value="de",
) as mock_resolve,
):
await middleware.dispatch(request, call_next)
mock_resolve.assert_called_once_with(
customer_preferred="de",
session_language=None,
store_storefront="fr",
browser_language=None,
enabled_languages=["fr", "de"],
)
assert request.state.language == "de"