from __future__ import annotations import pytest import pytest_asyncio from sqlalchemy import select from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from app.models.base import Base from app.models.project import Project from app.models.signal import Signal from app.models.signal_source_registry import SignalSourceRegistry from app.models.user import User from app.services.signal.signal_source_orchestration import SignalSourceOrchestrationService @pytest_asyncio.fixture async def db_session(): engine = create_async_engine("sqlite+aiosqlite:///:memory:") async with engine.begin() as conn: await conn.run_sync( lambda sync_conn: Base.metadata.create_all( sync_conn, tables=[ User.__table__, Project.__table__, Signal.__table__, SignalSourceRegistry.__table__, ], ) ) maker = async_sessionmaker(engine, expire_on_commit=False) async with maker() as session: yield session await engine.dispose() @pytest.mark.asyncio async def test_materialize_requirement_sources_dedupes_duplicate_items(db_session) -> None: user = User( tenant_id=1, username="owner", email="owner@example.com", password_hash="hash", name="负责人", role="ADMIN", status="active", is_active=True, ) project = Project( tenant_id=1, name="demo-project", title="Demo Project", status="active", assigned_role="backend", ) signal = Signal( tenant_id=1, source="AI-Steward", content="main signal", priority="high", processed=True, ) db_session.add_all([user, project, signal]) await db_session.commit() service = SignalSourceOrchestrationService(db_session) results = await service.materialize_requirement_sources( tenant_id=1, project_id=int(project.id), signal_id=int(signal.id), operator_user_id=int(user.id), requested_via="ai_steward", origin="project_analysis", origin_text="我们要做一个 AI 客服工具,包含内容本地化和社媒运营。", default_source_kind="custom", requirement_items=[ { "section": "核心痛点", "title": "内容本地化", "description": "翻译公司贵、慢、不地道", }, { "section": "核心痛点", "title": "内容本地化", "description": "翻译公司贵、慢、不地道", }, { "section": "MVP 功能", "title": "社媒运营", "description": "自动生成社媒文案并排期发布", }, ], ) assert len(results) == 2 assert results[0]["section"] == "核心痛点" assert results[0]["source_kind"] == "custom" assert results[0]["origin"] == "project_analysis" rows = (await db_session.execute(select(SignalSourceRegistry).order_by(SignalSourceRegistry.id))).scalars().all() assert len(rows) == 2 assert rows[0].raw_context["origin"] == "project_analysis" assert rows[0].raw_context["requirement_item"]["title"] == "内容本地化" assert rows[1].raw_context["requirement_item"]["section"] == "MVP 功能"