settings: PATCH /api/settings/language with ACTIVE_LANGUAGES gate
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1ea71bc160
commit
f4025e3cbb
2 changed files with 97 additions and 0 deletions
|
|
@ -21,6 +21,7 @@ import httpx
|
|||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.auth import require_token, maybe_current_user, CurrentUser
|
||||
from app.services.i18n import ACTIVE_LANGUAGES
|
||||
from app.config import get_settings
|
||||
from app.db import get_session, utcnow
|
||||
from app.services.openrouter import (
|
||||
|
|
@ -895,3 +896,38 @@ async def patch_digest_prefs(
|
|||
user.digest_tone = payload.tone
|
||||
await session.commit()
|
||||
return DigestPrefsOut(opt_in=payload.opt_in, tone=payload.tone)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Settings — language preference
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class LanguagePrefsIn(BaseModel):
|
||||
lang: str
|
||||
|
||||
|
||||
class LanguagePrefsOut(BaseModel):
|
||||
lang: str
|
||||
|
||||
|
||||
@router.patch("/settings/language", response_model=LanguagePrefsOut)
|
||||
async def patch_language_prefs(
|
||||
payload: LanguagePrefsIn,
|
||||
principal: CurrentUser = Depends(require_token),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
) -> LanguagePrefsOut:
|
||||
if principal.user is None:
|
||||
raise HTTPException(status_code=400, detail="no_user_context")
|
||||
lang = (payload.lang or "").strip().lower()
|
||||
if lang not in ACTIVE_LANGUAGES:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"unsupported language: {payload.lang!r}",
|
||||
)
|
||||
user = await session.get(User, principal.user.id)
|
||||
if user is None:
|
||||
raise HTTPException(status_code=404, detail="user_not_found")
|
||||
user.lang = lang
|
||||
await session.commit()
|
||||
return LanguagePrefsOut(lang=lang)
|
||||
|
|
|
|||
|
|
@ -395,3 +395,64 @@ async def test_log_endpoint_falls_back_to_english_when_no_translation(tmp_path):
|
|||
user = await session.get(User, 11)
|
||||
content = await _resolve_log_content(session, log_id, user.lang)
|
||||
assert "Open" in content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_language_accepts_active(tmp_path):
|
||||
"""PATCH /api/settings/language accepts 'en' and 'it' and persists."""
|
||||
from app.models import User
|
||||
from app.routers.api import patch_language_prefs, LanguagePrefsIn
|
||||
|
||||
_, factory, setup = _build_session_factory(tmp_path)
|
||||
await setup()
|
||||
|
||||
async with factory() as session:
|
||||
session.add(User(id=20, email="u@x", tier="paid", lang="en"))
|
||||
await session.commit()
|
||||
|
||||
class _P:
|
||||
is_admin = False
|
||||
def __init__(self, u): self.user = u
|
||||
|
||||
async with factory() as session:
|
||||
user = await session.get(User, 20)
|
||||
result = await patch_language_prefs(
|
||||
payload=LanguagePrefsIn(lang="it"),
|
||||
principal=_P(user),
|
||||
session=session,
|
||||
)
|
||||
assert result.lang == "it"
|
||||
|
||||
async with factory() as session:
|
||||
user = await session.get(User, 20)
|
||||
assert user.lang == "it"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_language_rejects_wip(tmp_path):
|
||||
"""PATCH rejects 'es'/'fr'/'de'/'xx' with 400 — ACTIVE_LANGUAGES gate."""
|
||||
from fastapi import HTTPException
|
||||
from app.models import User
|
||||
from app.routers.api import patch_language_prefs, LanguagePrefsIn
|
||||
|
||||
_, factory, setup = _build_session_factory(tmp_path)
|
||||
await setup()
|
||||
|
||||
async with factory() as session:
|
||||
session.add(User(id=21, email="u2@x", tier="paid", lang="en"))
|
||||
await session.commit()
|
||||
|
||||
class _P:
|
||||
is_admin = False
|
||||
def __init__(self, u): self.user = u
|
||||
|
||||
for bad in ("es", "fr", "de", "xx"):
|
||||
async with factory() as session:
|
||||
user = await session.get(User, 21)
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
await patch_language_prefs(
|
||||
payload=LanguagePrefsIn(lang=bad),
|
||||
principal=_P(user),
|
||||
session=session,
|
||||
)
|
||||
assert exc.value.status_code == 400
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue