aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/conftest.py23
-rw-r--r--packages/meshbay-node/tests/test_cli_dispatch.py21
-rw-r--r--packages/meshbay-node/tests/test_cli_golden.py8
-rw-r--r--packages/meshbay-node/tests/test_root_paths_are_operator_only.py10
-rw-r--r--packages/meshbay-node/tests/test_roster_pairing.py4
5 files changed, 55 insertions, 11 deletions
diff --git a/packages/meshbay-node/tests/conftest.py b/packages/meshbay-node/tests/conftest.py
index 2ca0c8a..9ef23a3 100644
--- a/packages/meshbay-node/tests/conftest.py
+++ b/packages/meshbay-node/tests/conftest.py
@@ -92,3 +92,26 @@ def opened_ack(session, msg: dict) -> dict:
ctx = session._group_ctx()
return file_upload_ack_payload(ctx["gek"], session._group_id or "", msg)
+
+
+def patch_cli(monkeypatch, name: str, value) -> None:
+ """Replace `name` wherever `meshbay-node`'s command line reads it.
+
+ Each module of `meshbay_node.cli` imports what it uses by name, so a helper
+ like `_daemon_api` is bound once in every module that calls it, and patching
+ the one that defines it leaves the verbs calling the real thing — which
+ talks to whatever node is running on this machine. `daemon` is included:
+ its `main()` still reads the config path when no verb is given.
+ """
+ import importlib
+ import pkgutil
+
+ import meshbay_node.cli as cli
+ from meshbay_node import daemon
+
+ modules = [daemon] + [importlib.import_module(f"meshbay_node.cli.{m.name}")
+ for m in pkgutil.iter_modules(cli.__path__)]
+ holders = [m for m in modules if hasattr(m, name)]
+ assert holders, f"no module of the CLI has {name!r} to patch"
+ for module in holders:
+ monkeypatch.setattr(module, name, value)
diff --git a/packages/meshbay-node/tests/test_cli_dispatch.py b/packages/meshbay-node/tests/test_cli_dispatch.py
index 26ffb05..5b16fbe 100644
--- a/packages/meshbay-node/tests/test_cli_dispatch.py
+++ b/packages/meshbay-node/tests/test_cli_dispatch.py
@@ -20,6 +20,8 @@ import pytest
from meshbay_node import daemon as daemon_mod
from node_source import cli_source
+from conftest import patch_cli
+
# Each verb, with the arguments that reach its branch. `--yes` where the command
# would otherwise stop for a confirmation nobody can type in a test.
VERBS = [
@@ -107,12 +109,12 @@ def stub_daemon(monkeypatch, tmp_path):
"encrypted_messages": 0, "max_age_days": 30,
}
- monkeypatch.setattr(daemon_mod, "_daemon_api", fake_api)
- monkeypatch.setattr(daemon_mod, "_resolve_group", lambda cfg, g: "g" * 32)
+ patch_cli(monkeypatch, "_daemon_api", fake_api)
+ patch_cli(monkeypatch, "_resolve_group", lambda cfg, g: "g" * 32)
conf = tmp_path / "node.toml"
conf.write_text('[hub]\nurl = "https://example.invalid"\n')
- monkeypatch.setattr(daemon_mod, "DEFAULT_CONFIG_PATH", conf)
+ patch_cli(monkeypatch, "DEFAULT_CONFIG_PATH", conf)
# And a home of its own: the data directory (where `member invite` and
# `operator pair` write their code, where `status` finds the run token of
# a live node) and the keystore's default path are both derived from it.
@@ -192,6 +194,19 @@ def test_the_verb_list_here_matches_the_parser(monkeypatch, capsys):
assert "ui" not in declared, "the `ui` verb came back"
+def test_every_verb_the_parser_accepts_runs_something():
+ """
+ A verb the parser accepts and the dispatch table does not name would not
+ fail: `run()` would report nothing to run, and `main()` would go on and
+ start the daemon.
+ """
+ from meshbay_node.cli.dispatch import VERBS
+ from meshbay_node.cli.parser import build_parser
+
+ command = next(a for a in build_parser()._actions if a.dest == "command")
+ assert set(command.choices) == set(VERBS)
+
+
@pytest.mark.skipif(sys.platform == "win32",
reason="systemd lifecycle; Windows uses the loopback API / "
"Startup-folder launcher, covered by test_platform.py")
diff --git a/packages/meshbay-node/tests/test_cli_golden.py b/packages/meshbay-node/tests/test_cli_golden.py
index ddaa82f..3d7b0c0 100644
--- a/packages/meshbay-node/tests/test_cli_golden.py
+++ b/packages/meshbay-node/tests/test_cli_golden.py
@@ -27,6 +27,8 @@ from meshbay_node import config as config_mod
from meshbay_node import daemon as daemon_mod
from test_cli_dispatch import VERBS
+from conftest import patch_cli
+
GOLDEN = Path(__file__).parent / "golden" / "cli.json"
# The verbs that ask first, asked and answered "no": test_cli_dispatch.py
# passes --yes to all of them.
@@ -78,9 +80,9 @@ def _run(argv: list[str], tmp_path: Path, capsys) -> dict:
conf.write_text('[hub]\nurl = "https://example.invalid"\n')
code = 0
with pytest.MonkeyPatch.context() as mp:
- mp.setattr(daemon_mod, "_daemon_api", fake_api)
- mp.setattr(daemon_mod, "_resolve_group", lambda cfg, g: "g" * 32)
- mp.setattr(daemon_mod, "DEFAULT_CONFIG_PATH", conf)
+ patch_cli(mp, "_daemon_api", fake_api)
+ patch_cli(mp, "_resolve_group", lambda cfg, g: "g" * 32)
+ patch_cli(mp, "DEFAULT_CONFIG_PATH", conf)
# The data directory and the keystore's default path come from these;
# left real, `status` would record the developer's own node.
mp.setenv("HOME", str(tmp_path / "home"))
diff --git a/packages/meshbay-node/tests/test_root_paths_are_operator_only.py b/packages/meshbay-node/tests/test_root_paths_are_operator_only.py
index a18e9fa..e00f1cf 100644
--- a/packages/meshbay-node/tests/test_root_paths_are_operator_only.py
+++ b/packages/meshbay-node/tests/test_root_paths_are_operator_only.py
@@ -26,6 +26,8 @@ from meshbay_node import daemon as daemon_mod
from meshbay_node import ops
from meshbay_node.roots import RootSet
+from conftest import patch_cli
+
def _roots(tmp_path: Path) -> RootSet:
for name in ("Films", "Albums"):
@@ -116,12 +118,14 @@ def test_the_cli_only_reads_fields_the_payload_carries(monkeypatch, tmp_path, ca
return super().get(key, default)
gid = "g" * 32
- monkeypatch.setattr(daemon_mod, "_daemon_api", lambda cfg, path, **kw: {
+ patch_cli(monkeypatch, "_daemon_api", lambda cfg, path, **kw: {
"groups": [{"id": gid, "roots": [_Recording(offered)]}]})
- monkeypatch.setattr(daemon_mod, "_resolve_group", lambda cfg, g: gid)
+ patch_cli(monkeypatch, "_resolve_group", lambda cfg, g: gid)
conf = tmp_path / "node.toml"
conf.write_text('[hub]\nurl = "https://example.invalid"\n')
- monkeypatch.setattr(daemon_mod, "DEFAULT_CONFIG_PATH", conf)
+ patch_cli(monkeypatch, "DEFAULT_CONFIG_PATH", conf)
+ # A stub missed would otherwise reach the node running on this machine.
+ monkeypatch.setenv("HOME", str(tmp_path / "home"))
monkeypatch.setattr(sys, "argv", ["meshbay-node", "root", "list"])
daemon_mod.main()
diff --git a/packages/meshbay-node/tests/test_roster_pairing.py b/packages/meshbay-node/tests/test_roster_pairing.py
index ba16317..a2d0f8b 100644
--- a/packages/meshbay-node/tests/test_roster_pairing.py
+++ b/packages/meshbay-node/tests/test_roster_pairing.py
@@ -25,7 +25,7 @@ from meshbay_node.roster import Roster, hash_code, normalize_code
from meshbay_node.transport.webrtc_server import WebRTCPeerSession
from node_source import daemon_source, session_method, webrtc_source
-from conftest import one_root
+from conftest import one_root, patch_cli
# ── Fixtures ──────────────────────────────────────────────────────────────────
@@ -823,7 +823,7 @@ def _run_cli(monkeypatch, tmp_path, argv, responses):
return value
return {}
- monkeypatch.setattr(_daemon, "_daemon_api", fake_api)
+ patch_cli(monkeypatch, "_daemon_api", fake_api)
conf = tmp_path / "node.toml"
tp = tmp_path.as_posix() # a raw Windows path is a TOML escape error