"""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"] == "岁月知己"