summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_root_writable_policy.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-18 15:59:24 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-18 15:59:24 +0200
commit5fa158fab709d3d24a33318b3d910f75c051af2e (patch)
tree937a475e878b248cb3415e4fa23303988ac21221 /packages/meshbay-node/tests/test_root_writable_policy.py
parent79b8f770ab319cf8d64132a56fc0036dcf0486f7 (diff)
downloadmeshbay-5fa158fab709d3d24a33318b3d910f75c051af2e.tar.gz
fix(node): take the availability poll and every upload write off the loop
The rest of AV9's disk half. Serving a file left the loop in the commit before this one; two paths were still on it. **The availability poll.** `RootSet.refresh_availability` stats every root, and eleven call sites reached it from `async def` — the reconcile loop among them, on a timer. On a sleeping disk that is a stall once per tick, and the stat is also what keeps the disk awake, so a node paid spin-up for a library nobody was reading. All eleven now go through `off_disk`, `Root.is_live` included. **The upload write.** `open`/`write`, and the resolve, the stat, the free-name search, the rename and the unlink around it. This one could not simply be awaited: the handler was synchronous, so nothing could come between the `chunk_index != state.next_index` check and the `advance` that answers it, and that is the whole of the chunk-ordering rule. Awaiting the write opens the gap — chunk 1 arriving while chunk 0 is in the disk thread reads a position that has not moved and is refused as out of order, so an upload would fail on a slow disk and nowhere else. Verified, not assumed: without the lock the new ordering test refuses three chunks of four. So the check, the write and the advance are one critical section again, under a lock held **per group**. Not per session: `partial_uploads` lives in the group context so a reconnecting client finds its upload where it left it, which means two sessions of one member share the position of one `.part` file. Arrival order is preserved by construction — the dispatcher creates one task per message as it arrives, tasks start in creation order, and the lock is the first thing each one waits on, so its waiters queue in arrival order too. `_do_file_upload` is a coroutine now, which is why forty-two test call sites gain an `await`. Their outcomes are unchanged, file by file, against the run before the change. `test_ops.py` asked which public coroutines `ops` exposes and got `off_disk`, imported rather than defined there. It now asks for the ones written in the module, which is what its own docstring means; all forty-three operations are still checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_root_writable_policy.py')
-rw-r--r--packages/meshbay-node/tests/test_root_writable_policy.py12
1 files changed, 6 insertions, 6 deletions
diff --git a/packages/meshbay-node/tests/test_root_writable_policy.py b/packages/meshbay-node/tests/test_root_writable_policy.py
index 730e636..23b55cb 100644
--- a/packages/meshbay-node/tests/test_root_writable_policy.py
+++ b/packages/meshbay-node/tests/test_root_writable_policy.py
@@ -64,8 +64,8 @@ def _session(tmp_path: Path, user_id: str, *,
return session
-def _upload(session, filename="clip.mp4", body=b"bytes"):
- session._do_file_upload(sealed_upload(
+async def _upload(session, filename="clip.mp4", body=b"bytes"):
+ await session._do_file_upload(sealed_upload(
session, filename=filename, data=body, dir="shared"))
@@ -79,7 +79,7 @@ def _uploads_dir(session) -> Path:
async def test_a_member_cannot_upload_to_a_read_only_root(tmp_path):
session = _session(tmp_path, "member-1", writable=False)
- _upload(session)
+ await _upload(session)
refusal = [m for m in session.sent if m.get("type") == "error"]
assert refusal and refusal[0].get("code") == "root_read_only"
@@ -88,7 +88,7 @@ async def test_a_member_cannot_upload_to_a_read_only_root(tmp_path):
async def test_members_upload_normally_to_a_writable_root(tmp_path):
session = _session(tmp_path, "member-1", writable=True)
- _upload(session)
+ await _upload(session)
assert not [m for m in session.sent if m.get("type") == "error"]
assert (_uploads_dir(session) / "clip.mp4").read_bytes() == b"bytes"
@@ -103,7 +103,7 @@ async def test_read_only_binds_the_operator_too(tmp_path):
session = _session(tmp_path, "the-operator", writable=False,
operator="the-operator")
session._is_node_admin = lambda: True
- _upload(session)
+ await _upload(session)
refusal = [m for m in session.sent if m.get("type") == "error"]
assert refusal and refusal[0].get("code") == "root_read_only"
@@ -249,6 +249,6 @@ async def test_no_message_can_reopen_uploads_for_a_whole_group(tmp_path):
"the node answered an instruction it does not implement")
# And the door is still shut.
- _upload(session)
+ await _upload(session)
refusal = [m for m in session.sent if m.get("type") == "error"]
assert refusal and refusal[0].get("code") == "root_read_only"