Files
dropbox_helper/tests/test_config.py
T
jared e363020080
CI / Tests (Linux, Python 3.12) (push) Failing after 4m51s
CI / Tests (Linux, Python 3.11) (push) Failing after 5m19s
CI / Tests (Linux, Python 3.9) (push) Failing after 5m3s
CI / Tests (macOS) (push) Has been cancelled
CI / Tests (Windows) (push) Has been cancelled
feat: initial Dropbox tag sync tool
Cross-platform CLI that mirrors Dropbox web tags to OS-native tags:
- macOS: Finder xattrs (content-preserving, no re-upload)
- Windows: XMP:Subject via ExifTool (re-upload expected once per file)

Includes an interactive wizard that auto-detects the OS and Dropbox
folder, walks the user through read-only token creation, previews
tagged files, applies tags, and installs a background scheduler
(launchd on macOS, Task Scheduler on Windows).

Ships with:
- 47-test suite (pytest) covering config, state DB, run_sync, writers
- Gitea Actions CI for Linux / macOS / Windows with coverage reporting
2026-04-20 14:16:50 -04:00

115 lines
3.4 KiB
Python

"""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, "_keyring_available", lambda: False)
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, "_keyring_available", lambda: False)
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 not config_module._keyring_available():
pytest.skip("keyring not importable")
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()