mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-27 21:50:36 +00:00
test(local-dev): normalize the upgrade script's MSYS path rendering on Windows (#6660)
* test(local-dev): normalize the upgrade script's MSYS path rendering on Windows nginx-local-conf.sh computes its repo root with pwd -P, which inside Git Bash yields the MSYS mount form (/tmp/... for the Windows temp dir) — correct for its bash-native consumers (serve.sh, nginx.sh) but never equal to the test's Windows-form Path.resolve(). Map the rendering back through cygpath -w before comparing, so the tests read and compare the rendered config on Git Bash hosts too. Fixes #6657 * test(local-dev): harden Git Bash path normalization --------- Co-authored-by: xiaodu55 <xiaodu55@users.noreply.github.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
parent
782fed9135
commit
ea68e5b3d7
@ -2,6 +2,11 @@
|
||||
|
||||
Backend tests must preserve the runtime invariants they exercise without changing production execution topology.
|
||||
|
||||
Local nginx path fixtures convert MSYS output through the selected Git Bash's
|
||||
PATH and decode both renderer and converter output as UTF-8. POSIX Git-layout
|
||||
stubs exercise Windows-style locale decoding and missing/failed/empty converters;
|
||||
native Windows keeps the real Git Bash renderer checks, including Unicode paths.
|
||||
|
||||
`test_skills_custom_multiprocess.py` starts independent spawn workers sharing a
|
||||
temporary custom-skill root. Park edit/rollback before the write and before the
|
||||
history append, observe the peer reaching the mutation lock, then check both
|
||||
|
||||
@ -17,9 +17,11 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from support.shell import require_script_bash
|
||||
@ -100,9 +102,11 @@ def test_stop_signals_nginx_through_the_tracked_config():
|
||||
# ── nginx-local-conf.sh ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def checkout(tmp_path: Path) -> Path:
|
||||
@pytest.fixture(params=["checkout", "checkout-测试 with spaces"], ids=["ascii", "unicode-spaces"])
|
||||
def checkout(tmp_path: Path, request: pytest.FixtureRequest) -> Path:
|
||||
"""A minimal checkout: the script resolves the repo root from its own location."""
|
||||
tmp_path = tmp_path / request.param
|
||||
tmp_path.mkdir()
|
||||
(tmp_path / "scripts").mkdir()
|
||||
(tmp_path / "docker" / "nginx").mkdir(parents=True)
|
||||
shutil.copy2(NGINX_LOCAL_CONF_SH, tmp_path / "scripts" / NGINX_LOCAL_CONF_SH.name)
|
||||
@ -119,16 +123,117 @@ def _run(checkout: Path, bind_host: str | None) -> subprocess.CompletedProcess[s
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
def _normalize_rendered_path(rendered: str) -> str:
|
||||
"""Map the script's POSIX rendering back to Windows form on Git Bash hosts.
|
||||
|
||||
``pwd -P`` inside Git Bash yields the MSYS mount form (``/tmp/...`` for the
|
||||
Windows temp dir), which the test's Windows-side ``Path`` can neither
|
||||
compare nor read. Resolve cygpath through the selected Git Bash's PATH,
|
||||
including when bash.exe lives in Git's wrapper directory.
|
||||
"""
|
||||
if os.name != "nt" or not rendered.startswith("/"):
|
||||
return rendered
|
||||
result = subprocess.run(
|
||||
[require_script_bash(), "-c", 'cygpath -w "$DEERFLOW_TEST_RENDERED_PATH"'],
|
||||
env={**os.environ, "DEERFLOW_TEST_RENDERED_PATH": rendered},
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
pytest.fail(f"Git Bash cygpath failed to normalize {rendered!r} (exit {result.returncode}): {result.stderr.strip()}")
|
||||
normalized = result.stdout.strip()
|
||||
if not normalized:
|
||||
pytest.fail(f"Git Bash cygpath returned an empty path for {rendered!r}")
|
||||
return normalized
|
||||
|
||||
|
||||
def _simulate_windows_locale(monkeypatch) -> None:
|
||||
native_run = subprocess.run
|
||||
|
||||
def run_with_windows_locale(*args, **kwargs):
|
||||
# Exercise real byte decoding even on UTF-8 POSIX hosts.
|
||||
if kwargs.get("text"):
|
||||
kwargs.setdefault("encoding", "cp1252")
|
||||
return native_run(*args, **kwargs)
|
||||
|
||||
monkeypatch.setitem(globals(), "subprocess", SimpleNamespace(run=run_with_windows_locale))
|
||||
|
||||
|
||||
@pytest.fixture(params=["bin", "usr/bin"], ids=["wrapper", "usr-bin"])
|
||||
def git_bash_layout(tmp_path: Path, monkeypatch, request: pytest.FixtureRequest) -> Path:
|
||||
"""Run a POSIX shim with Git for Windows' wrapper and usr/bin layouts."""
|
||||
if os.name == "nt":
|
||||
pytest.skip("POSIX executable shims; native Windows uses the real renderer tests")
|
||||
native_bash = require_script_bash()
|
||||
git_root = tmp_path / "Git installation"
|
||||
usr_bin = git_root / "usr" / "bin"
|
||||
usr_bin.mkdir(parents=True)
|
||||
bash = git_root / request.param / "bash.exe"
|
||||
bash.parent.mkdir(parents=True, exist_ok=True)
|
||||
bash.write_text(f'#!/bin/sh\nexport PATH={shlex.quote(str(usr_bin))}\nexec {shlex.quote(native_bash)} "$@"\n', encoding="utf-8")
|
||||
bash.chmod(0o755)
|
||||
monkeypatch.setitem(globals(), "require_script_bash", lambda: str(bash))
|
||||
monkeypatch.setitem(globals(), "os", SimpleNamespace(name="nt", environ=os.environ))
|
||||
return usr_bin
|
||||
|
||||
|
||||
def _write_cygpath_stubs(directory: Path, body: str) -> None:
|
||||
# POSIX Bash resolves the bare name; the .exe also models the old lookup.
|
||||
for name in ("cygpath", "cygpath.exe"):
|
||||
path = directory / name
|
||||
path.write_text("#!/bin/sh\n" + body + "\n", encoding="utf-8")
|
||||
path.chmod(0o755)
|
||||
|
||||
|
||||
def test_rendered_path_uses_selected_git_bash(git_bash_layout: Path, monkeypatch):
|
||||
rendered = "/tmp/测试 user/$(printf changed)/nginx.local.conf"
|
||||
native = r"C:\Users\测试 User\AppData\Local\Temp\nginx.local.conf"
|
||||
monkeypatch.setenv("EXPECTED_MSYS_PATH", rendered)
|
||||
monkeypatch.setenv("EXPECTED_NATIVE_PATH", native)
|
||||
_write_cygpath_stubs(
|
||||
git_bash_layout,
|
||||
'[ "$1" = "-w" ] || exit 2\n[ "$2" = "$EXPECTED_MSYS_PATH" ] || exit 3\nprintf "%s\\n" "$EXPECTED_NATIVE_PATH"',
|
||||
)
|
||||
_simulate_windows_locale(monkeypatch)
|
||||
|
||||
assert _normalize_rendered_path(rendered) == native
|
||||
|
||||
|
||||
@pytest.mark.parametrize("behavior", ["missing", "failed", "empty"])
|
||||
def test_rendered_path_conversion_fails_loudly(git_bash_layout: Path, behavior: str):
|
||||
if behavior == "failed":
|
||||
_write_cygpath_stubs(git_bash_layout, 'echo "converter failed" >&2\nexit 5')
|
||||
elif behavior == "empty":
|
||||
_write_cygpath_stubs(git_bash_layout, "exit 0")
|
||||
|
||||
with pytest.raises(pytest.fail.Exception, match="cygpath") as failure:
|
||||
_normalize_rendered_path("/tmp/nginx.local.conf")
|
||||
if behavior == "failed":
|
||||
assert "converter failed" in str(failure.value)
|
||||
|
||||
|
||||
def test_renderer_decodes_paths_as_utf8(checkout: Path, monkeypatch):
|
||||
_simulate_windows_locale(monkeypatch)
|
||||
|
||||
result = _run(checkout, None)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
assert Path(_normalize_rendered_path(result.stdout.strip())) == (checkout / "docker" / "nginx" / "nginx.local.conf").resolve()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bind_host", [None, "", "127.0.0.1"], ids=["unset", "blank", "loopback"])
|
||||
def test_default_bind_host_uses_the_tracked_config(checkout: Path, bind_host: str | None):
|
||||
result = _run(checkout, bind_host)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
assert Path(result.stdout.strip()) == (checkout / "docker" / "nginx" / "nginx.local.conf").resolve()
|
||||
assert Path(_normalize_rendered_path(result.stdout.strip())) == (checkout / "docker" / "nginx" / "nginx.local.conf").resolve()
|
||||
assert not (checkout / "temp").exists()
|
||||
|
||||
|
||||
@ -163,7 +268,7 @@ def test_bind_host_renders_a_config_listening_there(checkout: Path, bind_host: s
|
||||
result = _run(checkout, bind_host)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
rendered = Path(result.stdout.strip())
|
||||
rendered = Path(_normalize_rendered_path(result.stdout.strip()))
|
||||
assert rendered == (checkout / "temp" / "nginx.local.conf").resolve()
|
||||
content = rendered.read_text(encoding="utf-8")
|
||||
assert _listen_directives(content) == expected
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user