aps-agent/tests/conftest.py

117 lines
4.0 KiB
Python
Raw Normal View History

# ============================================================
# 测试默认启用演示种子,并把所有持久化状态隔离到进程级临时目录
# ============================================================
from __future__ import annotations
import os
from datetime import datetime
import tempfile
from pathlib import Path
import pytest
# conftest 在测试模块收集前导入:这里必须立即覆盖持久化环境,避免模块级
# 单例在 session fixture 执行前连接仓库内 server/data 或现场数据库。
_SESSION_TEMP = tempfile.TemporaryDirectory(prefix="aps-agent-tests-")
_SESSION_ROOT = Path(_SESSION_TEMP.name).resolve()
_SESSION_HOME = _SESSION_ROOT / "home"
_SESSION_DATA = _SESSION_HOME / "data"
_PERSISTENCE_ENV = {
"APS_HOME": _SESSION_HOME,
"APS_DATA_DIR": _SESSION_DATA,
"APS_DB_PATH": _SESSION_DATA / "master.db",
"APS_WORLD_PATH": _SESSION_DATA / "world.json",
"APS_PROJECTS_PATH": _SESSION_HOME / "sessions" / "workspace.json",
"APS_KNOWLEDGE_PATH": _SESSION_DATA / "knowledge.json",
"APS_CHECKPOINT_PATH": _SESSION_DATA / "checkpoints.json",
"APS_PREFERENCE_PATH": _SESSION_DATA / "preferences.json",
"APS_EMBEDDINGS_PATH": _SESSION_DATA / "embeddings.json",
"APS_APPROVAL_PATH": _SESSION_DATA / "approvals.json",
"APS_AUDIT_LEDGER_DIR": _SESSION_DATA / "audit-ledger",
"APS_AUDIT_MIRROR_DIR": _SESSION_DATA / "audit-mirror",
"APS_AUTOMATION_STATE_PATH": _SESSION_DATA / "automation.json",
"APS_MCP_BUS_PATH": _SESSION_DATA / "mcp-bus.json",
"APS_BRANCH_PATH": _SESSION_DATA / "branches.json",
"APS_BRANCH_DIR": _SESSION_DATA / "branches",
"APS_GOLDEN_CACHE": _SESSION_DATA / "golden-tests.json",
}
for _name, _path in _PERSISTENCE_ENV.items():
os.environ[_name] = str(_path)
# 任何外部数据库 URL 都会绕过 APS_DB_PATH;测试默认必须 fail-closed。
os.environ.pop("APS_DATABASE_URL", None)
os.environ["APS_APPROVAL_BACKEND"] = "file"
os.environ.setdefault("APS_MODE", "web")
# 黄金测试依赖 build_demo_world 的自洽数据;生产默认 APS_SEED_DEMO 关闭。
os.environ.setdefault("APS_SEED_DEMO", "1")
# 本轮系统验收日期固定为 2026-08-02;黄金测试不得因宿主机跨午夜漂移。
import server.timeutil as _timeutil
def _fixed_today0() -> datetime:
return datetime(2026, 8, 2)
_timeutil.today0 = _fixed_today0
def _reset_runtime_caches() -> None:
"""丢弃所有依赖持久化路径的进程级缓存,防止测试间串目录。"""
from server.db.database import reset_engine
reset_engine()
from server.state import store as world_store
with world_store._stores_lock:
world_store._stores.clear()
from server.state.branches import reset_branch_stores
reset_branch_stores()
from server.agent_core.plan_runtime import reset_plan_stores
reset_plan_stores()
from server.agent_core.mcp_bus import reset_mcp_bus
reset_mcp_bus()
from server.knowledge import assets, embedding, preferences
with assets._stores_lock:
assets._stores.clear()
with preferences._stores_lock:
preferences._stores.clear()
with embedding._stores_lock:
embedding._stores.clear()
embedding._provider = None
@pytest.fixture(scope="session", autouse=True)
def _isolated_persistence_session():
"""保证整个 pytest session 只在临时 APS_HOME 下持久化。"""
for path in _PERSISTENCE_ENV.values():
assert Path(path).resolve().is_relative_to(_SESSION_ROOT)
assert not os.environ.get("APS_DATABASE_URL")
_reset_runtime_caches()
yield _SESSION_ROOT
_reset_runtime_caches()
_SESSION_TEMP.cleanup()
@pytest.fixture(autouse=True)
def _isolated_approval_store(tmp_path):
"""每条用例隔离审批状态,并在前后清空所有路径相关缓存。"""
from server.agent_core import harness
_reset_runtime_caches()
store = harness.configure_approval_store(str(tmp_path / "approvals.json"))
yield
store.clear()
_reset_runtime_caches()