diff options
Diffstat (limited to 'packages/meshbay-node/tests')
| -rw-r--r-- | packages/meshbay-node/tests/test_root_availability.py | 3 | ||||
| -rw-r--r-- | packages/meshbay-node/tests/test_root_writable_policy.py | 36 | ||||
| -rw-r--r-- | packages/meshbay-node/tests/test_roots.py | 17 |
3 files changed, 31 insertions, 25 deletions
diff --git a/packages/meshbay-node/tests/test_root_availability.py b/packages/meshbay-node/tests/test_root_availability.py index d514dee..2d848c7 100644 --- a/packages/meshbay-node/tests/test_root_availability.py +++ b/packages/meshbay-node/tests/test_root_availability.py @@ -121,8 +121,7 @@ async def test_members_are_told_which_roots_are_unavailable(tmp_path): idx = await _indexer(_roots(films)) assert idx.index.roots == [ {"name": "Films", "kind": "generic", "available": True, - "writable": True, "removable": False, "ejected": False, - "upload": True}] + "writable": True, "removable": False, "ejected": False}] (films / "a.mkv").unlink() films.rmdir() diff --git a/packages/meshbay-node/tests/test_root_writable_policy.py b/packages/meshbay-node/tests/test_root_writable_policy.py index 8345880..730e636 100644 --- a/packages/meshbay-node/tests/test_root_writable_policy.py +++ b/packages/meshbay-node/tests/test_root_writable_policy.py @@ -1,10 +1,11 @@ """ Who may write to the operator's disk, now that RO/RW on the root decides it. -This replaces `test_member_upload_policy.py`. The old model had two orthogonal -controls — one root designated as the upload target, and a group-wide -`member_upload` switch — and collapsed into one property per root: `writable`. -The properties worth keeping from the old file survive the change unaltered: +One property per root — `writable` — and no second control anywhere: not a +group-wide switch, not a designated upload target. Two controls for one question +is a question answered differently depending on which is read first. + +The properties this holds: * the interface hiding a control is a courtesy to the people who are not trying; **the node refusing is the part that holds** against someone who is. @@ -220,25 +221,32 @@ async def test_a_request_with_nobody_to_authorize_it_is_refused(tmp_path): assert [m for m in session.sent if m.get("type") == "error"] -# ── The deprecated message must not still work ─────────────────────────────── +# ── There is no group-wide upload switch ───────────────────────────────────── -async def test_the_old_member_upload_message_changes_nothing(tmp_path): +async def test_no_message_can_reopen_uploads_for_a_whole_group(tmp_path): """ - MNP still parses `member_upload` so an old client gets an answer instead of - a dropped request. What it must not do is act: this instruction could - reopen uploads for a whole group, and a client old enough to send it is - exactly one that knows nothing about read-only roots. + Whether a member may write is a property of each root, and there is no + second way to say it. + + A group-wide switch is the thing this model replaced, and it cannot come + back by accident: the type does not exist, so a peer asking for it is a peer + the dispatcher logs and ignores. What must never happen is what a switch + would have allowed — one instruction turning a published, read-only library + into a writable one. """ + assert not hasattr(MNP, "MEMBER_UPLOAD"), ( + "a group-wide upload switch is back in the protocol") + session = _session(tmp_path, "member-1", writable=False) session._has_admin_authority = lambda: True issued = _capture_challenges(session) - session._do_member_upload({"allowed": True}) + session._dispatch_message({"type": "member_upload", "allowed": True}) - assert issued == [], "a deprecated instruction asked to be signed" + assert issued == [], "an unknown instruction asked to be signed" assert session._ctx["roots"].roots[0].writable is False - acks = [m for m in session.sent if m.get("type") == MNP.MEMBER_UPLOAD_ACK] - assert acks and acks[0].get("deprecated") is True + assert not [m for m in session.sent if "upload" in str(m.get("type"))], ( + "the node answered an instruction it does not implement") # And the door is still shut. _upload(session) diff --git a/packages/meshbay-node/tests/test_roots.py b/packages/meshbay-node/tests/test_roots.py index 505091b..9233180 100644 --- a/packages/meshbay-node/tests/test_roots.py +++ b/packages/meshbay-node/tests/test_roots.py @@ -272,27 +272,26 @@ def test_describe_reports_what_a_member_needs(tmp_path): described = roots.describe() assert described == [ {"name": "Media", "kind": "generic", "available": True, - "writable": True, "removable": False, "ejected": False, - "upload": True}, + "writable": True, "removable": False, "ejected": False}, {"name": "Music", "kind": "audio", "available": True, - "writable": False, "removable": True, "ejected": False, - "upload": False}, + "writable": False, "removable": True, "ejected": False}, ] # Deliberately no paths: a member is told what exists and whether it is # readable, not where on the operator's disk it lives. assert not any("path" in d for d in described) -def test_describe_still_carries_upload_for_mnp_1_0_clients(tmp_path): +def test_describe_names_a_root_and_never_a_path(tmp_path): """ - `upload` is `writable` under its old name, kept because an MNP 1.0 client - reads no other field and would otherwise decide the group takes no uploads - at all. It is derived, never stored — the two can never disagree. + What a member is told about a root: that it exists, and whether it is + readable and writable. Never where on the operator's disk it lives — that + is the operator's own view (`with_paths`), over their own channel. """ (tmp_path / "Media").mkdir() roots = RootSet.build([_spec(tmp_path / "Media", writable=True)]) described = roots.describe()[0] - assert described["upload"] == described["writable"] is True + assert set(described) == {"name", "kind", "available", "writable", + "removable", "ejected"} # ── SAFE_UPLOAD_NAME ──────────────────────────────────────────────────────── |