summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_transport_contracts.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_transport_contracts.py')
-rw-r--r--packages/meshbay-hub/tests/test_transport_contracts.py50
1 files changed, 42 insertions, 8 deletions
diff --git a/packages/meshbay-hub/tests/test_transport_contracts.py b/packages/meshbay-hub/tests/test_transport_contracts.py
index b462242..879062b 100644
--- a/packages/meshbay-hub/tests/test_transport_contracts.py
+++ b/packages/meshbay-hub/tests/test_transport_contracts.py
@@ -307,26 +307,60 @@ def test_no_setter_survives_the_state_it_belonged_to():
# ── Parallel uploads ──────────────────────────────────────────────────────────
-def test_an_upload_refusal_names_the_file_it_is_about(transport):
+def test_an_upload_refusal_names_the_upload_it_is_about(transport):
"""Reported 2026-08-16: a second upload started in parallel killed both.
- An error used to carry no filename, so the client could not tell whose it
- was and failed every upload in flight — one name the node disliked took the
- other file with it. The node names the file now, and only that upload stops.
+ An error used to carry nothing identifying, so the client could not tell
+ whose it was and failed every upload in flight — one name the node disliked
+ took the other file with it.
+
+ The node named the *file* until MNP 2.0 and names the `upload_id` now: the
+ filename moved inside the seal, and echoing it in clear so the two sides
+ could match on it would give back precisely what sealing the upload is for.
+ The property is unchanged — one refusal, one failed upload.
"""
body = transport[transport.index("if (msg.type === 'error' && this._uploaders.size)"):]
body = body[:body.index("\n if (msg.type === 'chat_msg'")]
- assert "this._uploaders.has(msg.filename)" in body, (
+ assert "this._uploaders.has(msg.upload_id)" in body, (
"a named refusal must reach one uploader, not all of them")
- assert "if (!msg.filename)" in body, (
+ assert "if (!msg.upload_id)" in body, (
"an unnamed error from an older node must still stop everything — "
"guessing which upload it belongs to would be worse")
-def test_uploads_are_tracked_per_file(transport):
+def test_uploads_are_tracked_per_upload(transport):
"""Acks interleave when two files are in flight."""
assert "this._uploaders = new Map()" in transport
- assert "this._uploaders.set(file.name" in transport
+ assert "this._uploaders.set(uploadId" in transport
+ # And the "already being uploaded" guard still speaks in filenames, because
+ # that is what the caller passed and what it would recognise in the error.
+ assert "this._inFlightUploads.has(file.name)" in transport
+
+
+def test_the_upload_itself_is_sealed(transport):
+ """
+ MNP 2.0. The filename, the destination and the bytes go inside the seal
+ together — sealing the content and announcing the name beside it would be
+ theatre — and only what the node routes on stays outside.
+ """
+ start = transport.index(" async uploadFile(file,")
+ body = transport[start:transport.index("\n /** Create a directory", start)]
+ assert "sealGroup(" in body and "'file_upload'" in body, (
+ "the upload must be sealed under the group key")
+ assert "openGroup(" in body and "'file_upload_ack'" in body, (
+ "the ack carries the stored name and must be opened, not read")
+ # The message the node actually receives: everything between `this._send({`
+ # and its close. Read on its own, because the same field names appear a few
+ # lines above inside `msgpack_encode({...})`, which is the sealed half.
+ sent = body[body.index("this._send({"):]
+ sent = sent[:sent.index("});")]
+ assert "filename" not in sent, "the filename is on the message in clear"
+ assert "data" not in sent, "the bytes are on the message in clear"
+ assert "dir" not in sent and "root" not in sent, (
+ "the destination is on the message in clear")
+ assert "...sealed," in sent, "the message must carry the sealed pair"
+ assert "supportsSealedUpload" in body, (
+ "an older node must be refused before a chunk is sent, not after")
# ── MNP 1.0: the sealed handshake ack ────────────────────────────────────────