From fd220cd9ddaa885f13807308fc088c7bda8d4030 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 13 Jun 2026 20:19:06 +0100 Subject: [PATCH 1/2] fix(tests): don't run PowerShell tests via WSL-interop powershell.exe --- tests/test_check_prerequisites_paths_only.py | 2 +- tests/test_setup_plan_feature_json.py | 2 +- tests/test_setup_plan_no_overwrite.py | 2 +- tests/test_setup_tasks.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_check_prerequisites_paths_only.py b/tests/test_check_prerequisites_paths_only.py index 0675da3113..50317e640f 100644 --- a/tests/test_check_prerequisites_paths_only.py +++ b/tests/test_check_prerequisites_paths_only.py @@ -17,7 +17,7 @@ CHECK_PREREQS_PS = PROJECT_ROOT / "scripts" / "powershell" / "check-prerequisites.ps1" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell") +_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None def _install_bash_scripts(repo: Path) -> None: diff --git a/tests/test_setup_plan_feature_json.py b/tests/test_setup_plan_feature_json.py index 7f44a47923..b1c2332126 100644 --- a/tests/test_setup_plan_feature_json.py +++ b/tests/test_setup_plan_feature_json.py @@ -18,7 +18,7 @@ PLAN_TEMPLATE = PROJECT_ROOT / "templates" / "plan-template.md" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell") +_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None def _install_bash_scripts(repo: Path) -> None: diff --git a/tests/test_setup_plan_no_overwrite.py b/tests/test_setup_plan_no_overwrite.py index 23c4a32335..cc1a741a78 100644 --- a/tests/test_setup_plan_no_overwrite.py +++ b/tests/test_setup_plan_no_overwrite.py @@ -18,7 +18,7 @@ PLAN_TEMPLATE = PROJECT_ROOT / "templates" / "plan-template.md" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell") +_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None def _install_bash_scripts(repo: Path) -> None: diff --git a/tests/test_setup_tasks.py b/tests/test_setup_tasks.py index 233f342664..5b35160a75 100644 --- a/tests/test_setup_tasks.py +++ b/tests/test_setup_tasks.py @@ -20,7 +20,7 @@ TASKS_TEMPLATE = PROJECT_ROOT / "templates" / "tasks-template.md" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = shutil.which("powershell.exe") or shutil.which("powershell") +_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None # --------------------------------------------------------------------------- From 1a80f15b3402fc1a6e5423a4d0eef4a6746a6556 Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 16 Jun 2026 16:17:10 +0100 Subject: [PATCH 2/2] fix(tests): applies copilot feedback, with rename --- tests/test_check_prerequisites_paths_only.py | 14 ++++---- tests/test_setup_plan_feature_json.py | 10 +++--- tests/test_setup_plan_no_overwrite.py | 10 +++--- tests/test_setup_tasks.py | 36 ++++++++++---------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/test_check_prerequisites_paths_only.py b/tests/test_check_prerequisites_paths_only.py index 50317e640f..03e2fc6e8b 100644 --- a/tests/test_check_prerequisites_paths_only.py +++ b/tests/test_check_prerequisites_paths_only.py @@ -17,7 +17,7 @@ CHECK_PREREQS_PS = PROJECT_ROOT / "scripts" / "powershell" / "check-prerequisites.ps1" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None +_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None def _install_bash_scripts(repo: Path) -> None: @@ -160,14 +160,14 @@ def test_normal_mode_still_validates_branch(prereq_repo: Path) -> None: # ── PowerShell tests ────────────────────────────────────────────────────── -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_ps_paths_only_succeeds_on_non_spec_branch(prereq_repo: Path) -> None: """-PathsOnly must return paths when feature.json pins the feature dir.""" feat = prereq_repo / "specs" / "001-my-feature" feat.mkdir(parents=True, exist_ok=True) _write_feature_json(prereq_repo) script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json", "-PathsOnly"], cwd=prereq_repo, @@ -183,7 +183,7 @@ def test_ps_paths_only_succeeds_on_non_spec_branch(prereq_repo: Path) -> None: assert "FEATURE_DIR" in data -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None: """-PathsOnly must also work when feature.json and SPECIFY_FEATURE agree.""" subprocess.run( @@ -195,7 +195,7 @@ def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None: feat.mkdir(parents=True, exist_ok=True) _write_feature_json(prereq_repo) script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL env = _clean_env() env["SPECIFY_FEATURE"] = "001-my-feature" result = subprocess.run( @@ -211,11 +211,11 @@ def test_ps_paths_only_succeeds_on_spec_branch(prereq_repo: Path) -> None: assert "FEATURE_DIR" in data -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_ps_normal_mode_still_validates_branch(prereq_repo: Path) -> None: """Without -PathsOnly, feature directory validation must still fail on main.""" script = prereq_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], cwd=prereq_repo, diff --git a/tests/test_setup_plan_feature_json.py b/tests/test_setup_plan_feature_json.py index b1c2332126..09710e5d18 100644 --- a/tests/test_setup_plan_feature_json.py +++ b/tests/test_setup_plan_feature_json.py @@ -18,7 +18,7 @@ PLAN_TEMPLATE = PROJECT_ROOT / "templates" / "plan-template.md" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None +_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None def _install_bash_scripts(repo: Path) -> None: @@ -153,7 +153,7 @@ def test_setup_plan_numbered_branch_works_with_feature_json( assert (feat / "plan.md").is_file() -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_plan_ps_passes_custom_branch_when_feature_json_valid(plan_repo: Path) -> None: subprocess.run( ["git", "checkout", "-q", "-b", "feature/my-feature-branch"], @@ -165,7 +165,7 @@ def test_setup_plan_ps_passes_custom_branch_when_feature_json_valid(plan_repo: P (feat / "spec.md").write_text("# spec\n", encoding="utf-8") _write_feature_json(plan_repo, "specs/001-tiny-notes-app") script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script)], cwd=plan_repo, @@ -178,12 +178,12 @@ def test_setup_plan_ps_passes_custom_branch_when_feature_json_valid(plan_repo: P assert (feat / "plan.md").is_file() -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_plan_ps_errors_without_feature_context( plan_repo: Path, ) -> None: script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script)], cwd=plan_repo, diff --git a/tests/test_setup_plan_no_overwrite.py b/tests/test_setup_plan_no_overwrite.py index cc1a741a78..c0db317263 100644 --- a/tests/test_setup_plan_no_overwrite.py +++ b/tests/test_setup_plan_no_overwrite.py @@ -18,7 +18,7 @@ PLAN_TEMPLATE = PROJECT_ROOT / "templates" / "plan-template.md" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None +_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None def _install_bash_scripts(repo: Path) -> None: @@ -178,11 +178,11 @@ def test_setup_plan_json_parseable_on_first_run(plan_repo: Path) -> None: # ── PowerShell tests ────────────────────────────────────────────────────── -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_ps_setup_plan_creates_plan_when_missing(plan_repo: Path) -> None: """First run must create plan.md from the template.""" script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], cwd=plan_repo, @@ -199,7 +199,7 @@ def test_ps_setup_plan_creates_plan_when_missing(plan_repo: Path) -> None: assert len(content) > 0 -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_ps_setup_plan_preserves_existing_plan(plan_repo: Path) -> None: """Rerun must not overwrite an existing plan.md.""" feat = plan_repo / "specs" / "001-my-feature" @@ -208,7 +208,7 @@ def test_ps_setup_plan_preserves_existing_plan(plan_repo: Path) -> None: (feat / "plan.md").write_text(existing_content, encoding="utf-8") script = plan_repo / ".specify" / "scripts" / "powershell" / "setup-plan.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], cwd=plan_repo, diff --git a/tests/test_setup_tasks.py b/tests/test_setup_tasks.py index 5b35160a75..0e3fb85f41 100644 --- a/tests/test_setup_tasks.py +++ b/tests/test_setup_tasks.py @@ -20,7 +20,7 @@ TASKS_TEMPLATE = PROJECT_ROOT / "templates" / "tasks-template.md" HAS_PWSH = shutil.which("pwsh") is not None -_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None +_WINDOWS_POWERSHELL = (shutil.which("powershell.exe") or shutil.which("powershell")) if os.name == "nt" else None # --------------------------------------------------------------------------- @@ -118,7 +118,7 @@ def _run_bash_format_command(repo: Path, command_name: str) -> subprocess.Comple def _run_powershell_format_command(repo: Path, command_name: str) -> subprocess.CompletedProcess: script = repo / ".specify" / "scripts" / "powershell" / "common.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL return subprocess.run( [ exe, @@ -606,7 +606,7 @@ def test_setup_tasks_bash_errors_without_feature_context( # POWERSHELL TESTS # =========================================================================== -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None: """ When the core tasks-template.md is present and all prerequisites are met, @@ -615,7 +615,7 @@ def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None: """ _minimal_feature(tasks_repo) script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], @@ -635,7 +635,7 @@ def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None: assert tasks_tmpl.name == "tasks-template.md" -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None: """ When an override exists at .specify/templates/overrides/tasks-template.md, @@ -649,7 +649,7 @@ def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None: override_file.write_text("# override tasks template\n", encoding="utf-8") script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], @@ -671,7 +671,7 @@ def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None: ) -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None: """ When tasks-template.md is absent from all locations, setup-tasks.ps1 must @@ -683,7 +683,7 @@ def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None: core.unlink() script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], @@ -698,7 +698,7 @@ def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None: assert "tasks-template" in result.stderr.lower() or "tasks-template" in result.stdout.lower() -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_powershell_command_hint_normalizes_mixed_separators( tasks_repo: Path, ) -> None: @@ -717,7 +717,7 @@ def test_powershell_command_hint_normalizes_mixed_separators( assert result.stdout.strip() == "/speckit-git-commit" -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_powershell_command_hint_preserves_hyphens_inside_segments( tasks_repo: Path, ) -> None: @@ -729,7 +729,7 @@ def test_powershell_command_hint_preserves_hyphens_inside_segments( assert result.stdout.strip() == "/speckit.jira.sync-status" -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_tasks_ps_uses_invoke_separator_in_plan_hint(tasks_repo: Path) -> None: _write_integration_state(tasks_repo, "claude", "-") feat = tasks_repo / "specs" / "001-my-feature" @@ -738,7 +738,7 @@ def test_setup_tasks_ps_uses_invoke_separator_in_plan_hint(tasks_repo: Path) -> _write_feature_json(tasks_repo) script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], @@ -755,7 +755,7 @@ def test_setup_tasks_ps_uses_invoke_separator_in_plan_hint(tasks_repo: Path) -> assert "/speckit.plan" not in output -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_check_prerequisites_ps_uses_invoke_separator_in_tasks_hint( tasks_repo: Path, ) -> None: @@ -763,7 +763,7 @@ def test_check_prerequisites_ps_uses_invoke_separator_in_tasks_hint( _minimal_feature(tasks_repo) script = tasks_repo / ".specify" / "scripts" / "powershell" / "check-prerequisites.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-RequireTasks"], @@ -780,7 +780,7 @@ def test_check_prerequisites_ps_uses_invoke_separator_in_tasks_hint( assert "/speckit.tasks" not in output -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_tasks_ps_passes_custom_branch_when_feature_json_valid( tasks_repo: Path, ) -> None: @@ -801,7 +801,7 @@ def test_setup_tasks_ps_passes_custom_branch_when_feature_json_valid( _write_feature_json(tasks_repo) script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"], @@ -815,7 +815,7 @@ def test_setup_tasks_ps_passes_custom_branch_when_feature_json_valid( assert result.returncode == 0, result.stderr + result.stdout -@pytest.mark.skipif(not (HAS_PWSH or _POWERSHELL), reason="no PowerShell available") +@pytest.mark.skipif(not (HAS_PWSH or _WINDOWS_POWERSHELL), reason="no PowerShell available") def test_setup_tasks_ps_errors_without_feature_context( tasks_repo: Path, ) -> None: @@ -826,7 +826,7 @@ def test_setup_tasks_ps_errors_without_feature_context( (main_feat / "plan.md").write_text("# plan\n", encoding="utf-8") script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1" - exe = "pwsh" if HAS_PWSH else _POWERSHELL + exe = "pwsh" if HAS_PWSH else _WINDOWS_POWERSHELL result = subprocess.run( [exe, "-NoProfile", "-File", str(script), "-Json"],