summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/tests/test_cli_actions.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-15 01:35:09 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-15 01:35:09 +0200
commit47583c5e405f3011a526df73cc9485c46598f214 (patch)
tree90fafd2e7d3978071b38402253cf7eb70717ec7f /packages/meshbay-cli/tests/test_cli_actions.py
parentced66b96f898abccec2526650ab356eb6b82f066 (diff)
downloadmeshbay-47583c5e405f3011a526df73cc9485c46598f214.tar.gz
feat(cli): chat — read a group's last messages, post one
chat GROUP prints the last ten messages, oldest first (-N, as head -N, or -n N for another count, paging back past the node's 200), and -m posts one (- reads it from stdin). The application's construction: every epoch key from chat_keys_req, a post sealed under the current epoch through the device subkey and signed with this machine's key on the node, the sender's name inside the envelope; nothing sent past the node's ciphertext bound. A stored message is read signature first, then key, then content, and one that fails is shown as unreadable, never dropped. Not done here: the Tier 2 roster check on the signing device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-cli/tests/test_cli_actions.py')
-rw-r--r--packages/meshbay-cli/tests/test_cli_actions.py45
1 files changed, 45 insertions, 0 deletions
diff --git a/packages/meshbay-cli/tests/test_cli_actions.py b/packages/meshbay-cli/tests/test_cli_actions.py
index e06fb9c..5443d9b 100644
--- a/packages/meshbay-cli/tests/test_cli_actions.py
+++ b/packages/meshbay-cli/tests/test_cli_actions.py
@@ -510,6 +510,51 @@ def test_search_finds_across_groups_once_per_file(hub, monkeypatch, capsys):
assert main("search", "beach") == 0
+def test_chat_reads_the_last_messages_and_posts(node, monkeypatch, capsys):
+ from meshbay_cli.commands import chat
+
+ async def open_group(account, group):
+ return node.session()
+ monkeypatch.setattr(chat, "open_group", open_group)
+
+ for i in range(1, 231): # more than one page of the node's
+ node.say(f"message {i}")
+ node.say("forged", forge=True)
+ node.say("two lines\nand an \x1b[31m escape")
+ node.chat_epochs[2] = b"\x09" * 32
+ node.say("from an epoch this member was not given", epoch=2)
+ del node.chat_epochs[2]
+
+ assert main("chat", "Family") == 0
+ lines = capsys.readouterr().out.splitlines()
+ assert len(lines) == 11 and lines[0].endswith("bob-test: message 224")
+ assert lines[7].endswith("bob-test: [this message cannot be read: signature]")
+ assert lines[8].endswith("bob-test: two lines")
+ assert lines[9].strip() == "and an \\x1b[31m escape" # shown, never run
+ assert "cannot be read: no key for its epoch" in lines[10]
+
+ assert main("chat", "-5", "Family") == 0
+ assert capsys.readouterr().out.splitlines()[0].endswith("message 229")
+ assert main("--json", "chat", "Family", "-n", "300") == 0 # paged back
+ got = json.loads(capsys.readouterr().out)
+ assert len(got) == 233 and got[0]["text"] == "message 1"
+ assert got[230]["unreadable"] == "signature"
+
+ assert main("chat", "Family", "-m", "hello from the terminal") == 0
+ assert "Posted to Family@alice-test" in capsys.readouterr().err
+ monkeypatch.setattr("sys.stdin", io.StringIO("piped\n"))
+ assert main("chat", "Family", "-m", "-") == 0
+ capsys.readouterr()
+ assert main("chat", "-2", "Family") == 0
+ out = capsys.readouterr().out.splitlines()
+ assert out[0].endswith(f"{USERNAME}: hello from the terminal")
+ assert out[1].endswith(f"{USERNAME}: piped")
+
+ assert main("chat", "Family", "-m", " ") == 1
+ assert main("chat", "Family", "-m", "x" * 70_000) == 1 # over the node's bound
+ assert main("chat", "-0", "Family") == 1
+
+
def test_every_action_is_run_here():
"""Runs last in this module: every action the parser knows was run above."""
assert RUN == set(cli.ACTIONS), f"never run: {set(cli.ACTIONS) - RUN}"