aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-04 17:49:24 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-04 17:49:24 +0200
commit171a36984feff5247786b32958b52673e13cc8f0 (patch)
treef8702b8773022ee8b0b7ec41fb79584cbd65977e /packages/meshbay-node/tests
parentb78288640d8c13cc0fb3f4ee7c82f3efac33940f (diff)
downloadmeshbay-171a36984feff5247786b32958b52673e13cc8f0.tar.gz
test(node): pin the fresh-install-before-provisioning safety property
Two properties discussed but not yet pinned by a test, both load-bearing for service mode: 1. A bare `meshbay-node` with no config yet -- exactly what the W3 Startup .vbs and the service-mode Scheduled Task both run unattended, on the very first boot after a fresh install, quite possibly before the user has ever opened the client -- must fail closed, fast, and without a trace. Measured by hand first (under a second, zero bytes written against a real empty %LOCALAPPDATA%); this pins it as a test so it can't regress silently. load_config() already returns an empty Config on a missing path rather than raising, so main() reaches its own "hub.username not set" exit before ever touching NodeDaemon() or asyncio.run() -- nothing here has to mock the daemon startup. 2. node:start (main.js) must call provisionNode() before it ever checks for the service task or spawns -- reversed, the wizard's first Start on a fresh service-mode install would run/query the daemon before node.toml exists for it to read. Source-read, same technique as test_desktop_shell.py: the only evidence available without a live Electron run. Node suite 845 pass / 25 skip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/test_cli_dispatch.py34
-rw-r--r--packages/meshbay-node/tests/test_packaging_win.py27
2 files changed, 61 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_cli_dispatch.py b/packages/meshbay-node/tests/test_cli_dispatch.py
index caf672d..f58c020 100644
--- a/packages/meshbay-node/tests/test_cli_dispatch.py
+++ b/packages/meshbay-node/tests/test_cli_dispatch.py
@@ -190,3 +190,37 @@ def test_lifecycle_commands_report_systemctl_failure(
daemon_mod.main()
assert exc.value.code == 1
assert "not loaded" in capsys.readouterr().out
+
+
+def test_a_bare_invocation_with_no_config_yet_exits_cleanly(monkeypatch, tmp_path, capsys):
+ """
+ A bare `meshbay-node` — no subcommand, nothing dispatches, falls through to
+ "start the daemon" at the bottom of main() — is exactly what the Windows W3
+ Startup .vbs and the service-mode Scheduled Task both run unattended. On a
+ fresh install neither has ever provisioned anything, so this is the first
+ thing that runs on the very first boot, quite possibly before the user has
+ ever opened the client.
+
+ This must fail closed, fast, and without a trace: no exception (nothing
+ catches one — the .vbs and the Scheduled Task are both headless, so an
+ uncaught crash is invisible and, run on every boot, an inert but permanent
+ fixture in the Task Scheduler history and Windows' own Application log).
+ Confirmed by hand first: measured under a second and zero bytes written
+ to disk against a real, empty %LOCALAPPDATA%.
+
+ load_config() already returns an empty Config when the path does not
+ exist rather than raising — this pins that main() then notices the empty
+ hub.username and exits before ever reaching NodeDaemon()/asyncio.run(),
+ so nothing here has to mock the daemon startup at all.
+ """
+ missing_config = tmp_path / "does-not-exist" / "node.toml"
+ monkeypatch.setattr(daemon_mod, "DEFAULT_CONFIG_PATH", missing_config)
+ monkeypatch.setattr(sys, "argv", ["meshbay-node"])
+
+ with pytest.raises(SystemExit) as exc:
+ daemon_mod.main()
+
+ assert exc.value.code == 1
+ assert "meshbay-node init" in capsys.readouterr().out
+ assert not missing_config.parent.exists(), (
+ "a fresh, unprovisioned start must not create anything on disk")
diff --git a/packages/meshbay-node/tests/test_packaging_win.py b/packages/meshbay-node/tests/test_packaging_win.py
index 6471446..22ce44b 100644
--- a/packages/meshbay-node/tests/test_packaging_win.py
+++ b/packages/meshbay-node/tests/test_packaging_win.py
@@ -327,6 +327,33 @@ def test_main_js_drives_the_service_task_for_all_three_actions():
assert "winServiceTaskStatus" in body, f"{handler} never checks for the service task"
+def test_node_start_provisions_before_it_ever_touches_the_service_task():
+ """
+ On a fresh install with service mode chosen, the Scheduled Task exists
+ before anything is provisioned (node.toml is written by the wizard, not
+ by the installer). node:start must call provisionNode() first and only
+ then ask about the service task / spawn -- reversed, the very first
+ "start" from the wizard would run (or query) an unconfigured daemon
+ instead of the one it just told the caller to expect.
+
+ A bare unprovisioned run is independently proven harmless
+ (test_a_bare_invocation_with_no_config_yet_exits_cleanly in
+ test_cli_dispatch.py), so this is about correctness of *this* call
+ actually starting the daemon the wizard just configured, not about
+ safety if the order were reversed.
+ """
+ main_js = (CLIENT / "src" / "main.js").read_text(encoding="utf-8")
+ body = main_js.split("ipcMain.handle('node:start'", 1)[1]
+ body = body[:body.index("ipcMain.handle(")]
+
+ provision_at = body.index("provisionNode(")
+ service_check_at = body.index("winServiceTaskStatus")
+ assert provision_at < service_check_at, (
+ "node:start checks the service task before provisioning — a fresh "
+ "install's first Start would run/query the daemon before node.toml "
+ "exists for it to read")
+
+
def test_firewall_ps1_targets_both_executables_and_is_idempotent():
"""One script, both rules — so installer.nsh only ever has to name it
once on the way in and once on the way out."""