muse-agent-example/tests/支持/数据库夹具.py

126 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""pytest 数据库 fixture:会话只准备迁移模板,每例仍独占数据库。"""
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
import pytest
from .数据库底座 import PostgreSQL后端
from .数据库底座 import 数据库底座 as 底座管理器
_会话证据 = pytest.StashKey[tuple[底座管理器, Path]]()
@pytest.fixture(scope="session")
def 隔离数据库URL():
值 = os.environ.get("MUSE_TEST_DATABASE_URL", "").strip()
if not 值:
pytest.fail("未设置 MUSE_TEST_DATABASE_URL;隔离 PG 验证未执行", pytrace=False)
return 值
@pytest.fixture(scope="session")
def 数据库底座(隔离数据库URL, tmp_path_factory, request):
if not request.config.getoption("--外部环境"):
pytest.fail("数据库夹具必须显式选择 --外部环境", pytrace=False)
目录 = tmp_path_factory.mktemp("数据库底座")
根 = Path(__file__).resolve().parents[2]
基础目录 = tmp_path_factory.getbasetemp()
if os.environ.get("PYTEST_XDIST_WORKER"):
基础目录 = 基础目录.parent
共享目录 = 基础目录 / "数据库角色初始化"
底座 = 底座管理器(
PostgreSQL后端(隔离数据库URL, 共享目录),
根,
目录,
worker=os.environ.get("PYTEST_XDIST_WORKER", "main"),
)
输出 = 目录 / "成本事件.json"
request.config.stash[_会话证据] = (底座, 输出)
try:
yield 底座
finally:
try:
底座.关闭()
finally:
输出.write_text(json.dumps(底座.事件, ensure_ascii=False, indent=2))
终端 = request.config.pluginmanager.getplugin("terminalreporter")
if 终端:
终端.write_line(f"数据库成本事件:{输出}")
@pytest.fixture
def 测试数据库端点(数据库底座, tmp_path):
"""供真实初始化目标使用;这里不创建没有用途的应用库。"""
return 数据库底座.端点(tmp_path / "数据库端点")
@pytest.fixture
def 应用测试库(数据库底座, tmp_path):
借用 = 数据库底座.借库 if os.environ.get("MUSE_ISOLATED_CLONE") == "1" else 数据库底座.共享库
with 借用(tmp_path / "连接引用") as 工厂:
yield 工厂
@pytest.fixture
def 空测试库(数据库底座, tmp_path):
with 数据库底座.借库(tmp_path / "空库引用", 空库=True) as 工厂:
yield 工厂
@pytest.fixture(scope="session")
def 内置种子方案():
# 延迟加载,纯离线用例不导入资源,更不建立数据库连接。
from muse.元数据.接口 import 元数据服务, 导入内置结构
from muse.共享.调用身份 import 用途
根 = Path(__file__).resolve().parents[2]
# 种子及导入器是基线身份的一部分,禁止模板跨不同种子逻辑复用。
路径集 = sorted((根 / "src/muse/元数据/内置结构").glob("*.yaml"))
if not 路径集:
raise RuntimeError("内置结构种子目录为空")
摘要 = hashlib.sha256()
for 路径 in [*路径集, 根 / "src/muse/元数据/内置导入.py"]:
摘要.update(路径.relative_to(根).as_posix().encode())
摘要.update(路径.read_bytes())
def 导入(工厂):
with 工厂[用途.维护].连接() as 连, 连.transaction():
导入内置结构(元数据服务(连))
return 摘要.hexdigest(), 导入
@pytest.fixture
def 内置结构测试库(数据库底座, 内置种子方案, tmp_path):
指纹, 导入 = 内置种子方案
借用 = 数据库底座.借库 if os.environ.get("MUSE_ISOLATED_CLONE") == "1" else 数据库底座.共享库
with 借用(tmp_path / "种子库引用", 种子指纹=指纹, 导入种子=导入) as 工厂:
yield 工厂
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
结果 = yield
报告 = 结果.get_result()
底座 = item.funcargs.get("数据库底座")
if 底座 is not None:
底座.记录(
"phase",
nodeid=item.nodeid,
phase=报告.when,
seconds=报告.duration,
outcome=报告.outcome,
)
def pytest_sessionfinish(session, exitstatus):
# 最后一例的 teardown 报告晚于 session fixture 清理;在此补齐阶段耗时。
if _会话证据 in session.config.stash:
底座, 输出 = session.config.stash[_会话证据]
输出.write_text(json.dumps(底座.事件, ensure_ascii=False, indent=2))