aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_e2e_windows_app.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-07 22:17:27 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-07 22:22:51 +0200
commitb06338abf7a5be58151828acb801286c05a3d62a (patch)
tree32ba77301f9f8f798f6aba953ce02fd9b5a57235 /packages/meshbay-node/tests/test_e2e_windows_app.py
parente0dde3466fcb06570cc41b1c379707b2807040ad (diff)
downloadmeshbay-b06338abf7a5be58151828acb801286c05a3d62a.tar.gz
fix(client): stop the node at Quit in "only while open", whoever started it
Switching from "at sign-in" to "only while MeshBay is open" left the node the sign-in launcher had started running after Quit: only a node this process had started was stopped. In that mode the app owns the node, so Quit stops the one that is there. The start with the app and the sign-in's own start (ensureNode) also both ran `autostart start` at launch -- three meshbay-node.exe were seen racing for the port. The sign-in's start and node:start now wait for the launch's. The end-to-end test covers the mode: Quit leaves no node, opening the app starts one. It launches the app with the environment it was imported with: the suite's conftest points HOME, USERPROFILE, LOCALAPPDATA and APPDATA at a throwaway directory per test, and the app started under that crashed at once (0x80000003), which first looked like a crash of the app itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_e2e_windows_app.py')
-rw-r--r--packages/meshbay-node/tests/test_e2e_windows_app.py40
1 files changed, 37 insertions, 3 deletions
diff --git a/packages/meshbay-node/tests/test_e2e_windows_app.py b/packages/meshbay-node/tests/test_e2e_windows_app.py
index 86e6a62..da1d251 100644
--- a/packages/meshbay-node/tests/test_e2e_windows_app.py
+++ b/packages/meshbay-node/tests/test_e2e_windows_app.py
@@ -46,6 +46,12 @@ pytestmark = [
pytest.mark.timeout(900),
]
+# The environment as it is now, at import: the suite's conftest points HOME,
+# USERPROFILE, LOCALAPPDATA and APPDATA at a throwaway directory for every test,
+# and an application started under that crashes at once (0x80000003). The
+# module-scoped fixture launched before it applied; the relaunch in the test
+# body did not, which looked like a crash of the application.
+REAL_ENV = dict(os.environ)
LOCALAPPDATA = Path(os.environ.get("LOCALAPPDATA", ""))
APPDATA = Path(os.environ.get("APPDATA", ""))
INSTALL = Path(os.environ.get("MESHBAY_E2E_INSTALL", LOCALAPPDATA / "Programs" / "MeshBay"))
@@ -62,8 +68,11 @@ def _free_port() -> int:
def _launch(argv: list[str]) -> None:
- """Start a process detached: DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP."""
- subprocess.Popen(argv, creationflags=0x00000008 | 0x00000200, close_fds=True)
+ """Start the application detached, in the real profile."""
+ log = Path(REAL_ENV.get("TEMP", ".")) / "mb-e2e-app.log" # its console, for a failure
+ subprocess.Popen(argv, creationflags=0x00000008 | 0x00000200, close_fds=True,
+ stdout=log.open("a"), stderr=subprocess.STDOUT,
+ env={**REAL_ENV, "ELECTRON_ENABLE_LOGGING": "1"})
def _node_processes() -> dict[int, int]:
@@ -150,8 +159,16 @@ class Page:
_wait(f"a control named {label!r}",
lambda: _try(lambda: self.eval(f"(clickText({json.dumps(label)}), 1)")), timeout)
+ def quit(self) -> None:
+ """Quit the application the way its own Quit does (before-quit runs)."""
+ version = json.loads(_http_get(f"http://127.0.0.1:{CDP_PORT}/json/version"))
+ from websockets.sync.client import connect
+ with connect(version["webSocketDebuggerUrl"]) as browser:
+ browser.send(json.dumps({"id": 1, "method": "Browser.close"}))
+ _try(lambda: browser.recv(timeout=5))
+
def close(self):
- self._conn.__exit__(None, None, None)
+ _try(lambda: self._conn.__exit__(None, None, None))
def _http_get(url: str, token: str | None = None) -> str:
@@ -301,3 +318,20 @@ def test_a_fresh_account_gets_its_node_set_up_and_can_stop_it(app, hub):
s.get("status") == "running" and s.get("pid") != first.get("pid") and s, 120)
assert len(_node_pids()) == 1, _node_pids()
assert second["pk_node_ed25519"] == first["pk_node_ed25519"]
+
+ if MODE == "open":
+ # Only while MeshBay is open: Quit stops the node, opening starts it.
+ page.quit()
+ _wait("no node after Quit", lambda: not _node_pids(), 60)
+ # A second instance started while the first is still exiting defers
+ # to it (single-instance lock) and quits.
+ _wait("the application gone", lambda: b"MeshBay.exe" not in subprocess.run(
+ ["tasklist", "/FI", "IMAGENAME eq MeshBay.exe", "/NH"],
+ capture_output=True).stdout, 60)
+ _launch([str(INSTALL / "MeshBay.exe"), f"--remote-debugging-port={CDP_PORT}"])
+ again = _wait("the application window again", lambda: _try(Page), 60)
+ again.close()
+ _wait("a node started with the app", lambda: (s := _node_status()) and
+ s.get("status") == "running", 120)
+ # The CLI that started it is meshbay-node.exe too, for a moment.
+ _wait("a single meshbay-node.exe", lambda: len(_node_pids()) == 1, 20)