# ============================================================ # 测试默认启用演示种子,并把所有持久化状态隔离到进程级临时目录 # ============================================================ 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" # Test authentication must not inherit a delayed dotenv load from the developer machine. os.environ["APS_AUTH_ENABLED"] = "1" 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()