tools: add split_config_secrets for config.json → jsonc + secrets

This commit is contained in:
bot-ci committed 2026-11-02 13:24:27 +08:00
1 parent 8e7ead1669
commit 77d74c501a
4 files changed
+528

No files matched your search

+194
View File
@@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
"""config.json → jsonc + secrets 拆分脚本测试(离线):
uv run python tests/test_split_config_secrets.py"""
from __future__ import annotations
import json
import os
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.stdout.reconfigure(encoding="utf-8")
from reaction_board import jsonc # noqa: E402
from reaction_board.config import load_config # noqa: E402
from tools.split_config_secrets import ( # noqa: E402
split_config_file,
split_raw,
)
def _monolith() -> dict:
return {
"bilibili": {
"read": {"mid": 11, "csrf": "read-csrf", "cookie": "SESSDATA=secret-read"},
"write": {"mid": 22, "csrf": "write-csrf", "cookie": "SESSDATA=secret-write"},
},
"telegram": {
"enabled": True,
"bot_token": "tg-secret-token",
"chat_id": 12345,
},
"web": {
"host": "127.0.0.1",
"port": 8080,
"auth_token": "web-secret-token",
"public_base_url": "",
},
"folders": [{"year": 2026, "media_id": 1, "title": "t"}],
"track_fans": [37754047],
"up_blacklist_seed": [],
}
def test_split_raw_extracts_secrets_keeps_business():
business, secrets, stats = split_raw(_monolith())
assert "bilibili" not in business
assert business["telegram"] == {"enabled": True}
assert business["web"]["host"] == "127.0.0.1"
assert business["web"]["port"] == 8080
assert "auth_token" not in business["web"]
assert secrets["bilibili"]["read"]["mid"] == 11
assert secrets["telegram"] == {"bot_token": "tg-secret-token", "chat_id": 12345}
assert secrets["web"] == {"auth_token": "web-secret-token"}
assert "bilibili" in stats["moved_paths"]
assert "telegram.bot_token" in stats["moved_paths"]
assert "web.auth_token" in stats["moved_paths"]
def test_split_raw_omits_absent_secret_stubs():
raw = {
"telegram": {"enabled": False},
"web": {"host": "0.0.0.0", "port": 9},
"folders": [],
}
business, secrets, stats = split_raw(raw)
assert secrets == {}
assert business["telegram"] == {"enabled": False}
assert business["web"]["host"] == "0.0.0.0"
assert stats["moved_count"] == 0
def test_dry_run_writes_nothing():
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = root / "config.json"
src.write_text(json.dumps(_monolith(), ensure_ascii=False), encoding="utf-8")
summary = split_config_file(src, dry_run=True)
assert summary["dry_run"] is True
assert summary["moved_count"] >= 3
assert not (root / "config.jsonc").exists()
assert not (root / "secrets.json").exists()
assert src.exists()
# 摘要不得泄露 cookie/token
blob = json.dumps(summary, ensure_ascii=False)
assert "SESSDATA" not in blob
assert "tg-secret-token" not in blob
assert "web-secret-token" not in blob
def test_refuses_overwrite_without_force():
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = root / "config.json"
src.write_text(json.dumps(_monolith(), ensure_ascii=False), encoding="utf-8")
(root / "config.jsonc").write_text("// existing\n{}\n", encoding="utf-8")
try:
split_config_file(src, keep_src=True)
except FileExistsError as exc:
assert "config.jsonc" in str(exc)
assert "--force" in str(exc)
else:
raise AssertionError("已有输出时无 --force 必须失败")
assert src.exists()
assert "existing" in (root / "config.jsonc").read_text(encoding="utf-8")
def test_writes_and_renames_src_by_default():
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = root / "config.json"
src.write_text(json.dumps(_monolith(), ensure_ascii=False), encoding="utf-8")
summary = split_config_file(src)
cfg_path = root / "config.jsonc"
sec_path = root / "secrets.json"
bak = root / "config.json.bak"
assert summary["ok"] is True
assert not src.exists()
assert bak.exists()
assert cfg_path.exists()
assert sec_path.exists()
text = cfg_path.read_text(encoding="utf-8")
assert text.startswith("//")
assert "secrets.json" in text
business = jsonc.load(cfg_path)
secrets = json.loads(sec_path.read_text(encoding="utf-8"))
assert "bilibili" not in business
assert business["telegram"]["enabled"] is True
assert "bot_token" not in business["telegram"]
assert business["web"]["host"] == "127.0.0.1"
assert "auth_token" not in business["web"]
assert secrets["bilibili"]["read"]["mid"] == 11
assert secrets["telegram"]["chat_id"] == 12345
assert secrets["web"]["auth_token"] == "web-secret-token"
loaded = load_config(cfg_path, env={}, require_auth=False)
assert loaded.bilibili.read.mid == 11
assert loaded.bilibili.write.mid == 22
assert loaded.telegram.enabled is True
assert loaded.telegram.bot_token == "tg-secret-token"
assert loaded.web.host == "127.0.0.1"
assert loaded.web.auth_token == "web-secret-token"
assert summary["verify"]["checked"] is True
assert summary["verify"]["read_mid"] == 11
def test_force_overwrite_and_keep_src():
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = root / "config.json"
src.write_text(json.dumps(_monolith(), ensure_ascii=False), encoding="utf-8")
(root / "config.jsonc").write_text("{}\n", encoding="utf-8")
(root / "secrets.json").write_text("{}\n", encoding="utf-8")
summary = split_config_file(src, force=True, keep_src=True)
assert src.exists()
assert summary["src_renamed"] is False
secrets = json.loads((root / "secrets.json").read_text(encoding="utf-8"))
assert secrets["bilibili"]["read"]["mid"] == 11
def test_honors_secrets_path_relative():
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
src = root / "config.json"
data = _monolith()
data["secrets_path"] = "creds/my-secrets.json"
src.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
summary = split_config_file(src, keep_src=True)
sec = root / "creds" / "my-secrets.json"
assert Path(summary["secrets_out"]) == sec
assert sec.exists()
business = jsonc.load(root / "config.jsonc")
assert business.get("secrets_path") == "creds/my-secrets.json"
def main():
test_split_raw_extracts_secrets_keeps_business()
test_split_raw_omits_absent_secret_stubs()
test_dry_run_writes_nothing()
test_refuses_overwrite_without_force()
test_writes_and_renames_src_by_default()
test_force_overwrite_and_keep_src()
test_honors_secrets_path_relative()
print(
"OK: split_config_secrets 抽取 bilibili/telegram/web 凭据、"
"dry-run、拒绝覆盖、改名与 secrets_path 测试全部通过"
)
if __name__ == "__main__":
main()