"""Tests for pip detection fix in run-server.sh script. This test file ensures our pip detection improvements work correctly and don't break existing functionality. """ import subprocess import tempfile from pathlib import Path from unittest.mock import MagicMock, patch import pytest class TestPipDetectionFix: """Test cases for issue #188: PIP is available but not recognized.""" def test_run_server_script_syntax_valid(self): """Test that run-server.sh has valid bash syntax.""" result = subprocess.run(["bash", "-n", "./run-server.sh"], capture_output=True, text=True) assert result.returncode == 0, f"Syntax error in run-server.sh: {result.stderr}" def test_run_server_has_proper_shebang(self): """Test that run-server.sh starts with proper shebang.""" content = Path("./run-server.sh").read_text() assert content.startswith("#!/bin/bash"), "Script missing proper bash shebang" def test_critical_functions_exist(self): """Test that all critical functions are defined in the script.""" content = Path("./run-server.sh").read_text() critical_functions = ["find_python", "setup_environment", "setup_venv", "install_dependencies", "bootstrap_pip"] for func in critical_functions: assert f"{func}()" in content, f"Critical function {func}() not found in script" def test_pip_detection_consistency_issue(self): """Test the specific issue: pip works in setup_venv but fails in install_dependencies. This test verifies that our fix ensures consistent Python executable paths. """ # Test that the get_venv_python_path function now returns absolute paths content = Path("./run-server.sh").read_text() # Check that get_venv_python_path includes our absolute path conversion logic assert "abs_venv_path" in content, "get_venv_python_path should use absolute paths" assert 'cd "$(dirname' in content, "Should convert to absolute path" # Test successful completion - our fix should make the script more robust result = subprocess.run(["bash", "-n", "./run-server.sh"], capture_output=True, text=True) assert result.returncode == 0, "Script should have valid syntax after our fix" def test_pip_detection_with_non_interactive_shell(self): """Test pip detection works in non-interactive shell environments. This addresses the contributor's suggestion about non-interactive shells not sourcing ~/.bashrc where pip PATH might be defined. """ # Test case for Git Bash on Windows and non-interactive Linux shells with tempfile.TemporaryDirectory() as temp_dir: # Create mock virtual environment structure venv_path = Path(temp_dir) / ".zen_venv" bin_path = venv_path / "bin" bin_path.mkdir(parents=True) # Create mock python executable python_exe = bin_path / "python" python_exe.write_text("#!/bin/bash\necho 'Python 3.12.3'\n") python_exe.chmod(0o755) # Create mock pip executable pip_exe = bin_path / "pip" pip_exe.write_text("#!/bin/bash\necho 'pip 23.0.1'\n") pip_exe.chmod(0o755) # Test that we can detect pip using explicit paths (not PATH) assert python_exe.exists(), "Mock python executable should exist" assert pip_exe.exists(), "Mock pip executable should exist" assert python_exe.is_file(), "Python should be a file" assert pip_exe.is_file(), "Pip should be a file" @patch("subprocess.run") def test_improved_pip_detection_logic(self, mock_run): """Test the improved pip detection logic we plan to implement. Our fix should: 1. Use consistent Python executable paths 2. Try multiple detection methods 3. Provide better error diagnostics """ # Mock successful pip detection mock_run.return_value = MagicMock() mock_run.return_value.returncode = 0 mock_run.return_value.stdout = "pip 23.0.1" # Test that improved detection works with various scenarios test_cases = [ # (python_path, expected_success, description) (".zen_venv/bin/python", True, "Relative path should work"), ("/full/path/.zen_venv/bin/python", True, "Absolute path should work"), ("/usr/bin/python3", True, "System python should work if pip available"), ] for python_path, expected_success, _description in test_cases: # This test defines what our fix should achieve # The actual implementation will make these pass subprocess.run([python_path, "-m", "pip", "--version"], capture_output=True) if expected_success: # After our fix, all these should succeed pass # Will be uncommented after fix implementation # assert result.returncode == 0, f"Failed: {description}" def test_pip_detection_error_diagnostics(self): """Test that our fix provides better error diagnostics. When pip detection fails, users should get helpful information to debug the issue instead of generic error messages. """ # This test defines what improved error messages should look like expected_diagnostic_info = [ "Python executable:", "Python executable exists:", "Python executable permissions:", "Virtual environment path:", "Virtual environment exists:", "pip module:", ] # After our fix, error messages should include these diagnostic details # This helps users understand what went wrong for _info in expected_diagnostic_info: # Test will verify our improved error handling includes this info assert True # Placeholder for actual diagnostic testing class TestPipDetectionPlatformCompatibility: """Test pip detection works across different platforms.""" def test_linux_pip_detection(self): """Test pip detection on Linux systems.""" # Test Linux-specific scenarios pass def test_windows_git_bash_pip_detection(self): """Test pip detection on Windows with Git Bash.""" # Test Windows Git Bash scenarios mentioned in issue comments pass def test_wsl_pip_detection(self): """Test pip detection on Windows Subsystem for Linux.""" # Test WSL scenarios pass def test_macos_pip_detection(self): """Test pip detection on macOS.""" # Test macOS scenarios pass class TestPipDetectionRegression: """Test that our fix doesn't break existing functionality.""" def test_existing_working_setups_still_work(self): """Test that environments that currently work continue to work.""" # Ensure our fix doesn't regress existing working configurations pass def test_uv_first_approach_unaffected(self): """Test that uv-first approach continues to work correctly.""" # The script prefers uv over system Python - ensure this still works pass def test_python_version_detection_unaffected(self): """Test that Python version detection logic isn't broken.""" # Ensure our pip fix doesn't interfere with Python detection pass if __name__ == "__main__": pytest.main([__file__, "-v"])