"""大周天抽取候选审核服务测试(记忆候选 + 世界实体候选)。""" import pytest from sqlalchemy import select from app.core.errors import AppError from app.db.session import AsyncSessionLocal from app.models.big_cycle import BigCycle from app.models.candidates import MemoryCandidate from app.models.memory import Memory from app.models.world import CharacterWorldEntity from app.services import extraction as extraction_service from app.services.characters import get_active_character_model async def _seed_cycle(session, character_id: str) -> str: cycle = BigCycle( character_id=character_id, trigger="test", status="completed", memory_count_at_trigger=20, ) session.add(cycle) await session.flush() return cycle.id async def _seed_memory_candidate( session, character_id: str, cycle_id: str, status: str = "pending" ) -> str: cand = MemoryCandidate( character_id=character_id, cycle_id=cycle_id, type="preference", title="喜欢简洁", content="用户偏好简洁直接的回答", confidence=0.7, importance=0.8, sensitivity="low", status=status, ) session.add(cand) await session.flush() return cand.id async def _seed_world_entity(session, character_id: str, status: str = "pending") -> str: e = CharacterWorldEntity( character_id=character_id, type="project", name="项目X", state_type="inference", confidence=0.6, status=status, ) session.add(e) await session.flush() return e.id @pytest.mark.asyncio async def test_approve_memory_candidate_derives_memory() -> None: async with AsyncSessionLocal() as session: character = await get_active_character_model(session) cycle_id = await _seed_cycle(session, character.id) cid = await _seed_memory_candidate(session, character.id, cycle_id) await session.commit() resp = await extraction_service.approve_memory_proposal(cid) assert resp.status == "applied" async with AsyncSessionLocal() as session: mem = ( await session.execute( select(Memory).where( Memory.character_id == character.id, Memory.title == "喜欢简洁", ) ) ).scalar_one_or_none() assert mem is not None assert mem.source == "big_cycle" @pytest.mark.asyncio async def test_reject_memory_candidate() -> None: async with AsyncSessionLocal() as session: character = await get_active_character_model(session) cycle_id = await _seed_cycle(session, character.id) cid = await _seed_memory_candidate(session, character.id, cycle_id) await session.commit() resp = await extraction_service.reject_memory_proposal(cid) assert resp.status == "rejected" @pytest.mark.asyncio async def test_approve_non_pending_memory_conflicts() -> None: async with AsyncSessionLocal() as session: character = await get_active_character_model(session) cycle_id = await _seed_cycle(session, character.id) cid = await _seed_memory_candidate(session, character.id, cycle_id, status="applied") await session.commit() with pytest.raises(AppError): await extraction_service.approve_memory_proposal(cid) @pytest.mark.asyncio async def test_approve_world_entity_confirms() -> None: async with AsyncSessionLocal() as session: character = await get_active_character_model(session) eid = await _seed_world_entity(session, character.id) await session.commit() resp = await extraction_service.approve_world_entity(eid) assert resp.status == "confirmed" @pytest.mark.asyncio async def test_reject_world_entity() -> None: async with AsyncSessionLocal() as session: character = await get_active_character_model(session) eid = await _seed_world_entity(session, character.id) await session.commit() resp = await extraction_service.reject_world_entity(eid) assert resp.status == "rejected" @pytest.mark.asyncio async def test_list_memory_proposals_source_cycle() -> None: async with AsyncSessionLocal() as session: character = await get_active_character_model(session) cycle_id = await _seed_cycle(session, character.id) await _seed_memory_candidate(session, character.id, cycle_id) # 导入来源候选(import_job_id 非空) session.add( MemoryCandidate( character_id=character.id, import_job_id="import_test", type="user", title="导入记忆", content="来自导入", confidence=0.5, importance=0.5, sensitivity="low", status="pending", ) ) await session.commit() page = await extraction_service.list_memory_proposals(character_id=character.id, source="cycle") assert page.total == 1 assert page.items[0].title == "喜欢简洁" assert page.items[0].cycle_id == cycle_id