diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-10 12:20:07 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-10 12:20:07 +0200 |
| commit | 6964ff112fec47498ed778a491cf2a1490392c79 (patch) | |
| tree | af7ab9d4a9ca1cff804182b47995b6a4d4890469 /packages | |
| parent | 45d63060a975473367a0f0312572b349a5c448a4 (diff) | |
| download | meshbay-6964ff112fec47498ed778a491cf2a1490392c79.tar.gz | |
fix(node): report the transfer cap the node actually enforces
`transfer_state` read `slots.per_member` — the node-wide default — while
`_has_room` decides with `member_cap()`, which prefers the group's own signed
limit, and the handshake ack announces that same `member_cap()`. Three readings
of one number, and one of them was the odd one out.
In a group where the operator signed a higher limit, every lease update told the
client "cap: 2" while the node would grant five: the transfers widget draws
`used >= cap` as saturated, so a member with two transfers running saw the rest
of their slots disappear. Lowered the other way it is worse in the other
direction — the interface offers slots the node will queue.
Nothing was ever granted or refused wrongly; the enforcement was right on both
paths. It is the number beside it that contradicted them.
Two tests, one override above the default and one below, because a bug that
reads the node-wide value passes the first whenever the default happens to be
the larger number.
Node suite 1215 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsoWC3GmhNdwVFomW3QjH3
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py | 8 | ||||
| -rw-r--r-- | packages/meshbay-node/tests/test_transfer_slots_wire.py | 30 |
2 files changed, 36 insertions, 2 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py index a0efa83..11412bf 100644 --- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py +++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py @@ -3440,8 +3440,12 @@ class WebRTCPeerSession: "state": state, "kind": lease.kind, "used": slots.member_in_use(lease.kind, lease.member), - "cap": slots.per_member.get(lease.kind, - transfers_mod.DEFAULT_MAX_PER_MEMBER), + # `member_cap`, not the node-wide default: this group's own limit is + # what `_has_room` enforces and what the handshake ack announces, so + # reading the default here would have the widget contradicting both + # — "1 of 2" in a group where the operator signed 5, or two slots + # offered in a group limited to one. + "cap": slots.member_cap(lease.kind, lease.member), "node_used": slots.in_use(lease.kind), "node_cap": slots.caps.get(lease.kind, transfers_mod.DEFAULT_MAX_CONCURRENT), diff --git a/packages/meshbay-node/tests/test_transfer_slots_wire.py b/packages/meshbay-node/tests/test_transfer_slots_wire.py index db8c17a..c9fc85a 100644 --- a/packages/meshbay-node/tests/test_transfer_slots_wire.py +++ b/packages/meshbay-node/tests/test_transfer_slots_wire.py @@ -93,6 +93,36 @@ async def test_a_queued_transfer_is_told_how_many_are_ahead(ctx): assert peer.sent[-1]["ahead"] == 1 +async def test_the_cap_reported_is_the_cap_enforced(ctx): + """A group with its own signed limit is told that limit, not the default. + + Two code paths answer "how many may this member run at once": `_has_room`, + which decides, and this message, which the transfers widget draws. They read + the same value or the interface contradicts the node — offering a slot that + will be queued, or showing a member as saturated while the node would still + grant two more. + """ + ctx["transfer_limits"] = {DOWNLOAD: 5} + peer = _join(ctx, "s1", "alice") + peer._do_transfer_open({"tr": "t1"}) + assert peer.last()["cap"] == 5 + assert peer._slots().member_cap(DOWNLOAD, ("g1", "alice")) == 5 + + +async def test_a_lowered_group_cap_is_reported_too(ctx): + """The same in the other direction: an override *below* the default. + + Worth its own test because a bug that reads the node-wide value passes the + one above whenever the default happens to be the larger number. + """ + ctx["transfer_limits"] = {DOWNLOAD: 1} + peer = _join(ctx, "s1", "alice") + peer._do_transfer_open({"tr": "t1"}) + peer._do_transfer_open({"tr": "t2"}) + assert peer.sent[0]["cap"] == 1 + assert peer.sent[1]["state"] == "queued" + + async def test_the_reply_carries_no_name_and_no_path(ctx): """A lease holds neither, and `transfer_state` stays in clear — so this is the message where a filename would quietly become metadata on the wire.""" |