from __future__ import annotations import tempfile import unittest from unittest.mock import patch from pathlib import Path from biliup_next.core.config import SettingsService class SettingsServiceTests(unittest.TestCase): def test_load_seeds_settings_from_standalone_example_when_missing(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) config_dir = root / "config" config_dir.mkdir(parents=True, exist_ok=True) (config_dir / "settings.schema.json").write_text( """ { "groups": { "runtime": { "database_path": {"type": "string", "default": "data/workspace/biliup_next.db"} }, "paths": { "stage_dir": {"type": "string", "default": "data/workspace/stage"}, "backup_dir": {"type": "string", "default": "data/workspace/backup"}, "session_dir": {"type": "string", "default": "data/workspace/session"}, "cookies_file": {"type": "string", "default": "runtime/cookies.json"}, "upload_config_file": {"type": "string", "default": "runtime/upload_config.json"} }, "ingest": { "ffprobe_bin": {"type": "string", "default": "ffprobe"}, "yt_dlp_cmd": {"type": "string", "default": "yt-dlp"}, "yt_dlp_format": {"type": "string", "default": ""} }, "transcribe": { "ffmpeg_bin": {"type": "string", "default": "ffmpeg"} }, "split": { "ffmpeg_bin": {"type": "string", "default": "ffmpeg"} }, "song_detect": { "codex_cmd": {"type": "string", "default": "codex"}, "qwen_cmd": {"type": "string", "default": "qwen"} }, "publish": { "biliup_path": {"type": "string", "default": "runtime/biliup"}, "cookie_file": {"type": "string", "default": "runtime/cookies.json"} } } } """, encoding="utf-8", ) (config_dir / "settings.standalone.example.json").write_text( """ { "runtime": {"database_path": "data/workspace/biliup_next.db"}, "paths": { "stage_dir": "data/workspace/stage", "backup_dir": "data/workspace/backup", "session_dir": "data/workspace/session", "cookies_file": "runtime/cookies.json", "upload_config_file": "runtime/upload_config.json" }, "ingest": {"ffprobe_bin": "ffprobe", "yt_dlp_cmd": "yt-dlp", "yt_dlp_format": ""}, "transcribe": {"ffmpeg_bin": "ffmpeg"}, "split": {"ffmpeg_bin": "ffmpeg"}, "song_detect": {"codex_cmd": "codex", "qwen_cmd": "qwen"}, "publish": {"biliup_path": "runtime/biliup", "cookie_file": "runtime/cookies.json"} } """, encoding="utf-8", ) bundle = SettingsService(root).load() self.assertTrue((config_dir / "settings.json").exists()) self.assertTrue((config_dir / "settings.staged.json").exists()) self.assertEqual(bundle.settings["paths"]["cookies_file"], str((root / "runtime" / "cookies.json").resolve())) def test_load_applies_environment_overrides_before_path_normalization(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) config_dir = root / "config" config_dir.mkdir(parents=True, exist_ok=True) (config_dir / "settings.schema.json").write_text( """ { "groups": { "runtime": { "database_path": {"type": "string", "default": "data/workspace/biliup_next.db"} }, "paths": { "stage_dir": {"type": "string", "default": "data/workspace/stage"}, "backup_dir": {"type": "string", "default": "data/workspace/backup"}, "session_dir": {"type": "string", "default": "data/workspace/session"}, "cookies_file": {"type": "string", "default": "runtime/cookies.json"}, "upload_config_file": {"type": "string", "default": "runtime/upload_config.json"} }, "ingest": { "ffprobe_bin": {"type": "string", "default": "ffprobe"}, "yt_dlp_cmd": {"type": "string", "default": "yt-dlp"}, "yt_dlp_format": {"type": "string", "default": ""} }, "transcribe": { "groq_api_key": {"type": "string", "default": "", "sensitive": true}, "ffmpeg_bin": {"type": "string", "default": "ffmpeg"} }, "split": { "ffmpeg_bin": {"type": "string", "default": "ffmpeg"} }, "song_detect": { "codex_cmd": {"type": "string", "default": "codex"}, "qwen_cmd": {"type": "string", "default": "qwen"} }, "publish": { "biliup_path": {"type": "string", "default": "runtime/biliup"}, "cookie_file": {"type": "string", "default": "runtime/cookies.json"} }, "collection": { "season_id_a": {"type": "integer", "default": 0}, "season_id_b": {"type": "integer", "default": 0} } } } """, encoding="utf-8", ) (config_dir / "settings.standalone.example.json").write_text( """ { "runtime": {"database_path": "data/workspace/biliup_next.db"}, "paths": { "stage_dir": "data/workspace/stage", "backup_dir": "data/workspace/backup", "session_dir": "data/workspace/session", "cookies_file": "runtime/cookies.json", "upload_config_file": "runtime/upload_config.json" }, "ingest": {"ffprobe_bin": "ffprobe", "yt_dlp_cmd": "yt-dlp", "yt_dlp_format": ""}, "transcribe": {"groq_api_key": "", "ffmpeg_bin": "ffmpeg"}, "split": {"ffmpeg_bin": "ffmpeg"}, "song_detect": {"codex_cmd": "codex", "qwen_cmd": "qwen"}, "publish": {"biliup_path": "runtime/biliup", "cookie_file": "runtime/cookies.json"}, "collection": {"season_id_a": 0, "season_id_b": 0} } """, encoding="utf-8", ) with patch.dict( "os.environ", { "GROQ_API_KEY": "gsk_test", "COLLECTION_SEASON_ID_A": "7196643", "BILIUP_NEXT__COLLECTION__SEASON_ID_B": "7196624", "BILIUP_NEXT__PATHS__STAGE_DIR": "data/custom-stage", }, clear=True, ): bundle = SettingsService(root).load() self.assertEqual(bundle.settings["transcribe"]["groq_api_key"], "gsk_test") self.assertEqual(bundle.settings["collection"]["season_id_a"], 7196643) self.assertEqual(bundle.settings["collection"]["season_id_b"], 7196624) self.assertEqual(bundle.settings["paths"]["stage_dir"], str((root / "data" / "custom-stage").resolve())) def test_empty_environment_values_do_not_override_settings(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) config_dir = root / "config" config_dir.mkdir(parents=True, exist_ok=True) (config_dir / "settings.schema.json").write_text( """ { "groups": { "runtime": {"database_path": {"type": "string", "default": "data/workspace/biliup_next.db"}}, "paths": { "stage_dir": {"type": "string", "default": "data/workspace/stage"}, "backup_dir": {"type": "string", "default": "data/workspace/backup"}, "session_dir": {"type": "string", "default": "data/workspace/session"}, "cookies_file": {"type": "string", "default": "runtime/cookies.json"}, "upload_config_file": {"type": "string", "default": "runtime/upload_config.json"} }, "ingest": {"ffprobe_bin": {"type": "string", "default": "ffprobe"}, "yt_dlp_cmd": {"type": "string", "default": "yt-dlp"}}, "transcribe": {"groq_api_key": {"type": "string", "default": ""}, "ffmpeg_bin": {"type": "string", "default": "ffmpeg"}}, "split": {"ffmpeg_bin": {"type": "string", "default": "ffmpeg"}}, "song_detect": {"codex_cmd": {"type": "string", "default": "codex"}, "qwen_cmd": {"type": "string", "default": "qwen"}}, "publish": {"biliup_path": {"type": "string", "default": "runtime/biliup"}, "cookie_file": {"type": "string", "default": "runtime/cookies.json"}} } } """, encoding="utf-8", ) (config_dir / "settings.standalone.example.json").write_text( """ { "runtime": {"database_path": "data/workspace/biliup_next.db"}, "paths": { "stage_dir": "data/workspace/stage", "backup_dir": "data/workspace/backup", "session_dir": "data/workspace/session", "cookies_file": "runtime/cookies.json", "upload_config_file": "runtime/upload_config.json" }, "ingest": {"ffprobe_bin": "ffprobe", "yt_dlp_cmd": "yt-dlp"}, "transcribe": {"groq_api_key": "from-file", "ffmpeg_bin": "ffmpeg"}, "split": {"ffmpeg_bin": "ffmpeg"}, "song_detect": {"codex_cmd": "codex", "qwen_cmd": "qwen"}, "publish": {"biliup_path": "runtime/biliup", "cookie_file": "runtime/cookies.json"} } """, encoding="utf-8", ) with patch.dict("os.environ", {"GROQ_API_KEY": ""}, clear=True): bundle = SettingsService(root).load() self.assertEqual(bundle.settings["transcribe"]["groq_api_key"], "from-file") if __name__ == "__main__": unittest.main()