diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-22 18:26:22 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-22 18:26:22 +0200 |
| commit | 8dc11dc05a35a5d64ba4d2c892ccc01c7bfae3da (patch) | |
| tree | e172bb4d596bde53c48ce2ca3ef2d59c98968147 /packages/meshbay-node/tests/test_cli_dispatch.py | |
| parent | b4baa4770d517ea7d1d25bb0ac50fc7c989531d6 (diff) | |
| download | meshbay-8dc11dc05a35a5d64ba4d2c892ccc01c7bfae3da.tar.gz | |
feat(node): systemd service panel on the Node page, and a clean CLI restart
Add a status panel at the top of the Node page — always visible, even
before an MNP connection exists — showing the meshbay-node systemd
unit's own state (via `systemctl --user show`, main process only) with
Start/Stop/Restart controls. This is the piece the rest of the page
cannot provide: it has to work while the daemon is stopped or crash-
looping, which the MNP-based sections require the daemon to already
answer.
While touching node lifecycle: `reload` and `restart-daemon` in the
CLI shelled out to pgrep + SIGTERM/SIGHUP and respawned the process by
hand, logging to a hardcoded /tmp path. That pattern already SIGHUPed
a developer's own running node by accident once (see the old
test_cli_dispatch.py comment). Both now delegate to
`systemctl --user reload|restart meshbay-node`, which the unit already
supports correctly (ExecReload=, Restart=on-failure).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016SF6RKNBKg9qejmoMJ9ybA
Diffstat (limited to 'packages/meshbay-node/tests/test_cli_dispatch.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_cli_dispatch.py | 60 |
1 files changed, 49 insertions, 11 deletions
diff --git a/packages/meshbay-node/tests/test_cli_dispatch.py b/packages/meshbay-node/tests/test_cli_dispatch.py index fdcd82e..d9edb17 100644 --- a/packages/meshbay-node/tests/test_cli_dispatch.py +++ b/packages/meshbay-node/tests/test_cli_dispatch.py @@ -77,19 +77,21 @@ def stub_daemon(monkeypatch, tmp_path): import getpass monkeypatch.setattr(getpass, "getpass", lambda *a, **kw: "test-password") - # `reload` looks for a real daemon and signals it. Without this the test - # SIGHUPs whatever node happens to be running on the machine — which it did, - # once, before this was added. A test must not reach outside itself. - import os + # `reload` and `restart-daemon` shell out to `systemctl --user`. Without + # this the test would run that against whatever session bus is actually + # available — a test must not reach outside itself, which is exactly what + # the pgrep/os.kill version of this fixture existed to prevent before + # those commands were rewritten to delegate to systemd. import subprocess - signalled: list[int] = [] - monkeypatch.setattr( - subprocess, "run", - lambda *a, **k: subprocess.CompletedProcess(a[0], 0, stdout="4242\n", - stderr="")) - monkeypatch.setattr(os, "kill", lambda pid, sig: signalled.append(pid)) - calls.append(("_signalled", signalled)) + systemctl_calls: list[list[str]] = [] + + def fake_run(argv, **kw): + systemctl_calls.append(argv) + return subprocess.CompletedProcess(argv, 0, stdout="", stderr="") + + monkeypatch.setattr(subprocess, "run", fake_run) + calls.append(("_systemctl_calls", systemctl_calls)) return calls @@ -130,3 +132,39 @@ def test_the_verb_list_here_matches_the_parser(): assert not untested, ( f"CLI verbs with no dispatch test: {sorted(untested)} — add them to " f"VERBS above") + + +@pytest.mark.parametrize("argv,verb", [ + (["reload"], "reload"), + (["restart-daemon"], "restart"), +]) +def test_lifecycle_commands_delegate_to_systemctl_user( + argv, verb, stub_daemon, monkeypatch, capsys): + """ + `reload` and `restart-daemon` must ask systemd to do it, not hunt a PID + with pgrep and signal it directly — that pattern-matched a developer's own + running node by accident once, which is why it was replaced. + """ + monkeypatch.setattr(sys, "argv", ["meshbay-node", *argv]) + daemon_mod.main() + + systemctl_calls = dict(stub_daemon)["_systemctl_calls"] + assert systemctl_calls == [["systemctl", "--user", verb, "meshbay-node"]] + assert not capsys.readouterr().err + + +@pytest.mark.parametrize("argv", [["reload"], ["restart-daemon"]]) +def test_lifecycle_commands_report_systemctl_failure( + argv, stub_daemon, monkeypatch, capsys): + """A unit that refuses (not installed, not running) must exit non-zero.""" + import subprocess + + monkeypatch.setattr( + subprocess, "run", + lambda a, **k: subprocess.CompletedProcess( + a, 1, stdout="", stderr="Unit meshbay-node.service not loaded.\n")) + monkeypatch.setattr(sys, "argv", ["meshbay-node", *argv]) + with pytest.raises(SystemExit) as exc: + daemon_mod.main() + assert exc.value.code == 1 + assert "not loaded" in capsys.readouterr().out |