Files
dropbox_helper/tests/conftest.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

123 lines
3.2 KiB
Python

"""Shared pytest fixtures and helpers."""
from __future__ import annotations
import sys
from dataclasses import dataclass
from pathlib import Path
import pytest
# --- Isolate persistent state paths from the real user's home ---------------
@pytest.fixture(autouse=True)
def _isolate_user_state(tmp_path, monkeypatch):
"""Redirect all per-user state files into a tmp dir.
Ensures tests never clobber real settings, tokens, or state DB on the
developer's machine.
"""
fake_home = tmp_path / "home"
fake_home.mkdir()
monkeypatch.setattr(Path, "home", staticmethod(lambda: fake_home))
# Re-import config so its module-level paths re-evaluate against the
# patched home. We clear and re-import to reset cached module constants.
import importlib
from tagsync import config
importlib.reload(config)
yield
# Leave things as-is after the test; tmp_path cleanup handles disk.
# --- Fake Dropbox API objects ------------------------------------------------
#
# The Dropbox SDK may or may not be installed in the test env. We don't want
# to require it, and the parts of core.py that need it are only reached via
# helpers we can monkeypatch out.
@dataclass
class FakeFileMetadata:
"""Stand-in for dropbox.files.FileMetadata."""
name: str
path_lower: str
path_display: str
@dataclass
class FakeUserTag:
tag_text: str
class FakeTag:
"""Stand-in for dropbox.files.Tag (union type)."""
def __init__(self, tag_text: str) -> None:
self._user = FakeUserTag(tag_text)
def get_user_generated_tag(self) -> FakeUserTag:
return self._user
@dataclass
class FakePathToTags:
path: str
tags: list
@dataclass
class FakeTagsResult:
paths_to_tags: list
@pytest.fixture
def patched_core(monkeypatch):
"""Monkeypatch core's Dropbox-dependent helpers to controllable fakes.
Tests receive a `state` dict where they can configure:
- `files`: list of FakeFileMetadata returned by _list_all_files
- `tags_by_path`: dict[path_lower -> list[str]] returned by _fetch_tags
"""
from tagsync import core
state = {
"files": [],
"tags_by_path": {},
"fetch_calls": 0,
}
def fake_client(_token):
return object() # placeholder; never used because list/fetch are faked
def fake_list(_dbx):
return list(state["files"])
def fake_fetch(_dbx, files):
state["fetch_calls"] += 1
for f in files:
tag_names = sorted(state["tags_by_path"].get(f.path_lower, []))
yield f, tag_names
monkeypatch.setattr(core, "_dropbox_client", fake_client)
monkeypatch.setattr(core, "_list_all_files", fake_list)
monkeypatch.setattr(core, "_fetch_tags", fake_fetch)
return state
# --- Marker-based skips ------------------------------------------------------
def pytest_collection_modifyitems(config, items):
platform_skip = {
"macos": sys.platform != "darwin",
"windows": sys.platform != "win32",
}
for item in items:
for mark_name, should_skip in platform_skip.items():
if mark_name in item.keywords and should_skip:
item.add_marker(
pytest.mark.skip(reason=f"requires {mark_name}")
)