"""Tests for tagsync.config: Settings persistence + token storage.""" from __future__ import annotations import importlib import json import pytest @pytest.fixture def config_module(): from tagsync import config return importlib.reload(config) # --- Settings --------------------------------------------------------------- def test_settings_defaults(config_module): s = config_module.Settings() assert s.local_root == "" assert s.platform == "" assert s.skip_online_only is True assert s.write_sidecars is False assert s.schedule_installed is False assert s.schedule_interval_minutes == 60 def test_settings_roundtrip(config_module, tmp_path): path = tmp_path / "cfg.json" original = config_module.Settings( local_root="/home/u/Dropbox", platform="macos", skip_online_only=False, schedule_installed=True, schedule_interval_minutes=30, ) original.save(path) loaded = config_module.Settings.load(path) assert loaded == original def test_settings_load_missing_file_returns_defaults(config_module, tmp_path): loaded = config_module.Settings.load(tmp_path / "nope.json") assert loaded == config_module.Settings() def test_settings_load_ignores_unknown_keys(config_module, tmp_path): """Older configs should load even if a new field was removed.""" path = tmp_path / "cfg.json" path.write_text(json.dumps({ "local_root": "/x", "platform": "macos", "future_field_we_dont_know_about": 42, })) loaded = config_module.Settings.load(path) assert loaded.local_root == "/x" assert loaded.platform == "macos" # --- Token storage (fallback file path) ------------------------------------- def test_token_fallback_roundtrip(config_module, monkeypatch): """Force the non-keyring fallback path and verify round-trip.""" monkeypatch.setattr(config_module, "_get_keyring", lambda: None) assert config_module.load_token() is None location = config_module.save_token("sl.test-token-123") assert "plaintext" in location assert config_module.load_token() == "sl.test-token-123" config_module.clear_token() assert config_module.load_token() is None def test_token_fallback_file_permissions(config_module, monkeypatch): """The fallback token file should be mode 0600 where supported.""" import os import stat monkeypatch.setattr(config_module, "_get_keyring", lambda: None) config_module.save_token("sl.perm-test") mode = os.stat(config_module.TOKEN_FALLBACK_FILE).st_mode # Skip on Windows where POSIX modes aren't enforced the same way. if os.name == "posix": assert stat.S_IMODE(mode) == 0o600 @pytest.mark.needs_keyring def test_token_keyring_roundtrip(config_module): """If keyring is installed, verify we actually use it.""" pytest.importorskip("keyring") if config_module._get_keyring() is None: pytest.skip("keyring not usable") import keyring # Use the in-memory fallback backend to avoid touching real Keychain. from keyring.backends import fail as _fail # noqa: F401 (ensure package) try: import keyring.backends.null keyring.set_keyring(keyring.backends.null.Keyring()) except Exception: pytest.skip("no safe keyring backend available for tests") # Null keyring swallows set/get — just exercise code paths. config_module.save_token("sl.kr-test") config_module.clear_token()