from __future__ import annotations import pytest import pytest_asyncio from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from app.models.base import Base from app.models.reply_iteration import PromptRelease from app.services.prompt_release.service import PromptReleaseService from app.services.prompt_runtime_service import PromptRuntimeError, PromptRuntimeService @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=[PromptRelease.__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_prompt_runtime_service_prefers_active_release(db_session) -> None: release = await PromptReleaseService(db_session).create_release( tenant_id=1, prompt_key="PROMPT_FEISHU_DIRECT_QA", body_snapshot="active: {question}", created_by=1001, ) await PromptReleaseService(db_session).activate_release( tenant_id=1, release_id=release["id"], actor_id=1001, ) result = await PromptRuntimeService(db_session).render( tenant_id=1, prompt_key="PROMPT_FEISHU_DIRECT_QA", fallback_template="fallback: {question}", question="hello", ) assert result.prompt == "active: hello" assert result.source == "active_release" assert result.release_id == release["id"] assert result.routing_meta()["prompt_key"] == "PROMPT_FEISHU_DIRECT_QA" assert result.routing_meta()["prompt_release_id"] == release["id"] assert result.format_error is False @pytest.mark.asyncio async def test_prompt_runtime_service_fails_closed_on_active_release_format_error(db_session) -> None: release = await PromptReleaseService(db_session).create_release( tenant_id=1, prompt_key="PROMPT_FEISHU_DIRECT_QA", body_snapshot="broken: {missing}", created_by=1001, ) await PromptReleaseService(db_session).activate_release( tenant_id=1, release_id=release["id"], actor_id=1001, ) with pytest.raises(PromptRuntimeError, match="active prompt release render failed"): await PromptRuntimeService(db_session).render( tenant_id=1, prompt_key="PROMPT_FEISHU_DIRECT_QA", fallback_template="fallback: {question}", question="hello", ) @pytest.mark.asyncio async def test_prompt_runtime_service_fails_closed_without_active_release(db_session) -> None: with pytest.raises(PromptRuntimeError, match="active prompt release not found"): await PromptRuntimeService(db_session).render( tenant_id=1, prompt_key="PROMPT_FEISHU_DIRECT_QA", fallback_template="fallback: {question}", question="hello", ) @pytest.mark.asyncio async def test_prompt_runtime_service_falls_back_to_platform_active_release(db_session) -> None: release = await PromptReleaseService(db_session).create_release( tenant_id=1, prompt_key="PROMPT_FEISHU_DIRECT_QA", body_snapshot="platform: {question}", created_by=1001, ) await PromptReleaseService(db_session).activate_release( tenant_id=1, release_id=release["id"], actor_id=1001, ) result = await PromptRuntimeService(db_session).render( tenant_id=12, prompt_key="PROMPT_FEISHU_DIRECT_QA", fallback_template="fallback: {question}", question="hello", ) assert result.prompt == "platform: hello" assert result.source == "platform_active_release" assert result.release_id == release["id"] assert result.routing_meta()["prompt_runtime_tenant_id"] == 1