diff options
Diffstat (limited to 'packages/meshbay-cli/tests')
| -rw-r--r-- | packages/meshbay-cli/tests/fake_node.py | 22 | ||||
| -rw-r--r-- | packages/meshbay-cli/tests/test_interactive.py | 43 |
2 files changed, 64 insertions, 1 deletions
diff --git a/packages/meshbay-cli/tests/fake_node.py b/packages/meshbay-cli/tests/fake_node.py index c618ee4..47f82b3 100644 --- a/packages/meshbay-cli/tests/fake_node.py +++ b/packages/meshbay-cli/tests/fake_node.py @@ -79,6 +79,8 @@ class FakeNode: self.members: dict[str, Identity] = {} # sender → the device it signs with self.unresponsive = False # pings go unanswered, as a dead link's self.admin_ops: dict[str, dict] = {} + self.channels: list[FakeChannel] = [] + self.index_syncs = 0 # full indexes asked for def add(self, path: str, name: str, data: bytes, uploader_id: str = "") -> dict: fid = hashlib.blake2b(data + path.encode() + name.encode(), digest_size=32).hexdigest() @@ -92,9 +94,26 @@ class FakeNode: return entry def session(self) -> GroupSession: - return GroupSession(None, FakeChannel(self), GROUP_ID, "node-1", NODE_PK, self.gek, + ch = FakeChannel(self) + self.channels.append(ch) + return GroupSession(None, ch, GROUP_ID, "node-1", NODE_PK, self.gek, dict(self.ack), self.identity, self.nonce, self.role) + def push_delta(self, *, additions=(), updates=(), deletions=()) -> None: + """An `index_delta` to every session, sealed as the node seals one. The + entries are applied here first, so a later `index_sync` agrees.""" + for e in (*additions, *updates): + self.files[e["id"]] = e + for fid in deletions: + self.files.pop(fid, None) + wire = lambda es: [{k: v for k, v in e.items() if k != "data"} for e in es] # noqa: E731 + payload = {"base_version": 1, "version": 1, "additions": wire(additions), + "deletions": list(deletions), "updates": wire(updates)} + msg = {"type": "index_delta", "group_id": GROUP_ID, + **seal(self.gek, PURPOSE_INDEX, "index_delta", GROUP_ID, payload)} + for ch in self.channels: + ch._emit(msg) + def list_device(self, sender: str, device: Identity, *, countersigned_by: Identity | None = None, proof: bool = True) -> None: """A roster row for `sender`'s device: a root, or countersigned by another @@ -189,6 +208,7 @@ class FakeChannel: def _index_sync(self, msg): n = self.node + n.index_syncs += 1 payload = {"version": 1, "roots": n.roots, "dirs": sorted(n.dirs), "entries": [{k: v for k, v in e.items() if k != "data"} for e in n.files.values()]} diff --git a/packages/meshbay-cli/tests/test_interactive.py b/packages/meshbay-cli/tests/test_interactive.py index 36b7f47..9960533 100644 --- a/packages/meshbay-cli/tests/test_interactive.py +++ b/packages/meshbay-cli/tests/test_interactive.py @@ -444,3 +444,46 @@ async def test_put_sends_a_file_into_the_chat_folder_and_get_saves_one_here( await say("/get ../share/x") await _until(pilot, lambda: "name a file of the chat's folder" in _said(room)) await pilot.press("escape") + + +async def test_index_updates_are_applied_not_asked_for_again(world, monkeypatch): + """ + A library being enriched pushes an `index_delta` about once a second, each + one filling in a file's fields. The panes used to answer every one with the + whole index — 67 full indexes in the 80 s of one download on a real node, + on the event loop the download was being received on. Updates are applied + where they land; only a delta that adds or removes a file asks again, once + for a burst. + """ + from meshbay_cli.tui import screens + monkeypatch.setattr(screens, "REFRESH_AFTER_DELTA_S", 0.2) + hub, node, work = world + film = node.add("share", "film.mkv", b"F" * 5000) + assert await asyncio.to_thread(cli.main, ["--hub", "https://hub.test", "-u", USERNAME, + "--password", PASSPHRASE, "login"]) == 0 + app = _app() + async with app.run_test(size=(140, 40)) as pilot: + await _until(pilot, lambda: isinstance(app.screen, MainScreen)) + remote = app.screen.query_one(RemotePane) + await _open(pilot, remote, f"Family@{USERNAME}/") + await _open(pilot, remote, "share/") + await _until(pilot, lambda: "film.mkv" in _names(remote)) + asked = node.index_syncs + + for i in range(20): + node.push_delta(updates=[{**film, "duration": 100 + i}]) + await pilot.pause(0.6) + assert node.index_syncs == asked, "an update must not fetch the whole index" + tree = app.screen._trees["g1"] + assert tree.file("share/film.mkv")["duration"] == 119 + + extra = [node.add("share", f"new{i}.txt", b"n%d" % i) for i in range(3)] + node.files = {k: v for k, v in node.files.items() if v not in extra} + for e in extra: + node.push_delta(additions=[e]) + await _until(pilot, lambda: "new2.txt" in _names(remote)) + await pilot.pause(0.6) + assert node.index_syncs == asked + 1, "a burst of additions asks again once" + + node.push_delta(deletions=[film["id"]]) + await _until(pilot, lambda: "film.mkv" not in _names(remote)) |