utils/crdt-sync/crdt_sync/tests/test_device.py
Krzysztof kuhy Rudnicki 6154dc09bf Pin pre-commit's ruff to 0.16.7 and satisfy the CPY001 it brings
The three Python packages pinned ruff twice at different versions:
requirements.txt at 0.16.7, .pre-commit-config.yaml at v0.15.2. Only the
older one ever ran, so `select = ["ALL"]` was enforced two minor versions
behind. Measured before aligning: v0.15.2 reports 0 findings, every 0.16.x
reports CPY001 on each file -- 70 crdt-sync, 23 freedays, 71 gatelock, and
no other rule.

Resolved by adding the notice rather than ignoring the rule, so the gate
keeps its teeth.

Two gatelock modules sat at exactly the 250-line cap, so a one-line header
tipped them to 251. Both are split along a real seam rather than shaved:

  _widget_fills.py    colour arithmetic over LockConfig, no tkinter import
  _surfaces_focus.py  selects among existing surfaces, never mutates them

The focus split keeps _surfaces.py's documented containment (it stays the
only module that creates, moves or destroys windows), and the static guard
in test_recovery_invariants.py globs _surfaces*.py, so the new sibling is
covered with no list to extend. widgets.py re-exports the fills helpers,
which keeps gatelock.widgets the single public surface its tests import.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019kMvSMtsVMB3d2uoZ56pbp
2026-09-12 18:11:24 +02:00

115 lines
3.8 KiB
Python

# Copyright (c) 2026 Krzysztof Rudnicki
"""Tests for this device's persisted sync identity."""
from __future__ import annotations
from typing import TYPE_CHECKING
import uuid
import pytest
from crdt_sync import DeviceIdentity, load_device_identity
if TYPE_CHECKING:
from pathlib import Path
def test_mints_a_uuid_on_first_call(tmp_path: Path) -> None:
"""A device with no id file gets a fresh uuid4, persisted."""
path = tmp_path / "device_id"
identity = load_device_identity(path)
assert uuid.UUID(identity.device_id).version == 4
assert path.read_text(encoding="utf-8").strip() == identity.device_id
def test_returns_the_same_id_on_every_later_call(tmp_path: Path) -> None:
"""The id is stable across restarts -- that is the whole point."""
path = tmp_path / "device_id"
first = load_device_identity(path)
second = load_device_identity(path)
assert first.device_id == second.device_id
def test_creates_missing_parent_directories(tmp_path: Path) -> None:
"""First run on a clean machine has no state dir yet."""
path = tmp_path / "nested" / "deeper" / "device_id"
identity = load_device_identity(path)
assert path.read_text(encoding="utf-8").strip() == identity.device_id
def test_treats_a_blank_file_as_absent(tmp_path: Path) -> None:
"""An empty/whitespace file (interrupted first write) re-mints."""
path = tmp_path / "device_id"
path.write_text(" \n", encoding="utf-8")
identity = load_device_identity(path)
assert uuid.UUID(identity.device_id).version == 4
def test_reads_an_id_that_is_not_a_uuid(tmp_path: Path) -> None:
"""Whatever is on disk wins; the file is the source of truth."""
path = tmp_path / "device_id"
path.write_text("hand-edited-id\n", encoding="utf-8")
assert load_device_identity(path).device_id == "hand-edited-id"
def test_a_file_blocking_the_state_dir_surfaces_as_an_error(tmp_path: Path) -> None:
"""A file where the state directory belongs is a real misconfiguration.
The read is tolerant (it reads as "no id yet"), but the subsequent mkdir
must not be: silently continuing would hand back an id that was never
persisted, so every restart would mint a new one and strand a device
directory in the namespace each time.
"""
blocker = tmp_path / "blocker"
blocker.write_text("not a directory\n", encoding="utf-8")
with pytest.raises((NotADirectoryError, FileExistsError)):
load_device_identity(tmp_path / "blocker" / "device_id")
def test_legacy_id_is_carried_onto_the_identity(tmp_path: Path) -> None:
"""The pre-migration role constant travels with the new uuid."""
identity = load_device_identity(tmp_path / "device_id", legacy_id="pc")
assert identity.legacy_id == "pc"
def test_legacy_id_is_carried_when_loading_an_existing_id(tmp_path: Path) -> None:
"""The load branch carries it too, not just the mint branch."""
path = tmp_path / "device_id"
path.write_text("existing-uuid\n", encoding="utf-8")
identity = load_device_identity(path, legacy_id="pc")
assert (identity.device_id, identity.legacy_id) == ("existing-uuid", "pc")
def test_own_ids_includes_both_ids_when_migrating() -> None:
"""Skip-own must match the old path as well as the new one."""
identity = DeviceIdentity(device_id="new-uuid", legacy_id="pc")
assert identity.own_ids == frozenset({"new-uuid", "pc"})
def test_own_ids_is_just_the_uuid_once_the_old_path_is_reclaimed() -> None:
"""After GC there is no legacy id left to skip."""
assert DeviceIdentity(device_id="new-uuid").own_ids == frozenset({"new-uuid"})
def test_is_own_matches_either_id() -> None:
"""Both the current and the former id mean "this device"."""
identity = DeviceIdentity(device_id="new-uuid", legacy_id="pc")
assert identity.is_own("new-uuid")
assert identity.is_own("pc")
assert not identity.is_own("phone")