From 16d92c96bde3a65fc54a93b4662df30cafc831e4 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 15 Oct 2026 00:56:04 +0200 Subject: fix(node): a chat-epoch notice answers no request, not even the asker's Revoking a device or removing a member opens a chat epoch, announced to everyone connected to the group, the asker included. Sent inside the handler, the asker's copy was stamped with its request's id and arrived as the answer to device_revoke, before the real one. It now leaves outside the reply context. Co-Authored-By: Claude Opus 5.5 --- .../meshbay-node/tests/test_reply_correlation.py | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'packages/meshbay-node/tests') diff --git a/packages/meshbay-node/tests/test_reply_correlation.py b/packages/meshbay-node/tests/test_reply_correlation.py index bfb336e..a97ec1e 100644 --- a/packages/meshbay-node/tests/test_reply_correlation.py +++ b/packages/meshbay-node/tests/test_reply_correlation.py @@ -28,6 +28,7 @@ import base64 import msgpack import pytest from meshbay_common.protocol import MNP +from meshbay_node.transport.webrtc.channel import _REPLY_TO from meshbay_node.transport.webrtc_server import WebRTCPeerSession pytestmark = pytest.mark.asyncio @@ -163,3 +164,29 @@ async def test_work_handed_to_a_task_still_answers_under_the_right_id(): by_id = {m["req_id"]: m["detail"] for m in s._channel.sent} assert by_id == {11: "first", 12: "second"}, ( "a late reply answered under whichever request arrived most recently") + + +async def test_a_new_chat_epoch_answers_no_request_not_even_the_askers(): + """Revoking a device or removing a member opens a chat epoch, announced to + everyone connected — the asker included. Stamped with the asker's request, + that notice arrived as the answer to `device_revoke` before the real one.""" + asker, other = _session("asker"), _session("other") + asker._ctx = {"groups": {"g": {"_peers": {"a": asker, "b": other}}}} + asker._audit = lambda *a, **k: None + + async def run_op(fn, *args): + return {"epoch": 2} + asker._run_op = run_op + + token = _REPLY_TO.set((asker, 9)) # inside the handler of request 9 + try: + await asker._new_chat_epoch("g", "device_revoke") + asker._send({"type": MNP.DEVICE_ADD_ACK}) + finally: + _REPLY_TO.reset(token) + + notice, reply = asker._channel.sent + assert notice["type"] == MNP.CHAT_EPOCH_ACK and "req_id" not in notice + assert reply["type"] == MNP.DEVICE_ADD_ACK and reply["req_id"] == 9 + (seen,) = other._channel.sent + assert "req_id" not in seen -- cgit v1.2.3