diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-27 22:21:26 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-27 22:21:26 +0200 |
| commit | a45e77df1b0024707914a446aa89d33baa223787 (patch) | |
| tree | 969a4976c91d7aaa27b11ba212f99d69e2ccce0f /packages/meshbay-node/tests/test_cli_golden.py | |
| parent | c7899e2218af26f3bab6a157c70acf5eb7871da6 (diff) | |
| download | meshbay-a45e77df1b0024707914a446aa89d33baa223787.tar.gz | |
test: make both suites pass on Windows
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 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_cli_golden.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_cli_golden.py | 22 |
1 files changed, 20 insertions, 2 deletions
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), "<tmp>") + # 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], "<tmp>") + text = re.sub(r'<tmp>(?:\\\\[^\\\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 <version>") return json.loads(mask(json.dumps({ "exit": code, "stdout": out.out, "stderr": out.err, |