aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_chat_send.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_chat_send.py')
-rw-r--r--packages/meshbay-hub/tests/test_chat_send.py57
1 files changed, 56 insertions, 1 deletions
diff --git a/packages/meshbay-hub/tests/test_chat_send.py b/packages/meshbay-hub/tests/test_chat_send.py
index 8b098c2a..5db8f266 100644
--- a/packages/meshbay-hub/tests/test_chat_send.py
+++ b/packages/meshbay-hub/tests/test_chat_send.py
@@ -28,6 +28,7 @@ either source.
import json
import shutil
import subprocess
+import sys
from pathlib import Path
import pytest
@@ -42,7 +43,12 @@ pytestmark = pytest.mark.skipif(
@pytest.fixture(scope="module")
def probe():
- run = subprocess.run(["python3", str(HARNESS)], capture_output=True, timeout=180)
+ # `sys.executable`, not a bare "python3": the harness imports
+ # `meshbay_common` to seal the chat keys the way the node does, and the
+ # system interpreter has neither that nor msgpack. The other probes get
+ # away with "python3" because they import nothing from this project.
+ run = subprocess.run([sys.executable, str(HARNESS)],
+ capture_output=True, timeout=180)
assert run.returncode == 0, run.stderr.decode()[-2000:]
data = json.loads(run.stdout.decode())
steps = {sc["name"]: {s["label"]: s for s in sc["steps"]}
@@ -103,3 +109,52 @@ def test_the_reply_is_not_handed_to_another_request(probe, reply):
f"the chat {reply} was routed to the pending music_meta_req ({stolen}) -- "
"that request now believes it has an answer, and the chat send is "
"waiting for a reply that already arrived")
+
+
+@pytest.mark.parametrize("reply", ["ack", "error"])
+def test_the_message_goes_out_sealed_and_signed(probe, reply):
+ """
+ What actually left the browser (MNP 2.0).
+
+ Asserted for both scenarios because the composer is what decides to send:
+ a client that fell back to plaintext when something went wrong would be
+ refused by the node, but the refusal arrives after the fact and reads as
+ "the message did not send". There is no plaintext form on the wire.
+ """
+ data, _ = probe
+ sent = [line for line in data["log"]
+ if line.startswith(f"{reply}: chat_msg ")]
+ assert sent, (f"no chat_msg reached the stand-in node in the {reply} "
+ f"scenario — the send did not complete. log: {data['log']}")
+ assert "format=1" in sent[0], "the message was not sealed"
+ assert "sig=64" in sent[0], "the message was not signed"
+ assert "ct=" in sent[0] and "ct=0" not in sent[0], "there was no ciphertext"
+ assert "plaintextLeak=false" in sent[0], (
+ "the text the person typed appears somewhere in the message that went "
+ "on the wire")
+
+
+def test_the_chat_keys_answer_is_not_handed_to_another_request(probe):
+ """
+ The same defect this file exists for, in the message type that carries the
+ group's chat keys — which did not exist when it was written, and which a
+ send now depends on. It went astray on the probe's first encrypted run.
+ """
+ data, _ = probe
+ stolen = [line for line in data["log"] if "music_meta resolved with" in line]
+ assert not stolen, (
+ f"a reply was routed to the pending music_meta_req ({stolen}) — the "
+ "send then waits out its own 30s timeout with the composer disabled")
+
+
+def test_history_still_renders(probe):
+ """
+ Not about sending at all, and here because it broke without a sound:
+ `_asText` was deleted with an unrelated helper beside it, its only caller
+ sits inside a promise the panel catches, and every conversation rendered
+ empty with the node answering perfectly.
+ """
+ _, steps = probe
+ assert steps["ack"]["older request pending"]["bubbles"] == 5, (
+ "the five history messages did not render — the panel swallows a "
+ "failure in the transport's message reader, so this is silent")