"""人物模板配置加载与 Config→DB 同步测试。""" from __future__ import annotations import pytest from app.core.config import settings from app.db.session import AsyncSessionLocal from app.repositories import characters as character_repo from app.repositories.json_utils import loads_dict from app.services.character_templates import get_default_template, load_character_templates def test_load_templates_from_example(): """.example.yaml 加载成功且含 default 模板。""" templates = load_character_templates() assert "default" in templates cfg = templates["default"] assert cfg.key == "default" assert cfg.name == "魂镜" assert cfg.display_name == "魂镜" assert cfg.language == "zh-CN" assert cfg.privacy_level == "normal" assert cfg.profile["background"] == "默认人物" assert cfg.profile["speaking_style"] == "温和、清晰、简洁" assert cfg.profile["temperament"] == "沉静而温暖" assert isinstance(cfg.profile["hobbies"], list) assert "哲学" in cfg.profile["hobbies"] assert isinstance(cfg.profile["values"], list) assert "真诚" in cfg.profile["values"] assert cfg.model_policy["memory_extraction_model_hint"] == "memory-extraction" assert cfg.model_policy["personality_analysis_model_hint"] == "personality-analysis" assert cfg.model_policy["embedding_model_hint"] == "embedding" def test_get_default_template(): """取 default 模板配置成功。""" cfg = get_default_template() assert cfg.key == "default" assert cfg.name == "魂镜" @pytest.mark.asyncio async def test_ensure_default_system_template_creates_from_config(): """第一次调用 → 按 config 创建系统模板并写入 DB。""" from app.services.characters import ensure_default_system_template async with AsyncSessionLocal() as session: existing = await character_repo.get_system_template_by_key(session, "default") if existing: await session.delete(existing) await session.flush() template = await ensure_default_system_template(session) assert template is not None assert template.name == "魂镜" assert template.display_name == "魂镜" assert template.language == "zh-CN" assert template.privacy_level == "normal" assert template.template_key == "default" assert template.is_system_template is True assert template.owner_scope == "system" assert template.status == "active" profile = loads_dict(template.profile_json) assert profile.get("background") == "默认人物" assert profile.get("temperament") == "沉静而温暖" assert isinstance(profile.get("hobbies"), list) assert isinstance(profile.get("values"), list) assert isinstance(profile.get("birthday"), str) # _ensure_default_birthday 兜底 policy = loads_dict(template.model_policy_json) assert policy.get("default_chat_model_hint") == settings.default_model_hint assert policy.get("memory_extraction_model_hint") == "memory-extraction" assert policy.get("personality_analysis_model_hint") == "personality-analysis" assert policy.get("embedding_model_hint") == "embedding" await session.rollback() @pytest.mark.asyncio async def test_ensure_default_system_template_updates_changed_fields(): """已有模板时 config 字段变化 → 覆盖更新。""" from app.services.characters import ensure_default_system_template async with AsyncSessionLocal() as session: template = await ensure_default_system_template(session) await session.flush() template_id = template.id template.name = "旧名覆盖测试" await session.flush() template2 = await ensure_default_system_template(session) assert template2.name == "魂镜" # 被 config 覆盖回来 assert template2.id == template_id # 同一条记录 await session.rollback() @pytest.mark.asyncio async def test_template_resync_no_spurious_updates(): """config 与 DB 一致时同步不应触发无意义更新。""" from app.services.characters import ensure_default_system_template async with AsyncSessionLocal() as session: template = await ensure_default_system_template(session) await session.flush() template2 = await ensure_default_system_template(session) assert template2.id == template.id await session.rollback() @pytest.mark.asyncio async def test_create_character_uses_new_defaults(): """create_character 使用 ``_MODEL_POLICY_CREATE_DEFAULTS``。""" from app.schemas.character import CreateCharacterRequest from app.services.characters import create_character async with AsyncSessionLocal() as session: character = await create_character( session, CreateCharacterRequest(name="测试人物"), ) assert character is not None assert character.name == "测试人物" assert character.status == "wuji" # create_character 返回 CharacterResponse,model_policy 是 dict assert "default_chat_model_hint" in character.model_policy assert "memory_extraction_model_hint" in character.model_policy await session.rollback()