# -*- coding: utf-8 -*- """tests/_test_env.py —— 自动化测试的统一临时环境(P0-01 新增) 铁律(VERIFICATION.md §凭据与运行数据隔离): 1. 每个测试进程独立临时目录 + 最小临时配置; 2. 在 import 任何可能间接加载 MainWindow 的模块之前设置 HAOCODE_CONFIG_FILE; 3. 在 import MainWindow 之前把 core.db_manager._DEFAULT_DB 指向临时数据库; 4. 写入、迁移、附件和截图产物只落到临时目录。 用法(放在测试文件顶部、任何 UI import 之前): from tests._test_env import isolate isolate("bashpanel", config={"providers": {}, "mode_switch": True}) 说明: · 临时目录按 进程 pid 隔离,测试进程互不干扰; · 默认临时配置为最小可启动结构(空 providers + 测试 provider); · 本模块不 import 任何 PyQt 模块,可在无 GUI 环境中安全调用。 """ import json import os import sys import tempfile _TESTS_DIR = os.path.dirname(os.path.abspath(__file__)) _ROOT = os.path.dirname(_TESTS_DIR) if _ROOT not in sys.path: sys.path.insert(0, _ROOT) def default_config() -> dict: """最小可启动临时配置(不含任何真实凭据)。""" return { "providers": { "testprov": { "api_key": "test-key-not-real", "base_url": "http://127.0.0.1:9/v1", "models": ["test-model"], "model_contexts": {"test-model": 100000}, } }, "default_provider": "testprov", "default_model": "test-model", } def isolate(tag: str = "t", config: dict | None = None) -> dict: """创建临时配置 + 临时数据库并完成重定向。必须在 import MainWindow 之前调用。 返回 {"base": 临时目录, "config": 临时配置路径, "db": 临时数据库路径}。 """ base = os.path.join(tempfile.gettempdir(), f"haocode_test_{tag}_{os.getpid()}") os.makedirs(base, exist_ok=True) cfg_path = os.path.join(base, "config.json") with open(cfg_path, "w", encoding="utf-8") as f: json.dump(config if config is not None else default_config(), f, ensure_ascii=False, indent=2) db_path = os.path.join(base, "chat_history.db") for p in (db_path, db_path + "-wal", db_path + "-shm"): if os.path.exists(p): os.remove(p) os.environ["HAOCODE_CONFIG_FILE"] = cfg_path # P1-03:QtWebEngine 独立 profile 重定向到临时目录(源码运行才在 data/webengine/) os.environ.setdefault("HAOCODE_WEBENGINE_PROFILE_DIR", os.path.join(base, "webengine_profile")) import core.db_manager as _dbm _dbm._DEFAULT_DB = db_path return {"base": base, "config": cfg_path, "db": db_path}