diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bda522cf..2889f1212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Internal -- **Windows pytest-harness compatibility (#3664).** Hardened the test suite to run on Windows: profile-home fallback paths are path-normalized, strict POSIX file-mode (`0o600`) assertions are gated behind `os.name != "nt"` (Linux still asserts them at full strictness), the conftest cleanup handles Windows process-tree/port teardown and the Py3.12+ `shutil.rmtree` `onexc` shim, and tests that require `fork`/`fcntl` carry `@requires_fork` / `@requires_fcntl` markers (which never skip on Linux). Test-only — no runtime or app behavior change, no Linux CI behavior change. (#4254, #4255, #4256, #4257, #4259) +- **Windows pytest-harness compatibility (#3664).** Hardened the test suite to run on Windows: profile-home fallback paths are path-normalized, strict POSIX file-mode (`0o600`) assertions are gated behind `os.name != "nt"` (Linux still asserts them at full strictness), the conftest cleanup handles Windows process-tree/port teardown and the Py3.12+ `shutil.rmtree` `onexc` shim, and tests that require `fork`/`fcntl` carry `@requires_fork` / `@requires_fcntl` markers (which never skip on Linux). Test-only — no runtime or app behavior change, no Linux CI behavior change. (#4254, #4255, #4256, #4257, #4259, #4263, #4266) ## [v0.51.444] — 2026-06-15 — Release PE (refresh sidebar session list on uncommitted writes + settings changes) diff --git a/tests/test_bootstrap_python_selection.py b/tests/test_bootstrap_python_selection.py index 4fc8e44e8..9bbcc8d80 100644 --- a/tests/test_bootstrap_python_selection.py +++ b/tests/test_bootstrap_python_selection.py @@ -4,6 +4,13 @@ from unittest.mock import patch import bootstrap +def _repo_venv_python(repo_root: pathlib.Path) -> pathlib.Path: + rel = pathlib.Path( + "Scripts/python.exe" if bootstrap.platform.system() == "Windows" else "bin/python" + ) + return repo_root / ".venv" / rel + + def test_ensure_python_prefers_agent_venv_when_launcher_cannot_import_agent(monkeypatch, tmp_path): """Avoid starting WebUI with a local venv that later cannot import AIAgent.""" local_python = tmp_path / "webui" / ".venv" / "bin" / "python" @@ -42,11 +49,12 @@ def test_ensure_python_fails_loudly_when_no_interpreter_can_import_agent(monkeyp fake_venv_python = tmp_path / "fake-repo-venv-python" fake_venv_python.write_text("", encoding="utf-8") monkeypatch.setattr(bootstrap, "REPO_ROOT", tmp_path) - # Ensure the .venv/bin/python (or .venv/Scripts/python.exe) path resolves - # to our fake binary so .exists() returns True and EnvBuilder is skipped. - venv_subdir = tmp_path / ".venv" / "bin" - venv_subdir.mkdir(parents=True, exist_ok=True) - (venv_subdir / "python").write_text("", encoding="utf-8") + # Ensure the platform-native repo-local venv python already exists so + # EnvBuilder.create() is skipped and the test stays focused on interpreter + # selection rather than venv creation internals. + venv_python = _repo_venv_python(tmp_path) + venv_python.parent.mkdir(parents=True, exist_ok=True) + venv_python.write_text("", encoding="utf-8") if (tmp_path / ".venv").exists(): # platform-independent guard pass @@ -81,7 +89,7 @@ def test_local_venv_is_created_with_symlinks(monkeypatch, tmp_path): with patch.object(bootstrap.venv, "EnvBuilder") as mock_builder: # Make EnvBuilder().create() materialize the venv python so the post-create # `_python_can_run_webui_and_agent` retry path doesn't trip on a missing file. - venv_python = tmp_path / ".venv" / "bin" / "python" + venv_python = _repo_venv_python(tmp_path) def fake_create(target): venv_python.parent.mkdir(parents=True, exist_ok=True) diff --git a/tests/test_issue513_wsl_autostart.py b/tests/test_issue513_wsl_autostart.py index 44e624730..192f8d2a2 100644 --- a/tests/test_issue513_wsl_autostart.py +++ b/tests/test_issue513_wsl_autostart.py @@ -1,6 +1,7 @@ from __future__ import annotations import re +import shutil import subprocess from pathlib import Path @@ -76,9 +77,9 @@ def test_windows_task_scheduler_helper_is_idempotent_and_validates_wsl_script_pa def test_powershell_helper_passes_parser_when_pwsh_is_available(): pwsh = None for candidate in ("pwsh", "powershell"): - result = subprocess.run(["bash", "-lc", f"command -v {candidate}"], capture_output=True, text=True) - if result.returncode == 0: - pwsh = result.stdout.strip() + resolved = shutil.which(candidate) + if resolved: + pwsh = resolved break if not pwsh: # Linux CI often does not include PowerShell. The source-string tests diff --git a/tests/test_title_aux_routing.py b/tests/test_title_aux_routing.py index dbd05b7ce..39f55f967 100644 --- a/tests/test_title_aux_routing.py +++ b/tests/test_title_aux_routing.py @@ -7,6 +7,7 @@ Covers: - _aux_title_timeout rejects zero, negative, and non-numeric values """ import os +from pathlib import Path import sys import types import unittest @@ -785,7 +786,7 @@ class TestBackgroundTitleProfileRouting(unittest.TestCase): self.assertEqual(captured.get('hermes_home'), 'profile-home') self.assertEqual(str(captured.get('skill_module_home')), 'profile-home') - self.assertEqual(str(captured.get('skill_module_dir')), 'profile-home/skills') + self.assertEqual(Path(str(captured.get('skill_module_dir'))), Path('profile-home') / 'skills') self.assertEqual(captured.get('restored_hermes_home'), 'default-home') self.assertEqual(getattr(fake_skill_module, 'HERMES_HOME'), 'default-home') self.assertEqual(getattr(fake_skill_module, 'SKILLS_DIR'), 'default-home/skills')