v0.27.0: Cassette invites — one paste connects two friends
- Connections ▸ Share Library with a Friend… makes a one-time invite code (device id, display name, token) and the inviter's send-only folder. - Connections ▸ Add Friend Library… takes the pasted code: adds the inviter, shares our folder, and accepts theirs receive-only when it's offered back. - Syncthing hides an unknown device's folders (verified on 1.30), so the inviter probes a new knock only while an invite is open: added with nothing shared, completed on the right token, otherwise removed and never probed again. Used and cancelled codes go nowhere. - Sync Settings lists open invites (Cancel Invite) and gives each friend a page: avatar, plain-language status with a next step, last seen, last synced, Remove Friend. Removals are staged until OK. - scripts/cassette_pair.py runs throwaway Syncthings on loopback; pytest --syncthing drives a real three-machine handshake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,3 +79,51 @@ def jpeg_bytes(tmp_path):
|
||||
if result.returncode != 0:
|
||||
pytest.skip("ffmpeg could not generate jpeg fixture")
|
||||
return path.read_bytes()
|
||||
|
||||
|
||||
# ---- real-Syncthing tests (Cassette) ----
|
||||
# Two throwaway Syncthing instances on loopback (scripts/cassette_pair.py).
|
||||
# Slow-ish (a handshake takes Syncthing some seconds), so opt-in:
|
||||
# python3 -m pytest -m syncthing --syncthing
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--syncthing", action="store_true",
|
||||
help="run tests that start two real Syncthing instances")
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line(
|
||||
"markers", "syncthing: starts two real Syncthing instances "
|
||||
"(opt in with --syncthing)")
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if config.getoption("--syncthing"):
|
||||
return
|
||||
skip = pytest.mark.skip(reason="needs --syncthing")
|
||||
for item in items:
|
||||
if "syncthing" in item.keywords:
|
||||
item.add_marker(skip)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def syncthing_pair(tmp_path):
|
||||
"""[alice, bob, carol] Instances from scripts/cassette_pair.py, stopped
|
||||
after."""
|
||||
if shutil.which("syncthing") is None:
|
||||
pytest.skip("syncthing isn't installed")
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"cassette_pair",
|
||||
Path(__file__).resolve().parent.parent / "scripts" / "cassette_pair.py")
|
||||
pair = importlib.util.module_from_spec(spec)
|
||||
import sys
|
||||
sys.modules[spec.name] = pair # dataclasses look their module up there
|
||||
spec.loader.exec_module(pair)
|
||||
out = tmp_path / "pair"
|
||||
instances = pair.start(out, names=("alice", "bob", "carol"))
|
||||
try:
|
||||
yield instances
|
||||
finally:
|
||||
pair.stop(out)
|
||||
|
||||
Reference in New Issue
Block a user