mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:27:13 +01:00
_workout_sync.py (332) -> _sync_client.py token, Firebase/GitHub client
_sync_records.py payload predicates + log decoding
_sick_tracker.py (317) -> _sick_budget.py SickHistory + rolling budget
This one was not a pure move until the fixtures followed it. Three autouse
test guards patch names by module, and all three pointed at _workout_sync:
isolate_sync_token SYNC_TOKEN_FILE -> now also on _sync_client
_no_real_firebase_config CONFIG_FILE -> now on _sync_client
_firebase_config helper (7 call sites) -> now passed _sync_client
With them stale, sync_client built a REAL client: the suite went from 14s to
over 120s and test_workout_sync_part5 asserted against live workout data
pulled from the actual backend. Fixed, the same tests run in 0.7s. Both
modules keep SYNC_TOKEN_FILE so either patch point works, and every relocated
guard target is asserted to exist.
977 tests pass at 100% branch coverage; pylint back to 8.70 after fixing the
findings the split introduced (a dropped `return None` in _parse_iso and
leftover dead imports). `screen_lock --status` unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EveB813BQ8abAP68VSqZ5Y
109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
"""Backend selection and the two public pulls, split from ``test_workout_sync``.
|
|
|
|
``sync_client`` decides WHICH backend is read; ``pull_synced_workout`` and
|
|
``pull_all_manual_records`` are what the locker actually calls.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from crdt_sync import (
|
|
ConfigError,
|
|
FirebaseAuthError,
|
|
FirebaseSyncError,
|
|
)
|
|
import pytest
|
|
|
|
from screen_locker import _sync_client, _workout_sync
|
|
from screen_locker.tests._workout_sync_fixtures import (
|
|
_firebase_config,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
|
|
class TestSyncClient:
|
|
"""Every configuration of (GitHub token, Firebase config), plus failure."""
|
|
|
|
def test_returns_none_when_nothing_is_configured(self) -> None:
|
|
"""Returns none when nothing is configured."""
|
|
assert _workout_sync.sync_client() is None
|
|
|
|
def test_warns_when_nothing_is_configured(
|
|
self, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""Warns when nothing is configured."""
|
|
with caplog.at_level("WARNING"):
|
|
_workout_sync.sync_client()
|
|
assert "sync is OFF" in caplog.text
|
|
|
|
def test_uses_github_alone_when_only_the_token_exists(self) -> None:
|
|
"""Uses github alone when only the token exists."""
|
|
_workout_sync.SYNC_TOKEN_FILE.write_text("tok")
|
|
github = MagicMock()
|
|
with patch.object(_sync_client, "GitHubSyncClient", return_value=github):
|
|
assert _workout_sync.sync_client() is github
|
|
|
|
def test_mirrors_when_both_the_token_and_firebase_exist(
|
|
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""Mirrors when both the token and firebase exist."""
|
|
_workout_sync.SYNC_TOKEN_FILE.write_text("tok")
|
|
_firebase_config(_sync_client, tmp_path, monkeypatch)
|
|
github = MagicMock()
|
|
monkeypatch.setattr(
|
|
_sync_client, "mirror_client_for", lambda _app, client: ("mirror", client)
|
|
)
|
|
with patch.object(_sync_client, "GitHubSyncClient", return_value=github):
|
|
assert _workout_sync.sync_client() == ("mirror", github)
|
|
|
|
def test_uses_firebase_alone_when_only_the_config_exists(
|
|
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""A Firebase-only machine must still sync -- no PAT required."""
|
|
_firebase_config(_sync_client, tmp_path, monkeypatch)
|
|
firebase = MagicMock()
|
|
monkeypatch.setattr(_sync_client, "firebase_client_for", lambda _app: firebase)
|
|
assert _workout_sync.sync_client() is firebase
|
|
|
|
@pytest.mark.parametrize(
|
|
"error",
|
|
[
|
|
ConfigError("no password"),
|
|
FirebaseAuthError("bad credentials"),
|
|
FirebaseSyncError("backend down"),
|
|
],
|
|
)
|
|
def test_returns_none_when_firebase_only_is_unusable(
|
|
self, error: Exception, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""No PAT to fall back to: report it loudly, do not raise."""
|
|
_firebase_config(_sync_client, tmp_path, monkeypatch)
|
|
|
|
def _boom(*_args: object, **_kwargs: object) -> None:
|
|
raise error
|
|
|
|
monkeypatch.setattr(_sync_client, "firebase_client_for", _boom)
|
|
assert _workout_sync.sync_client() is None
|
|
|
|
def test_warns_when_firebase_only_is_unusable(
|
|
self,
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Warns when firebase only is unusable."""
|
|
_firebase_config(_sync_client, tmp_path, monkeypatch)
|
|
|
|
def _boom(*_args: object, **_kwargs: object) -> None:
|
|
message = "backend down"
|
|
raise FirebaseSyncError(message)
|
|
|
|
monkeypatch.setattr(_sync_client, "firebase_client_for", _boom)
|
|
with caplog.at_level("WARNING"):
|
|
_workout_sync.sync_client()
|
|
assert "backend down" in caplog.text
|