# -*- coding: utf-8 -*- """双账号配置加载 / 校验 / 热加载测试(不联网): uv run python tests/test_config.py""" import json import os import sys import tempfile import time 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.config import ConfigError, ConfigStore, load_config # noqa: E402 def write(path: Path, data) -> None: path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") def bump_mtime(path: Path) -> None: target = max(path.stat().st_mtime_ns, time.time_ns()) + 1_000_000 os.utime(path, ns=(target, target)) def base_config() -> dict: return { "bilibili": { "read": {"mid": 11, "csrf": "read-csrf", "cookie": "read-cookie"}, "write": {"mid": 22, "csrf": "write-csrf", "cookie": "write-cookie"}, }, "overrides": {"1": {"mid": 2, "name": "x"}}, "sleep": {"min": 1}, "folders": [ {"year": 2025, "media_id": 4451053851, "title": "2025"}, {"year": 2026, "media_id": 3700023351, "title": "2026"}, ], "track_fans": [37754047], "up_blacklist_seed": [1, 2, 2], } def test_dual_accounts_and_env_override(): with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "config.json" write(path, base_config()) env = { "BILI_READ_MID": "101", "BILI_READ_CSRF": "env-read", "BILI_READ_COOKIE": "env-read-cookie", "BILI_WRITE_MID": "202", "BILI_WRITE_CSRF": "env-write", "BILI_WRITE_COOKIE": "env-write-cookie", "DATABASE_PATH": "custom.db", } cfg = load_config(path, env=env) assert (cfg.bilibili.read.mid, cfg.bilibili.read.csrf, cfg.bilibili.read.cookie) == (101, "env-read", "env-read-cookie") assert (cfg.bilibili.write.mid, cfg.bilibili.write.csrf, cfg.bilibili.write.cookie) == (202, "env-write", "env-write-cookie") assert cfg.storage.database_path == Path(tmp) / "custom.db" assert cfg.up_blacklist_seed == (1, 2) def test_flat_and_missing_account_rejected(): with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "config.json" write(path, {"mid": 1, "csrf": "x", "cookie": "y", "folders": []}) try: load_config(path, env={}) except ConfigError as exc: assert "bilibili.read" in str(exc) else: raise AssertionError("flat 格式必须拒绝") write(path, {"bilibili": {"read": {}}, "folders": []}) try: load_config(path, env={}) except ConfigError as exc: assert "bilibili.write" in str(exc) else: raise AssertionError("缺少 write 必须拒绝") def test_validation_require_read_auth_and_hot_reload(): with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "config.json" data = base_config() data["bilibili"]["read"].update(cookie="", csrf="") write(path, data) load_config(path, env={}, require_auth=False) try: load_config(path, env={}, require_auth=True) except ConfigError as exc: assert "bilibili.read.cookie" in str(exc) else: raise AssertionError("read 空凭据必须拒绝") write(path, base_config()) store = ConfigStore(path, env={}) data = base_config() data["folders"][0]["title"] = "2025-new" write(path, data) bump_mtime(path) assert store.get().folders[0].title == "2025-new" path.write_text("{bad", encoding="utf-8") bump_mtime(path) assert store.get().folders[0].title == "2025-new" assert store.last_error def test_invalid_values(): with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "config.json" data = base_config() data["bilibili"]["write"]["mid"] = "bad" write(path, data) try: load_config(path, env={}) except ConfigError as exc: assert "bilibili.write.mid" in str(exc) else: raise AssertionError("非法 mid 必须拒绝") write(path, base_config()) try: load_config(path, env={"BILI_READ_MID": "bad"}) except ConfigError as exc: assert "BILI_READ_MID" in str(exc) else: raise AssertionError("非法环境变量必须拒绝") def main(): test_dual_accounts_and_env_override() test_flat_and_missing_account_rejected() test_validation_require_read_auth_and_hot_reload() test_invalid_values() print("OK: 双账号配置加载、拒绝 flat 格式、ENV 覆盖与热加载测试全部通过") if __name__ == "__main__": main()