summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/tests/test_chat_follow.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-cli/tests/test_chat_follow.py')
-rw-r--r--packages/meshbay-cli/tests/test_chat_follow.py94
1 files changed, 94 insertions, 0 deletions
diff --git a/packages/meshbay-cli/tests/test_chat_follow.py b/packages/meshbay-cli/tests/test_chat_follow.py
new file mode 100644
index 0000000..cc6db99
--- /dev/null
+++ b/packages/meshbay-cli/tests/test_chat_follow.py
@@ -0,0 +1,94 @@
+"""
+`chat --follow`: new messages as the node pushes them, a dead connection opened
+again, and what was said meanwhile read from the history — each message once.
+"""
+
+import asyncio
+import json
+
+import pytest
+from fake_hub import PASSPHRASE, USERNAME, FakeHub
+from fake_node import FakeNode
+from meshbay_cli import __main__ as cli
+from meshbay_cli.commands import chat, context
+
+
+@pytest.fixture
+def follow(monkeypatch, capsys):
+ hub, node = FakeHub(), FakeNode()
+ monkeypatch.setattr(context, "Hub", lambda url: hub)
+ sessions = []
+
+ async def open_group(account, group):
+ sessions.append(node.session())
+ return sessions[-1]
+ monkeypatch.setattr(chat, "open_group", open_group)
+ monkeypatch.setattr(chat, "PING_EVERY", 0.05)
+ monkeypatch.setattr(chat, "RECONNECT_WAITS", (0.01,))
+ assert cli.main(["--hub", hub.url, "-u", USERNAME, "--password", PASSPHRASE,
+ "login"]) == 0
+ capsys.readouterr()
+
+ def start(*argv):
+ args = cli.build_parser().parse_args(cli.chat.rewrite_count(list(argv), cli.ACTIONS))
+ return asyncio.ensure_future(chat.run(context.Context(args)))
+ return node, sessions, start
+
+
+def _push(session, node):
+ session.ch._emit({**node.chat[-1], "type": "chat_msg"})
+
+
+async def _until(cond, timeout=5.0):
+ deadline = asyncio.get_running_loop().time() + timeout
+ while not cond():
+ assert asyncio.get_running_loop().time() < deadline, "timed out"
+ await asyncio.sleep(0.02)
+
+
+async def test_follow_prints_pushes_reconnects_and_never_repeats(follow, capsys):
+ node, sessions, start = follow
+ node.say("before")
+ task = start("--json", "chat", "-1", "Family", "--follow")
+ printed = []
+
+ def lines():
+ printed.extend(json.loads(x)["text"] for x in capsys.readouterr().out.splitlines())
+ return printed
+ try:
+ await _until(lambda: lines() == ["before"])
+
+ node.say("live one")
+ _push(sessions[-1], node)
+ await _until(lambda: lines()[-1:] == ["live one"])
+
+ # The connection drops; meanwhile two messages are stored and only the
+ # last of them is pushed, once the new connection is up.
+ sessions[-1].ch.closed.set()
+ node.say("while away")
+ await _until(lambda: len(sessions) == 2)
+ await _until(lambda: "while away" in lines())
+ node.say("after")
+ _push(sessions[-1], node)
+ _push(sessions[-1], node) # the node said it twice
+ await _until(lambda: lines()[-1:] == ["after"])
+
+ # A connection that stops answering its ping is replaced too.
+ node.unresponsive = True
+ await _until(lambda: len(sessions) == 3)
+ node.unresponsive = False
+ node.say("on the third connection")
+ _push(sessions[-1], node)
+ await _until(lambda: lines()[-1:] == ["on the third connection"])
+ await asyncio.sleep(0.2)
+ assert lines() == ["before", "live one", "while away", "after",
+ "on the third connection"]
+ finally:
+ task.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await task
+
+
+def test_follow_and_post_are_not_asked_at_once(follow, capsys):
+ assert cli.main(["chat", "Family", "-f", "-m", "x"]) == 1
+ assert cli.main(["chat", "-f"]) == 1