diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-04 02:20:57 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-04 02:20:57 +0200 |
| commit | d11e571c5b6c24b586ef5b8fb2cfcf6a6bfa6d6d (patch) | |
| tree | fe3c4fbce3db02a8b84a15c413735622a51eefb0 /packages/meshbay-node/tests/test_platform.py | |
| parent | 7a4b905ddb64bdc92b7f9acf2ccde9bd84d7a6f3 (diff) | |
| download | meshbay-d11e571c5b6c24b586ef5b8fb2cfcf6a6bfa6d6d.tar.gz | |
test(node): make the suite pass on Windows
- `.read_text()` on source files now `encoding="utf-8"` — cp1252 chokes on
the em dashes / box-drawing chars those files contain.
- test node.toml templates embed paths via `Path.as_posix()`: a raw Windows
path in a basic TOML string is a parse error (`\U`, `\a`, ... are escapes).
- new `test_platform.py` covers `meshbay_node.platform` by mocking
`sys.platform` / `os.environ` — runs on both OSes.
- `skipif(sys.platform == "win32")`, in `conftest.needs_subprocess` and
inline, for the documented gaps: ffmpeg/ffprobe via asyncio subprocess
(the win32 selector loop, forced for aiortc, cannot spawn one), the
systemd `reload`/`restart-daemon` delegation (Windows path is W3), the
keystore `st_mode == 600` assertion (NTFS ignores mode bits), and the
symlink-escape test (needs Developer Mode).
Windows: 781 passed, 25 skipped. No change on Linux.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_platform.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_platform.py | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_platform.py b/packages/meshbay-node/tests/test_platform.py new file mode 100644 index 0000000..2725df6 --- /dev/null +++ b/packages/meshbay-node/tests/test_platform.py @@ -0,0 +1,94 @@ +"""Tests for meshbay_node.platform — the OS-specific paths and tool resolution. + +The behaviour is selected on `sys.platform` at call time, so it is exercised +here by monkeypatching that (and `os.environ`) rather than only on the OS the +suite happens to run on. +""" + +import asyncio +import sys +from pathlib import Path +from unittest.mock import Mock + +import pytest +from meshbay_node import platform as plat + +# ── Directories ────────────────────────────────────────────────────────────── + +@pytest.mark.parametrize("fn,tail", [ + (plat.config_dir, ()), + (plat.data_dir, ("data",)), + (plat.state_dir, ("state",)), +]) +def test_windows_dirs_live_under_localappdata(fn, tail, monkeypatch, tmp_path): + monkeypatch.setattr(sys, "platform", "win32") + monkeypatch.setenv("LOCALAPPDATA", str(tmp_path)) + assert fn() == tmp_path.joinpath("meshbay", *tail) + + +@pytest.mark.parametrize("fn,expected_tail", [ + (plat.config_dir, (".config", "meshbay")), + (plat.data_dir, (".local", "share", "meshbay")), + (plat.state_dir, (".local", "state", "meshbay")), +]) +def test_posix_dirs_follow_xdg(fn, expected_tail, monkeypatch): + monkeypatch.setattr(sys, "platform", "linux") + assert fn() == Path.home().joinpath(*expected_tail) + + +def test_windows_dirs_fall_back_to_home_without_localappdata(monkeypatch): + monkeypatch.setattr(sys, "platform", "win32") + monkeypatch.delenv("LOCALAPPDATA", raising=False) + assert plat.config_dir() == Path.home() / "meshbay" + + +# ── File permissions ───────────────────────────────────────────────────────── + +def test_chmod_private_is_a_noop_on_windows(monkeypatch): + monkeypatch.setattr(sys, "platform", "win32") + fake = Mock(spec=Path) + plat.chmod_private(fake) + fake.chmod.assert_not_called() + + +def test_chmod_private_applies_mode_off_windows(monkeypatch, tmp_path): + monkeypatch.setattr(sys, "platform", "linux") + f = tmp_path / "secret" + f.write_text("x", encoding="utf-8") + called = {} + monkeypatch.setattr(type(f), "chmod", lambda self, m: called.setdefault("mode", m)) + plat.chmod_private(f) + assert called["mode"] == 0o600 + + +# ── Media tools ────────────────────────────────────────────────────────────── + +def test_check_media_tools_raises_when_ffmpeg_is_missing(monkeypatch): + monkeypatch.setattr(plat.shutil, "which", lambda _n: None) + with pytest.raises(RuntimeError, match="not found in PATH"): + plat.check_media_tools() + + +def test_check_media_tools_stores_the_resolved_paths(monkeypatch): + monkeypatch.setattr(plat.shutil, "which", + lambda n: f"/opt/bin/{n}.exe") + plat.check_media_tools("ffmpeg", "ffprobe") + assert plat.ffmpeg_cmd() == "/opt/bin/ffmpeg.exe" + assert plat.ffprobe_cmd() == "/opt/bin/ffprobe.exe" + + +# ── Event loop ─────────────────────────────────────────────────────────────── + +def test_use_compatible_event_loop_is_a_noop_off_windows(monkeypatch): + monkeypatch.setattr(sys, "platform", "linux") + before = asyncio.get_event_loop_policy() + plat.use_compatible_event_loop() + assert asyncio.get_event_loop_policy() is before + + +@pytest.mark.skipif(sys.platform != "win32", + reason="WindowsSelectorEventLoopPolicy exists only on win32") +def test_use_compatible_event_loop_selects_the_selector_loop_on_windows(): + plat.use_compatible_event_loop() + assert isinstance(asyncio.get_event_loop_policy(), + asyncio.WindowsSelectorEventLoopPolicy) |