"""人物服务测试:默认生日兜底(创建时未设 birthday → 用 created_at 兜底)。""" import pytest from app.db.session import AsyncSessionLocal from app.models.character import Character from app.repositories import time_anniversaries as anni_repo from app.repositories.json_utils import loads_dict from app.schemas.character import CreateCharacterRequest from app.services.characters import create_character, create_character_from_template def _birthday_anniversaries(items: list) -> list: return [a for a in items if a.kind == "birthday"] @pytest.mark.asyncio async def test_create_character_without_birthday_applies_default(): """未传 birthday → profile.birthday 与纪念日均取 created_at 的 ISO 日期。""" async with AsyncSessionLocal() as session: resp = await create_character(session, CreateCharacterRequest(name="lingxi")) anniversaries = await anni_repo.list_anniversaries(session, resp.id) character = await session.get(Character, resp.id) birthday_iso = character.created_at.strftime("%Y-%m-%d") assert resp.profile["birthday"] == birthday_iso birthday_annivs = _birthday_anniversaries(anniversaries) assert len(birthday_annivs) == 1 a = birthday_annivs[0] assert a.date == birthday_iso assert a.calendar == "solar" assert a.recurs_yearly is True assert a.kind == "birthday" assert a.subject_type == "character_self" assert a.source == "auto" # display_name 为 None → 回落到 name assert a.name == "lingxi·生辰" @pytest.mark.asyncio async def test_create_character_with_birthday_keeps_user_value(): """用户传了 birthday(即便为自由文本)→ 保持用户值,且不自动建生日纪念日。""" async with AsyncSessionLocal() as session: resp = await create_character( session, CreateCharacterRequest(name="jingwu", profile={"birthday": "霜降之夜"}), ) anniversaries = await anni_repo.list_anniversaries(session, resp.id) assert resp.profile["birthday"] == "霜降之夜" assert _birthday_anniversaries(anniversaries) == [] @pytest.mark.asyncio async def test_create_character_with_blank_birthday_applies_default(): """birthday 为空白字符串视为未设 → 兜底。""" async with AsyncSessionLocal() as session: resp = await create_character( session, CreateCharacterRequest(name="chenyu", profile={"birthday": " "}), ) anniversaries = await anni_repo.list_anniversaries(session, resp.id) character = await session.get(Character, resp.id) birthday_iso = character.created_at.strftime("%Y-%m-%d") assert resp.profile["birthday"] == birthday_iso assert len(_birthday_anniversaries(anniversaries)) == 1 @pytest.mark.asyncio async def test_create_character_from_template_without_birthday_uses_own_created_at(): """模板 profile 无 birthday → 子角色用自己的 created_at 兜底。""" async with AsyncSessionLocal() as session: template = Character( name="tpl", display_name="模板", status="active", language="zh-CN", profile_json='{"background": "测试模板"}', # 无 birthday model_policy_json="{}", privacy_level="normal", owner_scope="system", is_system_template=True, source_template_id=None, template_key="test_tpl", ) session.add(template) await session.flush() character = await create_character_from_template(session, template, "local-user") profile = loads_dict(character.profile_json) anniversaries = await anni_repo.list_anniversaries(session, character.id) birthday_iso = character.created_at.strftime("%Y-%m-%d") assert profile["birthday"] == birthday_iso birthday_annivs = _birthday_anniversaries(anniversaries) assert len(birthday_annivs) == 1 assert birthday_annivs[0].date == birthday_iso # display_name 优先于 name assert birthday_annivs[0].name == "模板·生辰" @pytest.mark.asyncio async def test_create_character_from_template_inherits_explicit_birthday(): """模板 profile 有显式 birthday → 子角色继承该值,不兜底。""" async with AsyncSessionLocal() as session: template = Character( name="tpl2", display_name="模板二", status="active", language="zh-CN", profile_json='{"birthday": "2020-01-01"}', model_policy_json="{}", privacy_level="normal", owner_scope="system", is_system_template=True, source_template_id=None, template_key="test_tpl2", ) session.add(template) await session.flush() character = await create_character_from_template(session, template, "local-user") profile = loads_dict(character.profile_json) anniversaries = await anni_repo.list_anniversaries(session, character.id) assert profile["birthday"] == "2020-01-01" # 继承模板的显式 birthday birthday_annivs = _birthday_anniversaries(anniversaries) assert len(birthday_annivs) == 1 assert birthday_annivs[0].date == "2020-01-01" assert birthday_annivs[0].name == "模板二·生辰"