mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:07:16 +01:00
screen_locker pushed manual workouts as "pc" and the workout app as "phone". A role constant collides the moment a second machine takes the same role, and after a reinstall the new install inherits the previous one's CRDT identity. Both halves now mint a uuid once and persist it: the PC in ~/.local/share/screen_locker/.device_id, the app under SharedPreferences 'crdt.nodeId' -- the same key every sibling app uses. The old constant survives as the legacy id and is passed to sync_log / syncLog, so devices/<legacy>/ is skipped as this device's own rather than re-downloaded and re-merged as a peer's on every tick. It drops to None once that path is reclaimed. Note the *read* loop in _workout_sync deliberately still merges every device including this one: the PC needs its own manual workouts in the merged view, so "skip own" would be wrong there. Only the push path and the HLC node id move. The test fixture redirecting the new id file is added in the same change that introduces the constant -- without it the first test that syncs mints a uuid into the real state dir and this machine's live sync identity is decided by whichever test ran first. Verified: ~/.local/share/screen_locker/ held no .device_id after a full suite run. Verified against the live backend, not just mocks: ran the real push_pc_workouts, which minted 57582b3b and pushed 9 records. In the RTDB the new device's log is a strict superset of both old paths -- 0 of devices/pc/'s 10 records and 0 of devices/phone/'s 9 are missing from it. Python: 907 tests, 100% branch coverage. workout_app: 313 tests, analyzer clean. Pins bumped to crdt-sync-v0.6.0 / crdt_sync_dart-v0.7.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZH89WEqAjuJT8WZdqc7AP
65 lines
2.2 KiB
Python
65 lines
2.2 KiB
Python
"""Isolation fixture for the phone-workout GitHub sync token.
|
|
|
|
Split out of ``conftest.py`` (already at the repo's 400-line-per-file cap)
|
|
and re-exported there so pytest still picks this up as an autouse fixture
|
|
for every test in this directory.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
from pathlib import Path
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def isolate_sync_token(tmp_path: Path) -> Iterator[None]:
|
|
"""Redirect SYNC_TOKEN_FILE to tmp_path so tests never see a real token.
|
|
|
|
Without this, any test calling ``_verify_phone_workout`` would fall
|
|
through to ``pull_synced_workout()``, which -- if a real
|
|
``~/.config/screen_locker/sync_token`` happens to exist on the host --
|
|
would make a real GitHub API call. Defaulting to a nonexistent tmp_path
|
|
file makes ``read_sync_token()`` return None, the same benign "sync not
|
|
configured" state every existing test already assumes.
|
|
"""
|
|
with patch(
|
|
"screen_locker._workout_sync.SYNC_TOKEN_FILE",
|
|
tmp_path / "sync_token",
|
|
):
|
|
yield
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def isolate_sync_device_id(tmp_path: Path) -> Iterator[None]:
|
|
"""Redirect the persisted device id so tests never mint one for real.
|
|
|
|
Without this the first test that syncs writes a uuid into the real
|
|
``~/.local/share/screen_locker/.device_id``, and this machine's live sync
|
|
identity ends up decided by whichever test happened to run first.
|
|
"""
|
|
with patch(
|
|
"screen_locker._device.SYNC_DEVICE_ID_FILE",
|
|
tmp_path / ".device_id",
|
|
):
|
|
yield
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def no_sync_retry_sleep() -> Iterator[None]:
|
|
"""Make the sync retry's backoff instant for every test.
|
|
|
|
``with_sync_retry`` waits 2s + 4s + 8s before giving up, which is right in
|
|
production (it covers the network coming up after boot/resume) but would
|
|
add ~14s to every test that exercises a sync failure. Tests that care about
|
|
the backoff assert on the patched ``sleep`` calls instead -- see
|
|
``test_sync_retry.py``.
|
|
"""
|
|
with patch("screen_locker._sync_retry.sleep"):
|
|
yield
|