fix: improve the Python path detection in mock tests and ensures logger initialization order is correct.

Fix run-server.ps1 to handle PowerShell script creation correctly and ensure pip is installed in the uv environment.
This commit is contained in:
OhMyApps
2025-06-27 23:58:13 +02:00
parent c2c8d3de1e
commit 180a350f6d
3 changed files with 328 additions and 66 deletions

View File

@@ -33,6 +33,14 @@ FIXED ISSUES:
- Simulator couldn't find Windows Python executable - Simulator couldn't find Windows Python executable
- Solution: Added Windows-specific path detection - Solution: Added Windows-specific path detection
7. BASE TEST CLASSES LOGGER BUG:
- AttributeError: logger used before initialization in test classes
- Solution: Initialize logger before calling _get_python_path() in BaseSimulatorTest
8. BASE TEST PYTHON PATH DETECTION ON WINDOWS:
- Test classes couldn't find Windows Python executable
- Solution: Added Windows-specific path detection to BaseSimulatorTest
MODIFIED FILES: MODIFIED FILES:
- utils/file_utils.py : Home patterns + Unix path validation - utils/file_utils.py : Home patterns + Unix path validation
- tests/test_file_protection.py : Cross-platform assertions - tests/test_file_protection.py : Cross-platform assertions
@@ -40,6 +48,7 @@ MODIFIED FILES:
- run_integration_tests.sh : Windows venv detection - run_integration_tests.sh : Windows venv detection
- code_quality_checks.sh : Windows venv and tools detection - code_quality_checks.sh : Windows venv and tools detection
- communication_simulator_test.py : Logger initialization order + Windows paths - communication_simulator_test.py : Logger initialization order + Windows paths
- simulator_tests/base_test.py : Logger initialization order + Windows paths
Usage: Usage:
python patch_complet_crossplatform.py [--dry-run] [--backup] [--validate-only] python patch_complet_crossplatform.py [--dry-run] [--backup] [--validate-only]
@@ -73,6 +82,7 @@ class CrossPlatformPatcher:
"run_integration_tests_sh": self.workspace_root / "run_integration_tests.sh", "run_integration_tests_sh": self.workspace_root / "run_integration_tests.sh",
"code_quality_checks_sh": self.workspace_root / "code_quality_checks.sh", "code_quality_checks_sh": self.workspace_root / "code_quality_checks.sh",
"communication_simulator": self.workspace_root / "communication_simulator_test.py", "communication_simulator": self.workspace_root / "communication_simulator_test.py",
"base_test": self.workspace_root / "simulator_tests" / "base_test.py",
} }
for _, path in files.items(): for _, path in files.items():
@@ -509,7 +519,7 @@ fi"""
# Fallback to system python if venv doesn't exist # Fallback to system python if venv doesn't exist
self.logger.warning("Virtual environment not found, using system python") self.logger.warning("Virtual environment not found, using system python")
return "python\"""" return "python"""
new_python_path = """ def _get_python_path(self) -> str: new_python_path = """ def _get_python_path(self) -> str:
\"\"\"Get the Python path for the virtual environment\"\"\" \"\"\"Get the Python path for the virtual environment\"\"\"
@@ -536,7 +546,89 @@ fi"""
# Fallback to system python if venv doesn't exist # Fallback to system python if venv doesn't exist
self.logger.warning("Virtual environment not found, using system python") self.logger.warning("Virtual environment not found, using system python")
return "python\"""" return "python"""
if old_python_path in content:
content = content.replace(old_python_path, new_python_path)
return content, True
return content, False
def patch_base_test_logger_init(self, content: str) -> tuple[str, bool]:
"""Patch 11: Fix logger initialization order in BaseSimulatorTest."""
# Check if already patched
if "# Configure logging first" in content and "# Now get python path" in content:
return content, False
# Fix the initialization order in BaseSimulatorTest
old_init_order = """ def __init__(self, verbose: bool = False):
self.verbose = verbose
self.test_files = {}
self.test_dir = None
self.python_path = self._get_python_path()
# Configure logging
log_level = logging.DEBUG if verbose else logging.INFO
logging.basicConfig(level=log_level, format="%(asctime)s - %(levelname)s - %(message)s")
self.logger = logging.getLogger(self.__class__.__name__)"""
new_init_order = """ def __init__(self, verbose: bool = False):
self.verbose = verbose
self.test_files = {}
self.test_dir = None
# Configure logging first
log_level = logging.DEBUG if verbose else logging.INFO
logging.basicConfig(level=log_level, format="%(asctime)s - %(levelname)s - %(message)s")
self.logger = logging.getLogger(self.__class__.__name__)
# Now get python path (after logger is configured)
self.python_path = self._get_python_path()"""
if old_init_order in content:
content = content.replace(old_init_order, new_init_order)
return content, True
return content, False
def patch_base_test_python_path(self, content: str) -> tuple[str, bool]:
"""Patch 12: Add Windows Python path detection to BaseSimulatorTest."""
# Check if already patched
if "import platform" in content and 'platform.system() == "Windows"' in content:
return content, False
# Fix the _get_python_path method in BaseSimulatorTest
old_python_path = """ def _get_python_path(self) -> str:
\"\"\"Get the Python path for the virtual environment\"\"\"
current_dir = os.getcwd()
venv_python = os.path.join(current_dir, ".zen_venv", "bin", "python")
if os.path.exists(venv_python):
return venv_python
# Fallback to system python if venv doesn't exist
self.logger.warning("Virtual environment not found, using system python")
return "python"""
new_python_path = """ def _get_python_path(self) -> str:
\"\"\"Get the Python path for the virtual environment\"\"\"
import platform
current_dir = os.getcwd()
# Check for different venv structures
if platform.system() == "Windows":
# Windows paths
zen_venv_python = os.path.join(current_dir, ".zen_venv", "Scripts", "python.exe")
else:
# Unix/Linux/macOS paths
zen_venv_python = os.path.join(current_dir, ".zen_venv", "bin", "python")
if os.path.exists(zen_venv_python):
return zen_venv_python
# Fallback to system python if venv doesn't exist
self.logger.warning("Virtual environment not found, using system python")
return "python"""
if old_python_path in content: if old_python_path in content:
content = content.replace(old_python_path, new_python_path) content = content.replace(old_python_path, new_python_path)
@@ -674,68 +766,28 @@ fi"""
else: else:
print(" communication_simulator_test.py already patched") print(" communication_simulator_test.py already patched")
# Patch 6: run_integration_tests.sh # Patch 11 & 12: simulator_tests/base_test.py
print("\n🔧 Patching run_integration_tests.sh...") print("\n🔧 Patching simulator_tests/base_test.py...")
run_integration_content = self.read_file(files["run_integration_tests_sh"]) base_test_content = self.read_file(files["base_test"])
run_integration_content, modified6 = self.patch_shell_venv_detection(run_integration_content) base_test_content, modified11 = self.patch_base_test_logger_init(base_test_content)
base_test_content, modified12 = self.patch_base_test_python_path(base_test_content)
if modified6: if modified11 or modified12:
if create_backups: if create_backups:
backup = self.create_backup(files["run_integration_tests_sh"]) backup = self.create_backup(files["base_test"])
print(f" ✅ Backup created: {backup}") print(f" ✅ Backup created: {backup}")
self.write_file(files["run_integration_tests_sh"], run_integration_content) self.write_file(files["base_test"], base_test_content)
print(" ✅ Windows venv detection added")
self.patches_applied.append("Windows venv detection (run_integration_tests.sh)")
else:
print(" run_integration_tests.sh already patched")
# Patch 7 & 8: code_quality_checks.sh if modified11:
print("\n🔧 Patching code_quality_checks.sh...")
code_quality_content = self.read_file(files["code_quality_checks_sh"])
code_quality_content, modified7 = self.patch_shell_python_detection(code_quality_content)
code_quality_content, modified8 = self.patch_shell_tool_paths(code_quality_content)
if modified7 or modified8:
if create_backups:
backup = self.create_backup(files["code_quality_checks_sh"])
print(f" ✅ Backup created: {backup}")
self.write_file(files["code_quality_checks_sh"], code_quality_content)
if modified7:
print(" ✅ Windows Python detection added")
self.patches_applied.append("Windows Python detection (code_quality_checks.sh)")
if modified8:
print(" ✅ Windows tool paths added")
self.patches_applied.append("Windows tool paths (code_quality_checks.sh)")
else:
print(" code_quality_checks.sh already patched")
# Patch 9: communication_simulator_test.py
print("\n🔧 Patching communication_simulator_test.py...")
simulator_content = self.read_file(files["communication_simulator"])
simulator_content, modified9 = self.patch_simulator_logger_init(simulator_content)
simulator_content, modified10 = self.patch_simulator_python_path(simulator_content)
if modified9 or modified10:
if create_backups:
backup = self.create_backup(files["communication_simulator"])
print(f" ✅ Backup created: {backup}")
self.write_file(files["communication_simulator"], simulator_content)
if modified9:
print(" ✅ Logger initialization order fixed") print(" ✅ Logger initialization order fixed")
self.patches_applied.append("Logger initialization order") self.patches_applied.append("Logger initialization (base_test.py)")
if modified10: if modified12:
print(" ✅ Windows Python path detection added") print(" ✅ Windows Python path detection added")
self.patches_applied.append("Windows Python path detection") self.patches_applied.append("Windows Python paths (base_test.py)")
else: else:
print(" communication_simulator_test.py already patched") print(" simulator_tests/base_test.py already patched")
return all_success return all_success
@@ -791,6 +843,15 @@ fi"""
if "import platform" not in simulator_content: if "import platform" not in simulator_content:
errors.append("Windows Python path detection missing in communication_simulator_test.py") errors.append("Windows Python path detection missing in communication_simulator_test.py")
# Validate simulator_tests/base_test.py
base_test_content = self.read_file(files["base_test"])
if "# Configure logging first" not in base_test_content or "# Now get python path" not in base_test_content:
errors.append("Logger initialization order missing in base_test.py")
if "import platform" not in base_test_content or 'platform.system() == "Windows"' not in base_test_content:
errors.append("Windows Python path detection missing in base_test.py")
return errors return errors
def show_diff_summary(self, files: dict[str, Path]) -> None: def show_diff_summary(self, files: dict[str, Path]) -> None:

View File

@@ -2,8 +2,25 @@
""" """
Validation script for all cross-platform fixes. Validation script for all cross-platform fixes.
This script runs a series of tests to validate that all applied fixes This script runs a comprehensive series of tests to validate that all applied fixes
work correctly on Windows. work correctly on Windows, including:
1. Home directory pattern detection (Windows, macOS, Linux)
2. Unix path validation on Windows
3. Safe files functionality with temporary files
4. Cross-platform file discovery with Path.parts
5. Communication simulator logger and Python path fixes
6. BaseSimulatorTest logger and Python path fixes
7. Shell scripts Windows virtual environment support
Tests cover all modified files:
- utils/file_utils.py
- tests/test_file_protection.py
- tests/test_utils.py
- communication_simulator_test.py
- simulator_tests/base_test.py
- run_integration_tests.sh
- code_quality_checks.sh
""" """
import sys import sys
@@ -11,7 +28,7 @@ import tempfile
from pathlib import Path from pathlib import Path
from unittest.mock import patch from unittest.mock import patch
# Add parent directory to Python path to import from workspace root # Add parent directory to path to import project modules
sys.path.insert(0, str(Path(__file__).parent.parent)) sys.path.insert(0, str(Path(__file__).parent.parent))
# Import functions to test # Import functions to test
@@ -124,8 +141,7 @@ def test_safe_files_functionality():
success1 = all([has_begin, has_content, has_end, has_tokens]) success1 = all([has_begin, has_content, has_end, has_tokens])
# Test nonexistent Unix path # Test nonexistent Unix path (should return FILE NOT FOUND, not path error)
# (should return FILE NOT FOUND, not path error)
content, tokens = read_file_content("/etc/nonexistent") content, tokens = read_file_content("/etc/nonexistent")
not_found = "--- FILE NOT FOUND:" in content not_found = "--- FILE NOT FOUND:" in content
no_path_error = "Relative paths are not supported" not in content no_path_error = "Relative paths are not supported" not in content
@@ -138,8 +154,7 @@ def test_safe_files_functionality():
success2 = all([not_found, no_path_error, has_tokens2]) success2 = all([not_found, no_path_error, has_tokens2])
success = success1 and success2 success = success1 and success2
status = "passed" if success else "failed" print(f"\nResult: Safe files tests {'passed' if success else 'failed'}")
print(f"\nResult: Safe files tests {status}")
finally: finally:
# Clean up # Clean up
@@ -195,6 +210,172 @@ def test_cross_platform_file_discovery():
return success return success
def test_communication_simulator_fixes():
"""Test 5: Communication simulator fixes"""
print("\n🧪 Test 5: Communication simulator fixes")
print("-" * 60)
try:
# Import and test CommunicationSimulator
from communication_simulator_test import CommunicationSimulator
# Test that we can create an instance without logger errors
simulator = CommunicationSimulator(verbose=False, keep_logs=True)
# Check that logger is properly initialized
has_logger = hasattr(simulator, "logger") and simulator.logger is not None
print(f" ✅ Logger initialized: {has_logger}")
# Check that python_path is set
has_python_path = hasattr(simulator, "python_path") and simulator.python_path is not None
print(f" ✅ Python path set: {has_python_path}")
# Check that the path detection logic includes Windows
import os
import platform
if platform.system() == "Windows":
# Test Windows path detection
current_dir = os.getcwd()
expected_paths = [
os.path.join(current_dir, ".zen_venv", "Scripts", "python.exe"),
os.path.join(current_dir, "venv", "Scripts", "python.exe"),
]
# Check if the method would detect Windows paths
windows_detection = any("Scripts" in path for path in expected_paths)
print(f" ✅ Windows path detection: {windows_detection}")
else:
windows_detection = True # Pass on non-Windows systems
print(" ✅ Windows path detection: N/A (not Windows)")
success = all([has_logger, has_python_path, windows_detection])
print(f"\nResult: Communication simulator {'passed' if success else 'failed'}")
return success
except Exception as e:
print(f" ❌ Error testing CommunicationSimulator: {e}")
print("\nResult: Communication simulator failed")
return False
def test_base_simulator_test_fixes():
"""Test 6: BaseSimulatorTest fixes."""
print("\n🧪 Test 6: BaseSimulatorTest fixes")
print("-" * 60)
try:
# Import and test BaseSimulatorTest
from simulator_tests.base_test import BaseSimulatorTest
# Test that we can create an instance without logger errors
base_test = BaseSimulatorTest(verbose=False)
# Check that logger is properly initialized
has_logger = hasattr(base_test, "logger") and base_test.logger is not None
print(f" ✅ Logger initialized: {has_logger}")
# Check that python_path is set
has_python_path = hasattr(base_test, "python_path") and base_test.python_path is not None
print(f" ✅ Python path set: {has_python_path}")
# Check that the path detection logic includes Windows
import os
import platform
if platform.system() == "Windows":
# Test Windows path detection
current_dir = os.getcwd()
expected_path = os.path.join(current_dir, ".zen_venv", "Scripts", "python.exe")
# Check if the method would detect Windows paths
windows_detection = "Scripts" in expected_path
print(f" ✅ Windows path detection: {windows_detection}")
else:
windows_detection = True # Pass on non-Windows systems
print(" ✅ Windows path detection: N/A (not Windows)")
# Test that we can call methods that previously failed
try:
# Test accessing properties without calling abstract methods
# Just check that logger-related functionality works
logger_accessible = hasattr(base_test, "logger") and callable(getattr(base_test, "logger", None))
method_callable = True
print(f" ✅ Methods callable: {method_callable}")
print(f" ✅ Logger accessible: {logger_accessible}")
except AttributeError as e:
if "logger" in str(e):
method_callable = False
print(f" ❌ Logger error still present: {e}")
else:
method_callable = True # Different error, not logger-related
print(f" ✅ No logger errors (different error): {str(e)[:50]}...")
success = all([has_logger, has_python_path, windows_detection, method_callable])
print(f"\nResult: BaseSimulatorTest {'passed' if success else 'failed'}")
return success
except Exception as e:
print(f" ❌ Error testing BaseSimulatorTest: {e}")
print("\nResult: BaseSimulatorTest failed")
return False
def test_shell_scripts_windows_support():
"""Test 7: Shell scripts Windows support."""
print("\n🧪 Test 7: Shell scripts Windows support")
print("-" * 60)
try:
# Check run_integration_tests.sh
try:
with open("run_integration_tests.sh", encoding="utf-8") as f:
run_script_content = f.read()
has_windows_venv = 'elif [[ -f ".zen_venv/Scripts/activate" ]]; then' in run_script_content
has_windows_msg = "Using virtual environment (Windows)" in run_script_content
print(f" ✅ run_integration_tests.sh Windows venv: {has_windows_venv}")
print(f" ✅ run_integration_tests.sh Windows message: {has_windows_msg}")
run_script_ok = has_windows_venv and has_windows_msg
except FileNotFoundError:
print(" ⚠️ run_integration_tests.sh not found")
run_script_ok = True # Skip if file doesn't exist
# Check code_quality_checks.sh
try:
with open("code_quality_checks.sh", encoding="utf-8") as f:
quality_script_content = f.read()
has_windows_python = 'elif [[ -f ".zen_venv/Scripts/python.exe" ]]; then' in quality_script_content
has_windows_tools = 'elif [[ -f ".zen_venv/Scripts/ruff.exe" ]]; then' in quality_script_content
has_windows_msg = "Using venv (Windows)" in quality_script_content
print(f" ✅ code_quality_checks.sh Windows Python: {has_windows_python}")
print(f" ✅ code_quality_checks.sh Windows tools: {has_windows_tools}")
print(f" ✅ code_quality_checks.sh Windows message: {has_windows_msg}")
quality_script_ok = has_windows_python and has_windows_tools and has_windows_msg
except FileNotFoundError:
print(" ⚠️ code_quality_checks.sh not found")
quality_script_ok = True # Skip if file doesn't exist
success = run_script_ok and quality_script_ok
print(f"\nResult: Shell scripts {'passed' if success else 'failed'}")
return success
except Exception as e:
print(f" ❌ Error testing shell scripts: {e}")
print("\nResult: Shell scripts failed")
return False
def main(): def main():
"""Main validation function.""" """Main validation function."""
print("🔧 Final validation of cross-platform fixes") print("🔧 Final validation of cross-platform fixes")
@@ -209,6 +390,9 @@ def main():
results.append(("Unix path validation", test_unix_path_validation())) results.append(("Unix path validation", test_unix_path_validation()))
results.append(("Safe files", test_safe_files_functionality())) results.append(("Safe files", test_safe_files_functionality()))
results.append(("Cross-platform discovery", test_cross_platform_file_discovery())) results.append(("Cross-platform discovery", test_cross_platform_file_discovery()))
results.append(("Communication simulator", test_communication_simulator_fixes()))
results.append(("BaseSimulatorTest", test_base_simulator_test_fixes()))
results.append(("Shell scripts Windows support", test_shell_scripts_windows_support()))
# Final summary # Final summary
print("\n" + "=" * 70) print("\n" + "=" * 70)
@@ -217,7 +401,7 @@ def main():
passed_tests = 0 passed_tests = 0
for test_name, success in results: for test_name, success in results:
status = "PASSED" if success else "FAILED" status = "PASSED" if success else "FAILED"
print(f"{status:<10} {test_name}") print(f"{status:<10} {test_name}")
if success: if success:
passed_tests += 1 passed_tests += 1

View File

@@ -365,7 +365,14 @@ function Initialize-Environment {
Write-Info "Creating virtual environment with uv..." Write-Info "Creating virtual environment with uv..."
uv venv $VENV_PATH --python 3.12 uv venv $VENV_PATH --python 3.12
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
Write-Success "Environment created with uv" # Install pip in the uv environment for compatibility
Write-Info "Installing pip in uv environment..."
uv pip install --python "$VENV_PATH\Scripts\python.exe" pip
if ($LASTEXITCODE -eq 0) {
Write-Success "Environment created with uv (pip installed)"
} else {
Write-Success "Environment created with uv"
}
return "$VENV_PATH\Scripts\python.exe" return "$VENV_PATH\Scripts\python.exe"
} }
} catch { } catch {
@@ -546,8 +553,18 @@ function Install-Dependencies {
if (Test-Uv) { if (Test-Uv) {
Write-Info "Installing dependencies with uv..." Write-Info "Installing dependencies with uv..."
try { try {
uv pip install -r requirements.txt # Install in the virtual environment
uv pip install --python "$VENV_PATH\Scripts\python.exe" -r requirements.txt
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
# Also install dev dependencies if available
if (Test-Path "requirements-dev.txt") {
uv pip install --python "$VENV_PATH\Scripts\python.exe" -r requirements-dev.txt
if ($LASTEXITCODE -eq 0) {
Write-Success "Development dependencies installed with uv"
} else {
Write-Warning "Failed to install dev dependencies with uv, continuing..."
}
}
Write-Success "Dependencies installed with uv" Write-Success "Dependencies installed with uv"
return return
} }
@@ -804,7 +821,7 @@ if exist ".zen_venv\Scripts\python.exe" (
) else ( ) else (
python server.py %* python server.py %*
) )
"@ | Out-File -FilePath $zenWrapper -Encoding ASCII "@ | Out-File -FilePath $zenWrapper -Encoding UTF8
Write-Success "Created zen-mcp-server.cmd wrapper script" Write-Success "Created zen-mcp-server.cmd wrapper script"
} }