diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-15 01:35:09 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-15 01:35:09 +0200 |
| commit | 47583c5e405f3011a526df73cc9485c46598f214 (patch) | |
| tree | 90fafd2e7d3978071b38402253cf7eb70717ec7f /packages/meshbay-cli/tests/test_cli_actions.py | |
| parent | ced66b96f898abccec2526650ab356eb6b82f066 (diff) | |
| download | meshbay-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.py | 45 |
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}" |