conftest.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """pytest 全局装置。
  2. **必须在导入任何项目模块之前设置环境变量**:``config`` 在导入时就读
  3. ``.env`` 与环境变量并定稿,``db.db_conn`` 在导入时就建好连接适配器。
  4. 所以下面的 ``os.environ`` 赋值必须在 ``import config`` 之前发生——
  5. 这也是把它们放在文件最顶部、并设 ``DEEP_AGENT_SKIP_DOTENV`` 的原因:
  6. 不跳过 .env 的话,开发者本机的 .env 会覆盖这里的测试配置,
  7. 同一套测试在不同机器上跑出不同结果。
  8. """
  9. from __future__ import annotations
  10. import os
  11. import shutil
  12. import tempfile
  13. import uuid
  14. from pathlib import Path
  15. # ---------- 测试环境(必须在项目导入前设置)----------
  16. _TMP_ROOT = Path(tempfile.mkdtemp(prefix="deep_agent_test_"))
  17. os.environ["DEEP_AGENT_SKIP_DOTENV"] = "1"
  18. os.environ["DB_DIALECT"] = "sqlite"
  19. os.environ["DB_SQLITE_PATH"] = str(_TMP_ROOT / "test.sqlite")
  20. os.environ["CHECKPOINTER"] = "memory" # 测试不需要落盘图状态
  21. os.environ["CTX_COMPACT_ENABLED"] = "0" # 不引入摘要中间件,测试更纯粹
  22. os.environ["OPENAI_API_KEY"] = "sk-test-key"
  23. os.environ["OPENAI_BASE_URL"] = "https://example.invalid"
  24. os.environ["TFAGENTS_DAILY_TOKEN_LIMIT"] = "0"
  25. os.environ["BILLING_ALLOW_UNPRICED"] = "0"
  26. os.environ["LOG_LEVEL"] = "WARNING"
  27. import pytest # noqa: E402
  28. from db import db_conn # noqa: E402
  29. @pytest.fixture(scope="session", autouse=True)
  30. def _schema() -> None:
  31. """整个测试会话共用一份建好的表结构。"""
  32. db_conn.init_schema()
  33. @pytest.fixture(autouse=True)
  34. def _clean_tables():
  35. """每个用例前清空业务表,避免用例间互相污染。
  36. 删表数据而不是重建 schema:重建的代价在 sqlite 上也不小,而这里
  37. 只需要一个干净的状态。
  38. """
  39. for table in (
  40. "messages",
  41. "sessions",
  42. "session_context_usage",
  43. "context_archives",
  44. "billing_usage",
  45. "billing_pricing",
  46. "daily_quota",
  47. "user_preferences",
  48. "schedule_runs",
  49. "schedules",
  50. ):
  51. db_conn.execute(f"DELETE FROM {table}", ())
  52. # 工具注册表是模块级单例,清空后重新装载,避免用例顺序影响工具清单
  53. from tools import registry
  54. registry.reset_for_tests()
  55. registry.load_builtin_tools()
  56. yield
  57. @pytest.fixture
  58. def pricing_ready():
  59. """确保单价表里有当前模型的条目,供计费相关用例使用。"""
  60. from billing import pricing
  61. pricing.seed_default_pricing()
  62. return pricing
  63. @pytest.fixture
  64. def session_id() -> str:
  65. """建一个测试会话,返回 session_id。"""
  66. from db import chat_store
  67. return chat_store.create_session("tester", "full", "测试会话")["id"]
  68. @pytest.fixture
  69. def unique_user() -> str:
  70. """每次调用返回一个新用户名,避免额度/流水跨用例串味。"""
  71. return f"u_{uuid.uuid4().hex[:8]}"
  72. def pytest_sessionfinish(session, exitstatus) -> None:
  73. """收尾清理临时目录。"""
  74. shutil.rmtree(_TMP_ROOT, ignore_errors=True)