mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 17:47:23 +01:00
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
A real StrongLifts session on 2026-08-10 never reached this machine, so it had to be logged as a manual workout -- spending a manual budget slot on a workout that was genuinely verified. The session was in Firebase the whole time; four separate defects each hid it. - pull_synced_workout() read a hardcoded devices/phone/log.json. Sinceffc6c72the phone pushes under a per-install uuid, so that directory has been frozen since 2026-07-17 and the PC kept returning a stale session from 07-27 as if it were current. Both readers now walk EVERY device directory; reinstalls keep minting new ones, so no single path can be right for long. - A session is identified by SHAPE (it has `exercises`), not by a `kind` field the phone never writes. That filter is why the one reader that did walk every directory discarded real sessions, and it also kept PC-origin runnerup/phone_verified records from being handed to a caller expecting a session. - FirebaseSyncError is a SIBLING of GitHubSyncError, not a subclass, so `except GitHubSyncError` never caught it and a DNS failure took the whole locker down (status=1/FAILURE, then a restart loop). Catch the common parent; a pull failure degrades to a warning. - A GitHub token is no longer required to sync. Firebase has been primary sinceeb4ff01, so a Firebase-only machine was reporting "sync is OFF" while syncing fine. Sync also only ran once per locker start, so a workout finished after login was invisible until the next login. workout-sync.timer runs the new headless --sync-only every 15 minutes; remove_systemd.sh now removes every unit the installer creates, not just two of them. workout_app: the 2026-08-05 reinstall wiped progression because the DB seeds factory defaults before the backup is restored, and the first write then exported those defaults over the only off-device copy. Storage permission is now settled before the DB opens, a defaults-only DB refuses to overwrite a richer backup, and the three swallowed storage errors say what broke. Sessions that exist only in Firebase/GitHub are restored into local history, so a reinstalled app has the history it actually earned rather than starting at whatever survived. Matched on (date, start_time), so re-syncing never duplicates and local rows always win. Adds a target-reps editor: progression can only ever RAISE reps, so a rep target a defaults re-seed set wrong could not be corrected from the UI at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JS5V44jkHZiH1wC7BH5h5n
98 lines
3.1 KiB
Python
98 lines
3.1 KiB
Python
"""Record/payload builders shared by the workout-sync test modules.
|
|
|
|
Split out of ``test_workout_sync`` (and imported by it and its ``_part2``)
|
|
so neither file exceeds the repo's 400-line limit. Everything here builds
|
|
crdt-sync wire data the way the phone app actually emits it -- in
|
|
particular ``_session_payload`` carries NO ``kind`` field, because session
|
|
detection is by shape, and a fixture that invented a ``kind`` would hide
|
|
the very bug these tests exist to pin down.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import MagicMock
|
|
|
|
from crdt_sync import Hlc, Record
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
|
|
import pytest
|
|
|
|
|
|
def _record_json(payload: dict, *, wall_time_ms: int = 1000) -> dict:
|
|
hlc = Hlc(wall_time_ms=wall_time_ms, counter=0, node_id="phone")
|
|
record = Record(id="x", fields={"payload": (payload, hlc)})
|
|
return record.to_dict()
|
|
|
|
|
|
def _session_payload(**extra: object) -> dict:
|
|
"""A StrongLifts session as the phone app's ``toJson()`` actually emits it.
|
|
|
|
Deliberately carries NO ``kind`` field: session detection is by shape (an
|
|
``exercises`` list), and keying it on ``kind`` is the bug that made real
|
|
sessions invisible.
|
|
"""
|
|
payload: dict = {
|
|
"exercises": [{"name": "Squat", "sets": 5}],
|
|
"duration_seconds": 1800,
|
|
}
|
|
payload.update(extra)
|
|
return payload
|
|
|
|
|
|
def _session_record_dict(
|
|
record_id: str, payload: dict, *, wall_time_ms: int = 1000
|
|
) -> dict:
|
|
hlc = Hlc(wall_time_ms=wall_time_ms, counter=0, node_id="phone")
|
|
return Record(id=record_id, fields={"payload": (payload, hlc)}).to_dict()
|
|
|
|
|
|
def _manual_payload(**extra: object) -> dict:
|
|
payload = {"kind": "manual_workout", "date": "2026-07-13"}
|
|
payload.update(extra)
|
|
return payload
|
|
|
|
|
|
def _manual_record_dict(
|
|
record_id: str, payload: dict, *, wall_time_ms: int = 1000
|
|
) -> dict:
|
|
hlc = Hlc(wall_time_ms=wall_time_ms, counter=0, node_id="phone")
|
|
return Record(id=record_id, fields={"payload": (payload, hlc)}).to_dict()
|
|
|
|
|
|
def _multi_device_client(device_logs: dict[str, object]) -> MagicMock:
|
|
"""A client whose ``devices/<id>/log.json`` tree is ``device_logs``.
|
|
|
|
An ``Exception`` value is raised on fetch, so a single dict describes
|
|
healthy, missing and unreachable devices at once.
|
|
"""
|
|
client = MagicMock()
|
|
client.list_directory.return_value = list(device_logs)
|
|
|
|
def _get(path: str) -> object:
|
|
device = path.split("/")[-2]
|
|
value = device_logs[device]
|
|
if isinstance(value, Exception):
|
|
raise value
|
|
return value
|
|
|
|
client.get_file_text.side_effect = _get
|
|
return client
|
|
|
|
|
|
def _firebase_config(
|
|
module: ModuleType, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> Path:
|
|
"""Point CONFIG_FILE at a real file so the Firebase branch is taken.
|
|
|
|
The autouse ``_no_real_firebase_config`` fixture aims it at a nonexistent
|
|
path, so any test wanting the Firebase side must opt back in explicitly.
|
|
"""
|
|
config = tmp_path / "firebase.json"
|
|
config.write_text("{}", encoding="utf-8")
|
|
monkeypatch.setattr(module, "CONFIG_FILE", config)
|
|
return config
|