195 lines
7.1 KiB
Python
195 lines
7.1 KiB
Python
# -*- 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()
|