Files
group_fqcd_jr/tests/conftest.py
T
qyqy 7677aeaee1 merge: 并入同事的场外申购/推广/行情/NL2SQL 线(11 提交、334 文件)
冲突仅 3 个文件,全部取并集(双方都没有需要丢弃的改动):
- app/main.py:import 双方路由(我方 knowledge_management + 同事的 offsite_fund/
  promotion_material);include_router 段本已自动合并
- app/service/agent/bootstrap.py:import 与工具注册均取并集
  (query_customer_profile + query_financial_data 都注册)
- tests/integration/test_config_release_mysql.py:outbox 清理同时保留
  架构师的 event_type 限定(防误删其它域 outbox 行)与同事新增的 peer_release_id

同事这轮带入:11 个 alembic 迁移(建 offsite_* / promotion_* 等表)、
场外申购与推广素材 Agent、financial NL2SQL 工具。
注意:本库尚无 offsite_*/promotion_* 表,跑相关测试前需要执行 alembic upgrade。

边界核对:同事的场外代码未写入场内交易表(fin_sim_order/fin_capital_flow/fin_cash_ledger),
符合 AGENTS.md 规则 8。
2026-09-11 18:56:26 +08:00

74 lines
3.2 KiB
Python

"""测试共享夹具,以及测试期的数据库连接池隔离。
为什么在这里替换引擎(必须在导入任何 app 模块之前执行):
`app/infrastructure/db.py` 在导入时就建好了全局异步引擎,连接池里的 MySQL 连接绑定在
"建立它的那个事件循环"上。测试环境里同时存在多个循环——pytest-asyncio 每个用例一个、
每个 `TestClient` 一个 portal 线程循环、用例内 `asyncio.run(...)` 再建一个。连接被跨循环
复用时 SQLAlchemy/asyncmy 抛
`got Future <Future pending> attached to a different loop`,失败集合随执行顺序变化,
真实缺陷被噪声掩盖(同一用例全量跑失败、单条跑通过)。
NullPool 让每次取用都新建连接、归还即关闭,从根上消除跨循环复用。生产路径不受影响:
`app/infrastructure/db.py` 未改动,仍使用默认连接池。
"""
import asyncio
import sys
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.pool import NullPool
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
from app.infrastructure import db as _db # noqa: E402
_db.engine = create_async_engine(
_db.engine.url, pool_pre_ping=True, poolclass=NullPool
)
_db.SessionFactory = async_sessionmaker( # type: ignore[assignment]
_db.engine, class_=AsyncSession, expire_on_commit=False
)
import pytest # noqa: E402
from app.core.contracts import AgentDefinition, CoreResult, ResolvedAgentConfig # noqa: E402
from app.service.agent.base import BaseAgent # noqa: E402
from app.service.agent.factory import AgentFactory # noqa: E402
from app.service.agent.governance import review_output # noqa: E402
@pytest.fixture
def governance():
class TestGovernance:
async def resolve(self, definition, context):
return ResolvedAgentConfig(config_version="test", prompt_version="test",
model_endpoint="test")
async def recall(self, context):
return ()
async def review(self, result, context, config, memories, *, agent_type: str = ""):
# 透传 `agent_type`:门禁 F5(面向客户输出 100% 附固定话术)由它判定,
# 替身若不转发,`test_worker_runtime_mysql` 那条端到端断言会拿不到免责声明。
return review_output(result, context, config, memories, agent_type=agent_type)
return TestGovernance()
@pytest.fixture
def acceptance_registry(monkeypatch, governance):
"""Explicit test registration; production does not silently create business Agents."""
class TestAgent(BaseAgent):
async def handle(self, request, context):
return CoreResult(text="test result")
definition = AgentDefinition(agent_type="customer_service", version="test",
allowed_roles=("customer",), allowed_portals=("api",))
registry = AgentFactory(governance)
registry.register(definition, lambda _: TestAgent(definition))
monkeypatch.setattr("app.service.agent_run_application_service.get_agent_factory",
lambda: registry)
return registry