aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-05 09:28:03 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-05 09:28:03 +0200
commit7601991ccb1d75637c055062c38b1852eeef9700 (patch)
tree022d6ce76e632d005417c82e92e0277614a92689 /packages/meshbay-node
parent301c8998bfdcac80ad3302e2d2ebe853e2ea6de1 (diff)
downloadmeshbay-7601991ccb1d75637c055062c38b1852eeef9700.tar.gz
feat(client): a Node-page toggle to switch into/out of service mode
The installer's own mode question is effectively one-shot: customInstall skips it entirely once the firewall rules already exist, for any reason -- and per-user mode sets those up on its own, with no Scheduled Task involved. So declining once (or the rules existing from something unrelated, as happened on a dev machine this session) was a dead end: no reinstall, repair, or uninstall/reinstall cycle could ever bring the question back, since uninstall defaults to leaving both alone. Add the other door in (and out): a checkbox on the Node page, next to the existing per-user autostart toggle, wired main.js -> preload.js -> platform.js -> node-page.js. It runs packaging/win/service-mode.ps1 -- the exact script installer.nsh already runs -- via one Start-Process -Verb RunAs elevation, so the two paths can never disagree about what service mode means. The elevation helper writes a tiny param()-based .ps1 to %TEMP% so the target script path and its arguments bind through real PowerShell parameters instead of nested string-quoting. Also fixes a real pre-existing gap found while checking this: 8 of the 10 locale catalogues (all but en/fr) were missing the autostart/service-mode keys added in an earlier commit this session (b782886) -- test_locales.py's key-set-parity check uses a for-loop with an inline assert, so it stopped at the first mismatch (fr) and never actually reached the other eight. Backfilled all five keys (three pre-existing, two new) in de/es/it/ja/nl/pl/pt-BR/zh-CN. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node')
-rw-r--r--packages/meshbay-node/tests/test_packaging_win.py51
1 files changed, 51 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_packaging_win.py b/packages/meshbay-node/tests/test_packaging_win.py
index 31816d2..21aaf29 100644
--- a/packages/meshbay-node/tests/test_packaging_win.py
+++ b/packages/meshbay-node/tests/test_packaging_win.py
@@ -316,6 +316,57 @@ def test_service_status_reports_state_without_admin():
"calls status/run/end directly, unelevated")
+HUB_STATIC = ROOT / "packages" / "meshbay-hub" / "src" / "meshbay_hub" / "static"
+
+
+def test_service_mode_toggle_elevates_the_same_script_the_installer_runs():
+ """
+ The installer's own mode question is effectively one-shot (it skips
+ itself the moment the firewall rules exist for any reason, and per-user
+ mode sets those up on its own with no Scheduled Task involved) -- so
+ declining once, or the rules existing for any other reason, is a dead
+ end through setup alone. The Node page's toggle is the other door in
+ (and out), and it must drive service-mode.ps1 -- the exact script
+ installer.nsh runs -- so the two paths can never disagree about what
+ "service mode" means. One elevation (-Verb RunAs), no stored password.
+ """
+ main_js = (CLIENT / "src" / "main.js").read_text(encoding="utf-8")
+ assert "winElevateServiceMode" in main_js
+ fn = main_js.split("function winElevateServiceMode", 1)[1]
+ fn = fn[:fn.index("\n }\n")]
+ assert "service-mode.ps1" in fn
+ assert "-Verb RunAs" in fn or "'-Verb', 'RunAs'" in fn or "-Verb', 'RunAs'" in fn
+ assert "Get-Credential" not in fn
+
+ handler = main_js.split("ipcMain.handle('node:service-mode'", 1)[1]
+ handler = handler[:handler.index("ipcMain.handle(")]
+ assert "winElevateServiceMode" in handler
+ assert "'install'" in handler or '"install"' in handler
+ assert "'remove'" in handler or '"remove"' in handler
+
+
+def test_node_page_service_mode_toggle_is_wired_end_to_end():
+ """preload.js -> platform.js -> node-page.js, the same three-layer shape
+ the existing autostart toggle uses. A break anywhere in this chain means
+ the checkbox renders but does nothing, or never renders at all."""
+ preload = (CLIENT / "src" / "preload.js").read_text(encoding="utf-8")
+ assert "serviceMode:" in preload
+ assert "'node:service-mode'" in preload
+
+ platform_js = (HUB_STATIC / "platform.js").read_text(encoding="utf-8")
+ assert "serviceMode:" in platform_js
+ assert "bridge.node.serviceMode('install')" in platform_js
+ assert "bridge.node.serviceMode('remove')" in platform_js
+
+ node_page = (HUB_STATIC / "node-page.js").read_text(encoding="utf-8")
+ assert "platform.node.serviceMode.available" in node_page
+ assert "platform.node.serviceMode.install()" in node_page
+ assert "platform.node.serviceMode.remove()" in node_page
+ # Checked state must reflect the CURRENT mode, not a separate flag --
+ # otherwise the toggle and the status panel above it could disagree.
+ assert "info.mode === 'service'" in node_page
+
+
def test_the_help_smoke_test_joins_multiline_output_before_matching():
"""
`& exe --help 2>&1` is an ARRAY once the output wraps past one line, which