diff --git a/tests/integration/test_auto_install_e2e.py b/tests/integration/test_auto_install_e2e.py index 6f32c55bb..8a014d5ec 100644 --- a/tests/integration/test_auto_install_e2e.py +++ b/tests/integration/test_auto_install_e2e.py @@ -119,9 +119,9 @@ def test_auto_install_virtual_prompt_first_run(self, temp_e2e_home): # Once we see "Package installed and ready to run", execution is about to start # Terminate to avoid waiting for full prompt execution - if "✨ Package installed and ready to run" in line: + if "Package installed and ready to run" in line: execution_started = True - print("\n⚡ Test validated - terminating to save time") + print("\n Test validated - terminating to save time") process.terminate() break @@ -136,11 +136,11 @@ def test_auto_install_virtual_prompt_first_run(self, temp_e2e_home): output = ''.join(output_lines) # Check output for auto-install messages - assert "Auto-installing virtual package" in output or "📦" in output, \ + assert "Auto-installing virtual package" in output or "[+]" in output, \ "Should show auto-install message" - assert "Downloading from" in output or "📥" in output, \ + assert "Downloading from" in output or "[>]" in output, \ "Should show download message" - assert execution_started, "Should have started execution (✨ Package installed and ready to run)" + assert execution_started, "Should have started execution (Package installed and ready to run)" # Verify package was installed package_path = apm_modules / "github" / "awesome-copilot" / "skills" / "architecture-blueprint-generator" @@ -150,7 +150,7 @@ def test_auto_install_virtual_prompt_first_run(self, temp_e2e_home): assert (package_path / "SKILL.md").exists() or (package_path / "apm.yml").exists(), \ "Virtual package should have SKILL.md or apm.yml" - print(f"✅ Auto-install successful: {package_path}") + print(f"[+] Auto-install successful: {package_path}") def test_auto_install_uses_cache_on_second_run(self, temp_e2e_home): """Test that second run uses cached package (no re-download). @@ -182,7 +182,7 @@ def test_auto_install_uses_cache_on_second_run(self, temp_e2e_home): for line in iter(process.stdout.readline, ''): if not line: break - if "✨ Package installed and ready to run" in line: + if "Package installed and ready to run" in line: process.terminate() break process.wait(timeout=5) @@ -215,7 +215,7 @@ def test_auto_install_uses_cache_on_second_run(self, temp_e2e_home): break output_lines.append(line) # Terminate once we see execution starting (no need for full run) - if "Executing" in line or "✨" in line: + if "Executing" in line or "Package installed and ready to run" in line: process.terminate() break process.wait(timeout=5) @@ -227,10 +227,10 @@ def test_auto_install_uses_cache_on_second_run(self, temp_e2e_home): # Check output - should NOT show install/download messages assert "Auto-installing" not in output, "Should not auto-install on second run" - assert "Auto-discovered" in output or "ℹ" in output, \ + assert "Auto-discovered" in output or "[i]" in output, \ "Should show auto-discovery message (using cached package)" - print("✅ Second run used cached package (no re-download)") + print("[+] Second run used cached package (no re-download)") def test_simple_name_works_after_install(self, temp_e2e_home): """Test that simple name works after package is installed. @@ -262,7 +262,7 @@ def test_simple_name_works_after_install(self, temp_e2e_home): for line in iter(process.stdout.readline, ''): if not line: break - if "✨ Package installed and ready to run" in line: + if "Package installed and ready to run" in line: process.terminate() break process.wait(timeout=5) @@ -302,10 +302,10 @@ def test_simple_name_works_after_install(self, temp_e2e_home): output = ''.join(output_lines) # Check output - should discover the installed prompt - assert "Auto-discovered" in output or "ℹ" in output, \ + assert "Auto-discovered" in output or "[i]" in output, \ "Should auto-discover prompt from installed package" - print("✅ Simple name works after installation") + print("[+] Simple name works after installation") def test_auto_install_with_qualified_path(self, temp_e2e_home): """Test auto-install works with qualified path format. @@ -337,7 +337,7 @@ def test_auto_install_with_qualified_path(self, temp_e2e_home): if not line: break # Terminate once installation completes - if "✨ Package installed and ready to run" in line: + if "Package installed and ready to run" in line: process.terminate() break process.wait(timeout=5) @@ -353,7 +353,7 @@ def test_auto_install_with_qualified_path(self, temp_e2e_home): skill_file = package_path / "SKILL.md" assert skill_file.exists(), "SKILL.md should exist" - print("✅ Auto-install works with qualified path") + print("[+] Auto-install works with qualified path") if __name__ == "__main__": diff --git a/tests/unit/test_install_command.py b/tests/unit/test_install_command.py index 6eb1231cd..9f8e21f74 100644 --- a/tests/unit/test_install_command.py +++ b/tests/unit/test_install_command.py @@ -1,5 +1,6 @@ """Tests for the apm install command auto-bootstrap feature.""" +import contextlib import pytest import tempfile import os @@ -31,11 +32,24 @@ def teardown_method(self): repo_root = Path(__file__).parent.parent.parent os.chdir(str(repo_root)) - def test_install_no_apm_yml_no_packages_shows_helpful_error(self): - """Test that install without apm.yml and without packages shows helpful error.""" + @contextlib.contextmanager + def _chdir_tmp(self): + """Context manager: create a temp dir, chdir into it, restore CWD on exit. + + Restoring CWD *before* TemporaryDirectory.__exit__ avoids + PermissionError [WinError 32] on Windows when the process's current + directory is inside the directory being deleted. + """ with tempfile.TemporaryDirectory() as tmp_dir: - os.chdir(tmp_dir) + try: + os.chdir(tmp_dir) + yield Path(tmp_dir) + finally: + os.chdir(self.original_dir) + def test_install_no_apm_yml_no_packages_shows_helpful_error(self): + """Test that install without apm.yml and without packages shows helpful error.""" + with self._chdir_tmp(): result = self.runner.invoke(cli, ["install"]) assert result.exit_code == 1 @@ -51,9 +65,7 @@ def test_install_no_apm_yml_with_packages_creates_minimal_apm_yml( self, mock_install_apm, mock_apm_package, mock_validate, monkeypatch ): """Test that install with packages but no apm.yml creates minimal apm.yml.""" - with tempfile.TemporaryDirectory() as tmp_dir: - os.chdir(tmp_dir) - + with self._chdir_tmp(): # Mock package validation to return True mock_validate.return_value = True @@ -91,9 +103,7 @@ def test_install_no_apm_yml_with_multiple_packages( self, mock_install_apm, mock_apm_package, mock_validate, monkeypatch ): """Test that install with multiple packages creates apm.yml and adds all.""" - with tempfile.TemporaryDirectory() as tmp_dir: - os.chdir(tmp_dir) - + with self._chdir_tmp(): # Mock package validation mock_validate.return_value = True @@ -127,9 +137,7 @@ def test_install_existing_apm_yml_preserves_behavior( self, mock_install_apm, mock_apm_package ): """Test that install with existing apm.yml works as before.""" - with tempfile.TemporaryDirectory() as tmp_dir: - os.chdir(tmp_dir) - + with self._chdir_tmp(): # Create existing apm.yml existing_config = { "name": "test-project", @@ -170,9 +178,9 @@ def test_install_auto_created_apm_yml_has_correct_metadata( self, mock_install_apm, mock_apm_package, mock_validate ): """Test that auto-created apm.yml has correct metadata.""" - with tempfile.TemporaryDirectory() as tmp_dir: + with self._chdir_tmp() as tmp_dir: # Create a directory with a specific name to test project name detection - project_dir = Path(tmp_dir) / "my-awesome-project" + project_dir = tmp_dir / "my-awesome-project" project_dir.mkdir() os.chdir(project_dir) @@ -204,9 +212,7 @@ def test_install_auto_created_apm_yml_has_correct_metadata( @patch("apm_cli.commands.install._validate_package_exists") def test_install_invalid_package_format_with_no_apm_yml(self, mock_validate): """Test that invalid package format fails gracefully even with auto-bootstrap.""" - with tempfile.TemporaryDirectory() as tmp_dir: - os.chdir(tmp_dir) - + with self._chdir_tmp(): # Don't mock validation - let it handle invalid format result = self.runner.invoke(cli, ["install", "invalid-package"]) @@ -222,9 +228,7 @@ def test_install_dry_run_with_no_apm_yml_shows_what_would_be_created( self, mock_install_apm, mock_apm_package, mock_validate ): """Test that dry-run with no apm.yml shows what would be created.""" - with tempfile.TemporaryDirectory() as tmp_dir: - os.chdir(tmp_dir) - + with self._chdir_tmp(): mock_validate.return_value = True mock_pkg_instance = MagicMock()