diff --git a/backend/tests/_host_path_helpers.py b/backend/tests/_host_path_helpers.py new file mode 100644 index 000000000..ed3648636 --- /dev/null +++ b/backend/tests/_host_path_helpers.py @@ -0,0 +1,14 @@ +"""Helpers for asserting on paths the codebase spells in host-native style. + +The provisioner's ``join_host_path`` deliberately preserves the host +filesystem style (explicit ``PureWindowsPath`` branch), and +``os.path.normpath`` re-spells POSIX inputs with backslashes on Windows, so +hostPath volume strings, docker ``--mount`` args and ``PYTHONPATH`` entries +carry ``\\`` on a Windows host where POSIX CI sees ``/``. Tests that match +segments inside such strings normalize with :func:`posix_path` instead of +hard-coding one separator; the replace is a no-op on POSIX inputs. +""" + + +def posix_path(path: str) -> str: + return path.replace("\\", "/") diff --git a/backend/tests/test_provisioner_pvc_volumes.py b/backend/tests/test_provisioner_pvc_volumes.py index ae7c1b7ca..bbc2f7732 100644 --- a/backend/tests/test_provisioner_pvc_volumes.py +++ b/backend/tests/test_provisioner_pvc_volumes.py @@ -1,6 +1,7 @@ """Regression tests for provisioner three-way skills + PVC volume support.""" import pytest +from _host_path_helpers import posix_path def _thread_skill_mounts( @@ -39,7 +40,7 @@ class TestBuildVolumes: pub = volumes[0] assert pub.name == "skills-public" assert pub.host_path is not None - assert pub.host_path.path.endswith("/skills_view/public") + assert posix_path(pub.host_path.path).endswith("/skills_view/public") assert pub.host_path.type == "Directory" assert pub.persistent_volume_claim is None @@ -50,7 +51,7 @@ class TestBuildVolumes: custom = volumes[1] assert custom.name == "skills-custom" assert custom.host_path is not None - assert "users/user-7/skills_view/custom" in custom.host_path.path + assert "users/user-7/skills_view/custom" in posix_path(custom.host_path.path) assert custom.host_path.type == "Directory" def test_hostpath_skills_legacy_volume(self, provisioner_module): @@ -63,7 +64,7 @@ class TestBuildVolumes: legacy = volumes[2] assert legacy.name == "skills-legacy" assert legacy.host_path is not None - assert "users/default/skills_view/legacy" in legacy.host_path.path + assert "users/default/skills_view/legacy" in posix_path(legacy.host_path.path) assert legacy.host_path.type == "Directory" def test_hostpath_without_legacy_flag_still_has_empty_capable_mount(self, provisioner_module): @@ -82,7 +83,7 @@ class TestBuildVolumes: provisioner_module.USERDATA_PVC_NAME = "" volumes = provisioner_module._build_volumes("my-thread-42") userdata_vol = volumes[-1] - path = userdata_vol.host_path.path + path = posix_path(userdata_vol.host_path.path) assert "my-thread-42" in path assert path.endswith("user-data") assert userdata_vol.host_path.type == "DirectoryOrCreate" @@ -149,7 +150,7 @@ class TestBuildVolumes: assert len(volumes) == 5 extra_vol = volumes[-1] assert extra_vol.name == "extra-0" - assert extra_vol.host_path.path == "/state/users/alice/integrations/lark-cli/config" + assert posix_path(extra_vol.host_path.path) == "/state/users/alice/integrations/lark-cli/config" assert extra_vol.host_path.type == "DirectoryOrCreate" def test_extra_mount_uses_userdata_pvc_when_configured(self, provisioner_module): diff --git a/backend/tests/test_review_changed_public_skills.py b/backend/tests/test_review_changed_public_skills.py index d9f89c39a..148d2ea30 100644 --- a/backend/tests/test_review_changed_public_skills.py +++ b/backend/tests/test_review_changed_public_skills.py @@ -5,6 +5,7 @@ from pathlib import Path, PurePosixPath import pytest import review_changed_public_skills as runner +from _host_path_helpers import posix_path from skill_review_waivers import EMPTY_MANIFEST, WaiverManifest @@ -302,7 +303,7 @@ def test_main_exits_nonzero_when_review_cli_reports_error(tmp_path: Path, monkey "never", ] assert kwargs["cwd"] == tmp_path - assert "backend/packages/harness" in kwargs["env"]["PYTHONPATH"] + assert "backend/packages/harness" in posix_path(kwargs["env"]["PYTHONPATH"]) assert kwargs["capture_output"] is True assert kwargs["text"] is True assert kwargs["check"] is False diff --git a/backend/tests/test_three_way_skills_mount_e2e.py b/backend/tests/test_three_way_skills_mount_e2e.py index 091fc4001..8c32fe4e2 100644 --- a/backend/tests/test_three_way_skills_mount_e2e.py +++ b/backend/tests/test_three_way_skills_mount_e2e.py @@ -15,6 +15,7 @@ from types import SimpleNamespace from unittest.mock import patch import pytest +from _host_path_helpers import posix_path from deerflow.config.extensions_config import ExtensionsConfig, SkillStateConfig from deerflow.config.paths import Paths @@ -602,11 +603,11 @@ class TestThreeWayMountEndToEnd: assert "/mnt/skills/custom" in mount_entries assert "dst=/mnt/skills/custom" in mount_entries["/mnt/skills/custom"] - assert "users/noob/skills_view/custom" in mount_entries["/mnt/skills/custom"] + assert "users/noob/skills_view/custom" in posix_path(mount_entries["/mnt/skills/custom"]) assert "/mnt/skills/integrations" in mount_entries assert "dst=/mnt/skills/integrations" in mount_entries["/mnt/skills/integrations"] - assert "users/noob/skills_view/integrations" in mount_entries["/mnt/skills/integrations"] + assert "users/noob/skills_view/integrations" in posix_path(mount_entries["/mnt/skills/integrations"]) # noob has no per-user custom → legacy is mounted assert "/mnt/skills/legacy" in mount_entries