From a45e77df1b0024707914a446aa89d33baa223787 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sun, 27 Sep 2026 22:21:26 +0200 Subject: test: make both suites pass on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of these failed on Windows for reasons that had nothing to do with the code under test, which is how real Windows defects hid among them: - Read and write files as UTF-8, and talk to Node in UTF-8. read_text(), write_text() and subprocess text=True use the locale codepage, cp1252 on Windows: "é", "—" and "→" arrived as "?" or crashed, some sixty tests. Calls to PowerShell and schtasks are left alone -- they answer in the console codepage. - Import ESM harness modules by file URL (as_uri): a raw "C:\..." path is not a module specifier. - test_cli_golden: mask the tmp path in its JSON-escaped form, spell it the POSIX way, record on Linux, mask the protocol version (the recording had failed everywhere since the MNP 4.0 bump) and argparse's version-dependent quoting; point USERPROFILE at the tmp home, or `member invite` and `operator pair` wrote their codes into the developer's profile. - test_disk_io_off_loop: expect what a free loop can reach on the platform's timer, 15.6 ms on Windows, not an assumed 5 ms. - test_root_paths_are_operator_only: expect the OS's spelling of the path. - test_audio_meta_cache: find ffprobe with shutil.which. Node suite on Windows: 1489 passed, none failed. Hub suite: 3 failures left, all older than this change (two SQLite concurrency tests, one transfer resume). Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-node/tests/test_cli_golden.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'packages/meshbay-node/tests/test_cli_golden.py') diff --git a/packages/meshbay-node/tests/test_cli_golden.py b/packages/meshbay-node/tests/test_cli_golden.py index 3d7b0c0..5fe3f0b 100644 --- a/packages/meshbay-node/tests/test_cli_golden.py +++ b/packages/meshbay-node/tests/test_cli_golden.py @@ -18,11 +18,13 @@ import builtins import getpass import json import os +import re import subprocess import sys from pathlib import Path import pytest +from meshbay_common import MNP_VERSION from meshbay_node import config as config_mod from meshbay_node import daemon as daemon_mod from test_cli_dispatch import VERBS @@ -77,7 +79,7 @@ def _run(argv: list[str], tmp_path: Path, capsys) -> dict: return subprocess.CompletedProcess(args, 0, stdout="", stderr="") conf = tmp_path / "node.toml" - conf.write_text('[hub]\nurl = "https://example.invalid"\n') + conf.write_text('[hub]\nurl = "https://example.invalid"\n', encoding="utf-8") code = 0 with pytest.MonkeyPatch.context() as mp: patch_cli(mp, "_daemon_api", fake_api) @@ -86,6 +88,9 @@ def _run(argv: list[str], tmp_path: Path, capsys) -> dict: # The data directory and the keystore's default path come from these; # left real, `status` would record the developer's own node. mp.setenv("HOME", str(tmp_path / "home")) + # Path.home() on Windows: without it `member invite` and `operator + # pair` wrote their codes into the developer's own profile. + mp.setenv("USERPROFILE", str(tmp_path / "home")) mp.setattr(config_mod, "DEFAULT_CONFIG_PATH", conf) mp.setattr(builtins, "input", fake_input) mp.setattr(getpass, "getpass", lambda *a, **kw: "test-password") @@ -93,6 +98,9 @@ def _run(argv: list[str], tmp_path: Path, capsys) -> dict: # argparse wraps the help to the terminal's width. mp.setenv("COLUMNS", "100") mp.setattr(sys, "argv", ["meshbay-node", *argv]) + # Recorded on Linux: `autostart`/`service` are Windows-only there, and + # their Windows side is test_platform.py's and test_packaging_win.py's. + mp.setattr(sys, "platform", "linux") try: daemon_mod.main() except SystemExit as e: @@ -100,7 +108,17 @@ def _run(argv: list[str], tmp_path: Path, capsys) -> dict: out = capsys.readouterr() def mask(text: str) -> str: - return text.replace(str(tmp_path), "") + # Masked in the JSON text, where a Windows path's backslashes are + # doubled -- the unescaped form never matched there -- and spelled the + # POSIX way after it. The protocol version is not the CLI's shape: it + # left this recording failing everywhere from the MNP 4.0 bump on. + text = text.replace(json.dumps(str(tmp_path))[1:-1], "") + text = re.sub(r'(?:\\\\[^\\\s"]+)+', + lambda m: m.group(0).replace("\\\\", "/"), text) + # argparse quotes its choices on some Python versions and not others. + text = re.sub(r"\(choose from [^)]*\)", + lambda m: m.group(0).replace("'", ""), text) + return text.replace(f"MNP {MNP_VERSION}", "MNP ") return json.loads(mask(json.dumps({ "exit": code, "stdout": out.out, "stderr": out.err, -- cgit v1.2.3