import json import sys import tempfile import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" if str(SRC) not in sys.path: sys.path.insert(0, str(SRC)) from config import load, redacted_dict class ConfigTests(unittest.TestCase): def test_defaults_when_file_missing(self): with tempfile.TemporaryDirectory() as td: missing = Path(td) / "nested" / "config.json" cfg = load(str(missing)) self.assertEqual(cfg.daemon.hotkey, "Cmd+m") self.assertEqual(cfg.daemon.edit_hotkey, "Cmd+Shift+m") self.assertEqual(cfg.recording.input, "") self.assertEqual(cfg.stt.model, "base") self.assertEqual(cfg.stt.device, "cpu") self.assertEqual(cfg.injection.backend, "clipboard") self.assertFalse(cfg.injection.remove_transcription_from_clipboard) self.assertEqual(cfg.vocabulary.replacements, []) self.assertEqual(cfg.vocabulary.terms, []) self.assertTrue(missing.exists()) written = json.loads(missing.read_text(encoding="utf-8")) self.assertEqual(written, redacted_dict(cfg)) def test_loads_nested_config(self): payload = { "daemon": {"hotkey": "Ctrl+space", "edit_hotkey": "Ctrl+Shift+space"}, "recording": {"input": 3}, "stt": {"model": "small", "device": "cuda"}, "injection": { "backend": "injection", "remove_transcription_from_clipboard": True, }, "vocabulary": { "replacements": [ {"from": "Martha", "to": "Marta"}, {"from": "docker", "to": "Docker"}, ], "terms": ["Systemd", "Kubernetes"], }, } with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") cfg = load(str(path)) self.assertEqual(cfg.daemon.hotkey, "Ctrl+space") self.assertEqual(cfg.daemon.edit_hotkey, "Ctrl+Shift+space") self.assertEqual(cfg.recording.input, 3) self.assertEqual(cfg.stt.model, "small") self.assertEqual(cfg.stt.device, "cuda") self.assertEqual(cfg.injection.backend, "injection") self.assertTrue(cfg.injection.remove_transcription_from_clipboard) self.assertEqual(len(cfg.vocabulary.replacements), 2) self.assertEqual(cfg.vocabulary.replacements[0].source, "Martha") self.assertEqual(cfg.vocabulary.replacements[0].target, "Marta") self.assertEqual(cfg.vocabulary.terms, ["Systemd", "Kubernetes"]) def test_super_modifier_hotkey_is_valid(self): payload = {"daemon": {"hotkey": "Super+m", "edit_hotkey": "Super+Shift+m"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") cfg = load(str(path)) self.assertEqual(cfg.daemon.hotkey, "Super+m") self.assertEqual(cfg.daemon.edit_hotkey, "Super+Shift+m") def test_invalid_hotkey_missing_key_raises(self): payload = {"daemon": {"hotkey": "Ctrl+Alt"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "daemon.hotkey is invalid: missing key"): load(str(path)) def test_invalid_hotkey_multiple_keys_raises(self): payload = {"daemon": {"hotkey": "Ctrl+a+b"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex( ValueError, "daemon.hotkey is invalid: must include exactly one non-modifier key" ): load(str(path)) def test_invalid_edit_hotkey_raises(self): payload = {"daemon": {"edit_hotkey": "Ctrl+Alt"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "daemon.edit_hotkey is invalid: missing key"): load(str(path)) def test_equal_hotkeys_raise(self): payload = {"daemon": {"hotkey": "Cmd+m", "edit_hotkey": "Cmd+m"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "must be different"): load(str(path)) def test_invalid_injection_backend_raises(self): payload = {"injection": {"backend": "invalid"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "injection.backend"): load(str(path)) def test_invalid_clipboard_remove_option_raises(self): payload = {"injection": {"remove_transcription_from_clipboard": "yes"}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "injection.remove_transcription_from_clipboard"): load(str(path)) def test_unknown_top_level_fields_are_ignored(self): payload = { "custom_a": {"enabled": True}, "custom_b": {"nested": "value"}, "custom_c": 123, } with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") cfg = load(str(path)) self.assertEqual(cfg.daemon.hotkey, "Cmd+m") self.assertEqual(cfg.daemon.edit_hotkey, "Cmd+Shift+m") self.assertEqual(cfg.injection.backend, "clipboard") def test_conflicting_replacements_raise(self): payload = { "vocabulary": { "replacements": [ {"from": "Martha", "to": "Marta"}, {"from": "martha", "to": "Martha"}, ] } } with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "conflicting"): load(str(path)) def test_duplicate_rules_and_terms_are_deduplicated(self): payload = { "vocabulary": { "replacements": [ {"from": "docker", "to": "Docker"}, {"from": "DOCKER", "to": "Docker"}, ], "terms": ["Systemd", "systemd"], } } with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") cfg = load(str(path)) self.assertEqual(len(cfg.vocabulary.replacements), 1) self.assertEqual(cfg.vocabulary.replacements[0].source, "docker") self.assertEqual(cfg.vocabulary.replacements[0].target, "Docker") self.assertEqual(cfg.vocabulary.terms, ["Systemd"]) def test_wildcard_term_raises(self): payload = { "vocabulary": { "terms": ["Dock*"], } } with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") with self.assertRaisesRegex(ValueError, "wildcard"): load(str(path)) def test_unknown_vocabulary_fields_are_ignored(self): payload = {"vocabulary": {"custom_limit": 100, "custom_extra": 200, "terms": ["Docker"]}} with tempfile.TemporaryDirectory() as td: path = Path(td) / "config.json" path.write_text(json.dumps(payload), encoding="utf-8") cfg = load(str(path)) self.assertEqual(cfg.vocabulary.terms, ["Docker"]) if __name__ == "__main__": unittest.main()