- Add users.language_preference (Alembic 0018, default zh); capture at signup/SMS only; expose on auth and profile APIs - Lite English prompts for chat and memoir; localized stage labels and agent names (Life Echo / 岁月知己) - Tencent TTS: language-aware synthesis, ModelType=1 for 501004, English chunking - WebSocket pipeline: emit all AGENT_RESPONSE segments when TTS cancels; INFO logs for tts_this_turn and TTS decisions; on-demand TTS logging - Expo: device language on auth, i18n tiers/agent name, [SPLIT] streaming UX fixes - Tests for migration, prompts, pipeline, router tts_this_turn, reply segments Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""ConversationService.list_for_user 兜底标题随用户语言切换(zh→岁月知己 / en→Life Echo)。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import uuid
|
||
from datetime import datetime, timezone
|
||
from types import SimpleNamespace
|
||
from unittest.mock import AsyncMock, MagicMock
|
||
|
||
import pytest
|
||
|
||
# 预加载所有 feature 模型,触发 SQLAlchemy 关系解析
|
||
from app.features.asset import models as _asset_models # noqa: F401
|
||
from app.features.auth import models as _auth_models # noqa: F401
|
||
from app.features.conversation import models as _conv_models # noqa: F401
|
||
from app.features.memoir import models as _memoir_models # noqa: F401
|
||
from app.features.memory import models as _memory_models # noqa: F401
|
||
from app.features.payment import models as _payment_models # noqa: F401
|
||
from app.features.story import models as _story_models # noqa: F401
|
||
from app.features.user import models as _user_models # noqa: F401
|
||
|
||
from app.features.conversation import service as conv_service_mod
|
||
from app.features.conversation.service import ConversationService
|
||
from app.features.user.models import User
|
||
|
||
|
||
def _make_conv(user_id: str, summary: str | None) -> SimpleNamespace:
|
||
now = datetime.now(timezone.utc)
|
||
return SimpleNamespace(
|
||
id=str(uuid.uuid4()),
|
||
user_id=user_id,
|
||
summary=summary,
|
||
started_at=now,
|
||
last_message_at=now,
|
||
)
|
||
|
||
|
||
def _build_user(language: str) -> User:
|
||
return User(
|
||
id=str(uuid.uuid4()),
|
||
phone=f"138{uuid.uuid4().int % 100_000_000:08d}",
|
||
password_hash="x",
|
||
nickname="t",
|
||
subscription_type="free",
|
||
created_at=datetime.now(timezone.utc),
|
||
language_preference=language,
|
||
)
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_list_for_user_zh_fallback_title(monkeypatch) -> None:
|
||
user = _build_user("zh")
|
||
convs = [_make_conv(user.id, summary=None), _make_conv(user.id, summary="夏日记忆")]
|
||
|
||
db = MagicMock()
|
||
db.get = AsyncMock(return_value=user)
|
||
svc = ConversationService(db, MagicMock())
|
||
|
||
monkeypatch.setattr(
|
||
conv_service_mod.repo,
|
||
"get_user_conversations",
|
||
AsyncMock(return_value=convs),
|
||
)
|
||
monkeypatch.setattr(
|
||
ConversationService,
|
||
"ensure_redis_history_from_db",
|
||
AsyncMock(return_value=[]),
|
||
)
|
||
|
||
rows = await svc.list_for_user(user.id)
|
||
assert rows[0]["title"] == "岁月知己"
|
||
assert rows[1]["title"] == "夏日记忆"
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_list_for_user_en_fallback_title(monkeypatch) -> None:
|
||
user = _build_user("en")
|
||
convs = [_make_conv(user.id, summary=None)]
|
||
|
||
db = MagicMock()
|
||
db.get = AsyncMock(return_value=user)
|
||
svc = ConversationService(db, MagicMock())
|
||
|
||
monkeypatch.setattr(
|
||
conv_service_mod.repo,
|
||
"get_user_conversations",
|
||
AsyncMock(return_value=convs),
|
||
)
|
||
monkeypatch.setattr(
|
||
ConversationService,
|
||
"ensure_redis_history_from_db",
|
||
AsyncMock(return_value=[]),
|
||
)
|
||
|
||
rows = await svc.list_for_user(user.id)
|
||
assert rows[0]["title"] == "Life Echo"
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_list_for_user_missing_user_falls_back_to_zh(monkeypatch) -> None:
|
||
"""安全兜底:如果 DB 查不到 user 行(极端情况),用 zh 默认。"""
|
||
convs = [_make_conv("uid", summary=None)]
|
||
|
||
db = MagicMock()
|
||
db.get = AsyncMock(return_value=None)
|
||
svc = ConversationService(db, MagicMock())
|
||
|
||
monkeypatch.setattr(
|
||
conv_service_mod.repo,
|
||
"get_user_conversations",
|
||
AsyncMock(return_value=convs),
|
||
)
|
||
monkeypatch.setattr(
|
||
ConversationService,
|
||
"ensure_redis_history_from_db",
|
||
AsyncMock(return_value=[]),
|
||
)
|
||
|
||
rows = await svc.list_for_user("uid")
|
||
assert rows[0]["title"] == "岁月知己"
|