aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_windows_node_lifecycle.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests/test_windows_node_lifecycle.py')
-rw-r--r--packages/meshbay-node/tests/test_windows_node_lifecycle.py106
1 files changed, 105 insertions, 1 deletions
diff --git a/packages/meshbay-node/tests/test_windows_node_lifecycle.py b/packages/meshbay-node/tests/test_windows_node_lifecycle.py
index 241f583..b85b6ea 100644
--- a/packages/meshbay-node/tests/test_windows_node_lifecycle.py
+++ b/packages/meshbay-node/tests/test_windows_node_lifecycle.py
@@ -173,7 +173,7 @@ def test_node_pids_finds_every_copy_but_its_caller(tmp_path, monkeypatch):
shutil.copy(r"C:\Windows\System32\PING.EXE", exe)
proc = subprocess.Popen([str(exe), "-n", "300", "127.0.0.1"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
- monkeypatch.setattr(plat, "NODE_IMAGE", "mbpidtest.exe")
+ monkeypatch.setattr(plat, "NODE_IMAGES", ("mbpidtest.exe",))
try:
assert plat.node_pids() == [proc.pid]
plat.kill_pid(proc.pid)
@@ -184,6 +184,110 @@ def test_node_pids_finds_every_copy_but_its_caller(tmp_path, monkeypatch):
proc.kill()
+@pytest.mark.skipif(sys.platform != "win32", reason="lists and kills real processes")
+def test_node_pids_counts_the_windowless_daemon_too(tmp_path, monkeypatch):
+ """The Store package's startup task runs meshbay-nodew.exe: a Stop that
+ looked for meshbay-node.exe alone would call that node gone."""
+ import shutil
+ procs = []
+ for name in ("mbpidtest.exe", "mbpidtestw.exe"):
+ shutil.copy(r"C:\Windows\System32\PING.EXE", tmp_path / name)
+ procs.append(subprocess.Popen([str(tmp_path / name), "-n", "300", "127.0.0.1"],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL))
+ monkeypatch.setattr(plat, "NODE_IMAGES", ("mbpidtest.exe", "mbpidtestw.exe"))
+ try:
+ assert sorted(plat.node_pids()) == sorted(p.pid for p in procs)
+ finally:
+ for p in procs:
+ p.kill()
+ assert set(plat.NODE_IMAGES) == {"mbpidtest.exe", "mbpidtestw.exe"}
+ monkeypatch.undo()
+ assert plat.NODE_IMAGES == ("meshbay-node.exe", "meshbay-nodew.exe")
+
+
+def _current_package_family() -> str:
+ import ctypes
+ length = ctypes.c_uint32(0)
+ k32 = ctypes.windll.kernel32
+ if k32.GetCurrentPackageFamilyName(ctypes.byref(length), None) != 122:
+ return ""
+ buf = ctypes.create_unicode_buffer(length.value)
+ k32.GetCurrentPackageFamilyName(ctypes.byref(length), buf)
+ return buf.value
+
+
+@pytest.mark.skipif(sys.platform != "win32", reason="package identity is Windows'")
+def test_only_meshbays_own_package_counts_as_the_store_install(monkeypatch):
+ """A terminal inside another packaged application (an IDE or an agent
+ from the Store) gives its processes that application's identity, and the
+ test suite itself may run in one: that is not MeshBay's package."""
+ assert plat.in_store_package() is False
+ family = _current_package_family()
+ if family:
+ monkeypatch.setattr(plat, "STORE_PACKAGE_FAMILY_PREFIX", family.split("_")[0] + "_")
+ assert plat.in_store_package() is True
+
+
+def test_the_store_package_switches_its_startup_task_not_a_launcher(monkeypatch, capsys):
+ """There a Startup-folder launcher or a boot task would name the versioned
+ WindowsApps path each update deletes (and right after sign-in Windows
+ refused the launcher that path): `autostart` switches the package's
+ startup task, and says so on a first line the desktop app reads."""
+ monkeypatch.setattr(sys, "platform", "win32")
+ monkeypatch.setattr(plat, "in_store_package", lambda: True)
+ monkeypatch.setattr(plat, "autostart_install", lambda *a: pytest.fail("wrote a launcher"))
+ monkeypatch.setattr(plat, "autostart_remove", lambda *a: pytest.fail("removed a launcher"))
+ monkeypatch.setattr(plat, "service_install", lambda *a: pytest.fail("made a boot task"))
+ monkeypatch.setattr(plat, "service_status", lambda: {"installed": False, "state": ""})
+ asked = []
+ answers = {"enable": "Enabled", "disable": "Disabled", "query": "Disabled"}
+ monkeypatch.setattr(plat, "startup_task", lambda a: asked.append(a) or answers[a])
+
+ class Args:
+ config = None
+ subcommand = "install"
+ for sub, action, state in (("install", "enable", "Enabled"), ("remove", "disable", "Disabled"),
+ ("status", "query", "Disabled")):
+ Args.subcommand = sub
+ lifecycle.autostart(Args)
+ assert asked[-1] == action
+ assert capsys.readouterr().out.splitlines()[0] == f"startup task {state}"
+
+ # Switched off by the user in Windows Settings: theirs to switch back on.
+ answers["enable"] = "DisabledByUser"
+ Args.subcommand = "install"
+ with pytest.raises(SystemExit) as e:
+ lifecycle.autostart(Args)
+ assert e.value.code == 1
+ assert "Settings > Apps > Startup" in capsys.readouterr().out
+
+ # At boot is the .exe installer's.
+ with pytest.raises(SystemExit) as e:
+ lifecycle.service(Args)
+ assert e.value.code == 1
+ assert ".exe" in capsys.readouterr().out
+
+
+def test_a_refused_action_stays_on_the_node_page_until_the_next_action():
+ """The status poll (every five seconds) cleared the one error state there
+ was, so a refusal vanished before it was read."""
+ page = _js(STATIC / "node-page.js")
+ panel = page.split("function NodeServicePanel(", 1)[1].split("\nfunction ", 1)[0]
+ refresh = panel.split("const refresh = useCallback(", 1)[1].split("}, []);", 1)[0]
+ act = panel.split("const act = useCallback(", 1)[1].split("}, [", 1)[0]
+ assert "setActErr" not in refresh and "setPollErr('')" in refresh
+ assert "setActErr(platform.bridgeMessage(e))" in act
+ assert "const err = actErr || pollErr;" in panel
+
+
+def test_the_startup_task_is_refused_outside_the_store_package(monkeypatch):
+ monkeypatch.setattr(plat, "in_store_package", lambda: False)
+ with pytest.raises(RuntimeError, match="Microsoft Store package"):
+ plat.startup_task("query")
+ with pytest.raises(ValueError):
+ plat.startup_task("toggle")
+
+
# ── the desktop application: what it says, and when it sets the node up ──────
def test_the_node_page_asks_the_node_not_only_the_task():