aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_chat_send.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-09 16:55:53 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-09 16:55:53 +0200
commit4d5a07299c792de1251622a41ce7f94870b363fa (patch)
tree3d0b79d109b902b8d7d3f7e0d5c568e54a58e61a /packages/meshbay-hub/tests/test_chat_send.py
parent391db2f2197b5f7fbdba0c918a24830a5cbe6ee4 (diff)
downloadmeshbay-4d5a07299c792de1251622a41ce7f94870b363fa.tar.gz
test(spa): drive the composer's recovery through the real reconnect
The reconnect scenario added with the fix proved the composer follows `_setDevicePk`, and it poked that method itself at both ends. That is a narrower claim than it reads as: it says nothing about whether a reconnect *reaches* it, and the harness's own `Host` stands in for group-page.js, so a green probe did not mean the page joins the two. Both halves are real now. The scenario calls `connect()` with the arguments `_reconnectLoop` calls it with; it stops at signaling, because there is no hub in the harness, and the identity has to be gone by then — connect() drops it before it touches the network. The restore is the shipped `_announceDevice`, answered by the stand-in node with a `device_hello_ack` as `_do_device_hello` answers it, and the key it settles on is the one the following send seals and signs with. Three assertions check the scenario went that way rather than through a variable set by the test. The seam the harness cannot drive gets its own check: the wiring exists, the prop is in `commonProps`, and the callback is set *before* `connect()` — after it, device_hello's answer is missed and the composer starts closed. That check first passed with the wiring deleted, on the strength of a comment naming the callback; it matches the assignment now. Two things the harness turned up. `do_POST` answered every path, so the offer connect() posts to the hub was swallowed as the measurement and put the machine's own SDP, public address included, into the probe's output — it answers `/log` and nothing else now. And a connect() that gives up before `await channelReady` left that promise rejected with nobody attached, so closing the peer connection printed "Uncaught (in promise) DataChannel closed" on every failed reconnect attempt — noise in exactly the log a freeze is read from. Checked against the unfixed source both ways: with the clear removed from connect() and the wiring removed from group-page.js, three cases fail; with them back, 15 pass. Hub and node suites 2266 passed, 4 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019GXmScYB1uR29YCt74si9J
Diffstat (limited to 'packages/meshbay-hub/tests/test_chat_send.py')
-rw-r--r--packages/meshbay-hub/tests/test_chat_send.py48
1 files changed, 48 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_chat_send.py b/packages/meshbay-hub/tests/test_chat_send.py
index d442383..c1a7f6f 100644
--- a/packages/meshbay-hub/tests/test_chat_send.py
+++ b/packages/meshbay-hub/tests/test_chat_send.py
@@ -42,6 +42,7 @@ import pytest
HARNESS = Path(__file__).parent / "harness" / "chat_send_probe.py"
STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static"
+GROUP_PAGE = STATIC / "group-page.js"
pytestmark = pytest.mark.skipif(
shutil.which("google-chrome") is None or not (STATIC / "chat-app.js").exists(),
@@ -215,6 +216,53 @@ def test_sending_works_again_after_a_reconnect(probe):
"the text came back into the composer, so the send failed")
+def test_the_reconnect_scenario_drives_the_shipped_path(probe):
+ """The scenario has to be worth what it claims.
+
+ It would be easy to write one that sets `devicePk` itself at both ends and
+ proves only that the composer follows a variable. These three lines say the
+ clear came out of the real `connect()`, that the restore came out of the
+ real `_announceDevice`, and that the re-identification actually went over
+ the wire as a `device_hello`.
+ """
+ data, _ = probe
+ log = data["log"]
+ assert any("connect() stopped at signaling" in line for line in log), (
+ "the scenario never ran the real connect(), so it did not test the "
+ "reconnect path at all")
+ assert any("sent device_hello" == line for line in log), (
+ "no device_hello reached the stand-in node -- the identity was not "
+ "re-announced, it was assigned")
+ assert any("_announceDevice settled on the device key" in line for line in log), (
+ "_announceDevice did not settle on the key it signed with")
+
+
+def test_the_page_tells_the_composer_when_the_identity_moves():
+ """The one seam the probe stands in for.
+
+ `Host` in the harness plays group-page.js, so a green probe proves that
+ ChatPanel and the transport agree — not that the page joins them. These are
+ the lines that do, and the order matters: the callback has to be wired
+ before `connect()`, because `connect()` is where `device_hello` runs, and a
+ callback set after it misses the first answer and starts the composer shut.
+ """
+ src = GROUP_PAGE.read_text(encoding="utf-8")
+ # The assignment, not a mention of it. Matching the bare name passed with
+ # the wiring deleted, on the strength of a comment that named it.
+ assert "transport.onDeviceIdentity = " in src, (
+ "nothing tells the page that the device identity moved, so the "
+ "composer has no event to open back up on -- the freeze this file's "
+ "reconnect scenario is about")
+ assert "deviceReady," in src, (
+ "the answer never reaches the apps: ChatPanel defaults the prop to "
+ "true, so a composer wired this way is merely never closed rather "
+ "than correct")
+ assert src.index("transport.onDeviceIdentity = ") \
+ < src.index("await transport.connect("), (
+ "the callback is wired after connect(), which is where device_hello "
+ "runs -- its answer is missed and the composer starts closed")
+
+
def test_history_still_renders(probe):
"""
Not about sending at all, and here because it broke without a sound: