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
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
110 lines
3.4 KiB
Python
110 lines
3.4 KiB
Python
"""Tests for tagsync.detect: platform detection + Dropbox folder probing."""
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import json
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def detect_module():
|
|
from tagsync import detect
|
|
return importlib.reload(detect)
|
|
|
|
|
|
def test_detect_platform_returns_known_value(detect_module):
|
|
assert detect_module.detect_platform() in {"macos", "windows", "linux"}
|
|
|
|
|
|
def test_platform_label_nonempty(detect_module):
|
|
assert detect_module.platform_label()
|
|
|
|
|
|
def test_python_label_nonempty(detect_module):
|
|
label = detect_module.python_label()
|
|
assert label.startswith("Python ")
|
|
|
|
|
|
def test_has_module_true_for_stdlib(detect_module):
|
|
assert detect_module.has_module("json")
|
|
assert detect_module.has_module("pathlib")
|
|
|
|
|
|
def test_has_module_false_for_garbage(detect_module):
|
|
assert not detect_module.has_module("definitely_not_a_real_module_xyz")
|
|
|
|
|
|
def test_detect_dropbox_folder_from_info_json(detect_module, tmp_path, monkeypatch):
|
|
"""When Dropbox's info.json exists, we should parse its path field."""
|
|
dropbox_dir = tmp_path / "Dropbox"
|
|
dropbox_dir.mkdir()
|
|
info_dir = tmp_path / ".dropbox"
|
|
info_dir.mkdir()
|
|
info = info_dir / "info.json"
|
|
info.write_text(json.dumps({
|
|
"personal": {"path": str(dropbox_dir), "host": 12345},
|
|
}))
|
|
|
|
monkeypatch.setattr(
|
|
detect_module,
|
|
"_dropbox_info_candidates",
|
|
lambda: [info],
|
|
)
|
|
result = detect_module.detect_dropbox_folder()
|
|
assert result == dropbox_dir
|
|
|
|
|
|
def test_detect_dropbox_folder_handles_malformed_json(detect_module, tmp_path, monkeypatch):
|
|
info = tmp_path / "info.json"
|
|
info.write_text("not valid json {{{")
|
|
|
|
monkeypatch.setattr(
|
|
detect_module,
|
|
"_dropbox_info_candidates",
|
|
lambda: [info],
|
|
)
|
|
# Should fall back silently, not raise.
|
|
result = detect_module.detect_dropbox_folder()
|
|
# No fallback path in tmp, so None is acceptable.
|
|
assert result is None or result.is_dir()
|
|
|
|
|
|
def test_detect_dropbox_folder_returns_none_if_missing(detect_module, monkeypatch):
|
|
from pathlib import Path
|
|
monkeypatch.setattr(detect_module, "_dropbox_info_candidates", lambda: [])
|
|
monkeypatch.setattr(Path, "home", staticmethod(lambda: Path("/definitely/does/not/exist")))
|
|
assert detect_module.detect_dropbox_folder() is None
|
|
|
|
|
|
def test_detect_dropbox_folder_prefers_personal_over_business(detect_module, tmp_path, monkeypatch):
|
|
personal = tmp_path / "Dropbox-Personal"
|
|
business = tmp_path / "Dropbox-Business"
|
|
personal.mkdir()
|
|
business.mkdir()
|
|
info = tmp_path / "info.json"
|
|
info.write_text(json.dumps({
|
|
"personal": {"path": str(personal)},
|
|
"business": {"path": str(business)},
|
|
}))
|
|
monkeypatch.setattr(
|
|
detect_module,
|
|
"_dropbox_info_candidates",
|
|
lambda: [info],
|
|
)
|
|
assert detect_module.detect_dropbox_folder() == personal
|
|
|
|
|
|
def test_find_exiftool_uses_shutil_which(detect_module, monkeypatch):
|
|
import shutil
|
|
monkeypatch.setattr(shutil, "which", lambda name: "/usr/bin/exiftool" if name == "exiftool" else None)
|
|
result = detect_module.find_exiftool()
|
|
assert result is not None
|
|
assert result.name == "exiftool"
|
|
|
|
|
|
def test_find_exiftool_returns_none_when_absent(detect_module, monkeypatch):
|
|
import shutil
|
|
monkeypatch.setattr(shutil, "which", lambda _name: None)
|
|
assert detect_module.find_exiftool() is None
|