""" The browser makes a name writable everywhere exactly as Python does. `static/portable-name.js` renames a file at the moment a member saves it; `meshbay_common.paths.sanitize_for_download` is the same rule in Python. Two copies of a rule that differ decide differently which files get renamed, so the real module runs under node here against the real Python. Skipped when node is unavailable; that is a coverage gap, not a pass. """ import json import shutil import subprocess from pathlib import Path import pytest from meshbay_common.paths import is_portable_name, sanitize_for_download PORTABLE_JS = (Path(__file__).resolve().parents[2] / "meshbay-hub" / "src" / "meshbay_hub" / "static" / "portable-name.js") pytestmark = pytest.mark.skipif( shutil.which("node") is None or not PORTABLE_JS.exists(), reason="node or portable-name.js unavailable — parity cannot be checked", ) NAMES = [ "plain.txt", "Réunion 12:30.pdf", 'ac:d"e/f\\g|h?i*j.txt', "tab\tnew\nline", "ends with dot.", "ends with space ", "trailing . . ", "CON", "con.txt", "aux.tar.gz", "COM1", "com10.txt", "LPT9.log", "nul.", ".", "..", "", " ", ".bashrc", "...", "名前:ファイル.mkv", "emoji 🙂?.png", "\x01\x1f.bin", "prn .txt", "Con", "a.b.c", "COM1.", "normal name (2).mp4", ] _HARNESS = r""" const fs = require('fs'); const src = fs.readFileSync(process.argv[2], 'utf8').replace(/^export /gm, ''); const M = new Function(src + '\nreturn { portableName, portablePath };')(); const names = JSON.parse(fs.readFileSync(process.argv[3], 'utf8')); process.stdout.write(JSON.stringify({ names: names.map((n) => M.portableName(n)), path: M.portablePath('Top:Folder/sub//aux.txt'), })); """ @pytest.fixture(scope="module") def js(tmp_path_factory): d = tmp_path_factory.mktemp("portable") (d / "harness.js").write_text(_HARNESS, encoding="utf-8") (d / "names.json").write_text(json.dumps(NAMES), encoding="utf-8") proc = subprocess.run( ["node", str(d / "harness.js"), str(PORTABLE_JS), str(d / "names.json")], capture_output=True, text=True, encoding="utf-8", timeout=60) if proc.returncode != 0: pytest.fail(f"node harness failed:\n{proc.stderr}") return json.loads(proc.stdout) @pytest.mark.parametrize("i,name", list(enumerate(NAMES))) def test_the_browser_renames_as_python_does(i, name, js): assert js["names"][i] == sanitize_for_download(name) @pytest.mark.parametrize("name", NAMES) def test_what_comes_out_can_be_written_everywhere(name): out = sanitize_for_download(name) assert is_portable_name(out), (name, out) assert sanitize_for_download(out) == out def test_a_portable_name_is_left_alone(): assert sanitize_for_download("plain.txt") == "plain.txt" def test_a_path_is_made_portable_segment_by_segment(js): assert js["path"] == "Top_Folder/sub//aux_.txt"