守住 runtime 環境變數文件契約

This commit is contained in:
OoO
2026-05-13 12:59:29 +08:00
parent 2b2233d385
commit acef0fa6ff
2 changed files with 79 additions and 0 deletions

View File

@@ -16,6 +16,29 @@ def _env_example_keys() -> set[str]:
return keys
def _runtime_env_keys_from_code() -> set[str]:
pattern = re.compile(r"""os\.(?:getenv|environ\.get)\(\s*['"]([A-Z][A-Z0-9_]+)['"]""")
scan_paths = [
ROOT / "app.py",
ROOT / "config.py",
ROOT / "scheduler.py",
ROOT / "run_scheduler.py",
ROOT / "routes",
ROOT / "services",
ROOT / "utils",
]
keys = set()
for scan_path in scan_paths:
paths = scan_path.rglob("*.py") if scan_path.is_dir() else [scan_path]
for path in paths:
if not path.exists() or "__pycache__" in path.parts:
continue
content = path.read_text(encoding="utf-8", errors="ignore")
keys.update(match.group(1) for match in pattern.finditer(content))
return keys
def test_phase3f_orphan_ai_services_stay_removed():
orphan_services = [
"services/elephant_alpha_decision_router.py",
@@ -44,9 +67,13 @@ def test_active_guides_do_not_point_to_removed_ai_services():
def test_env_example_documents_runtime_and_ai_automation_variables():
expected_keys = {
"AI_CALL_LOGGING_ENABLED",
"AUTO_FIX_ENABLED",
"AWAITING_REVIEW_PUSH_BATCH",
"CODE_REVIEW_AUTO_FIX_ENABLED",
"COST_THROTTLE_ENABLED",
"COST_THROTTLE_PROJECT_RATIO",
"COST_UNTHROTTLE_PROJECT_RATIO",
"DEEPSEEK_API_KEY",
"DEEPSEEK_BASE_URL",
"DEEPSEEK_DIRECT_ENABLED",
@@ -69,26 +96,45 @@ def test_env_example_documents_runtime_and_ai_automation_variables():
"MOMO_EVENT_ROUTER_REPLAY_ON_SUCCESS",
"MCP_FILESYSTEM_URL",
"MCP_FIRECRAWL_URL",
"MCP_CACHE_TTL_SEC",
"MCP_MAX_RESULT_BYTES",
"MCP_OMNISEARCH_URL",
"MCP_POSTGRES_URL",
"MCP_ROUTER_ENABLED",
"MCP_TIMEOUT_SEC",
"MODEL_ROUTER_ENABLED",
"N8N_HOST",
"N8N_PASSWORD",
"N8N_PROTOCOL",
"N8N_USER",
"N8N_WEBHOOK_BASE_URL",
"NEMOTRON_OLLAMA_FIRST",
"NEMOTRON_OLLAMA_MODEL",
"NEMOTRON_OLLAMA_TIMEOUT",
"OLLAMA_EMBED_TIMEOUT",
"OPENCLAW_ADMIN_USER_IDS",
"OPENCLAW_AGENT_DISPATCH",
"OPENCLAW_AGENT_DISPATCH_CMDS",
"OPENCLAW_ALLOW_PRIVATE_WITHOUT_WHITELIST",
"OPENCLAW_DAILY_HERMES_TEMPLATE",
"OPENCLAW_OLLAMA_MODEL",
"OPENCLAW_PPT_CACHE_TTL_HOURS",
"OPENCLAW_QA_OLLAMA_FIRST",
"OPENCLAW_QA_OLLAMA_HOST",
"OPENCLAW_QA_OLLAMA_MODEL",
"OPENCLAW_QA_OLLAMA_TIMEOUT",
"PPT_VISION_ENABLED",
"PPT_VISION_MODEL",
"PPT_VISION_TIMEOUT",
"PROMOTION_PENDING_BATCH_SIZE",
"RAG_ENABLED",
"RAG_DEFAULT_THRESHOLD",
"RAG_DEFAULT_TOP_K",
"RAG_EMBED_DIM",
"RAG_EMBED_MODEL",
"RAG_EMBED_NORMALIZE",
"TELEGRAM_ADMIN_CHAT_ID",
"TELEGRAM_BOT_USERNAME",
"TELEGRAM_CHAT_ID",
"WEB_CONCURRENCY",
}
@@ -96,6 +142,15 @@ def test_env_example_documents_runtime_and_ai_automation_variables():
assert expected_keys <= _env_example_keys()
def test_env_example_documents_runtime_os_env_keys():
internal_runtime_keys = {
"MOMO_ALLOW_INSECURE_CONFIG_FOR_TESTS",
"PYTEST_CURRENT_TEST",
}
assert _runtime_env_keys_from_code() - internal_runtime_keys <= _env_example_keys()
def test_scheduler_does_not_silently_swallow_exceptions():
scheduler_source = (ROOT / "scheduler.py").read_text(encoding="utf-8")