"""Exercise the Windows bootstrap in disposable projects without installing host tools.""" import os from pathlib import Path import shutil import subprocess import sys import pytest ROOT = Path(__file__).resolve().parents[2] pytestmark = pytest.mark.skipif( sys.platform != "win32" or not shutil.which("uv"), reason="Windows and uv required" ) @pytest.fixture def project(tmp_path): # Import fixtures let the final smoke check run without downloading project dependencies. path = tmp_path / "source with spaces!" path.mkdir() (path / "pyproject.toml").write_text( '[project]\nname="bootstrap-fixture"\nversion="0.1.0"\nrequires-python=">=3.12"\ndependencies=[]\n' ) for name in ("artemis", "uvicorn", "fastapi", "langchain_openai"): (path / f"{name}.py").write_text("") subprocess.run(["uv", "lock", "--offline"], cwd=path, check=True, capture_output=True) return path def run_bootstrap( project, *, missing_uv=False, installer_failure=False, sync_failure=False, mirror_index="" ): script = project / "check.ps1" script.write_text( """ $ErrorActionPreference = 'Continue' $realUv = (Get-Command uv).Source . $env:BOOTSTRAP_SCRIPT . (Join-Path (Split-Path $env:BOOTSTRAP_SCRIPT -Parent) 'download_config.ps1') if ($env:TEST_MIRROR) { $script:ArtemisDownloads = @{ python_index = $env:TEST_MIRROR } } $script:uvInstalled = $env:SIMULATE_MISSING_UV -ne '1' function Test-CommandExists { param($Command); return $script:uvInstalled } function Update-EnvironmentPath {} function Invoke-DownloadFile { param($Uri, $OutFile, $TimeoutSec) if ($Uri -ne 'https://astral.sh/uv/install.ps1') { throw 'Unexpected installer source' } Set-Content -LiteralPath $OutFile -Value '# fixture installer' return $true } function powershell.exe { Write-Host 'INSTALLER_CALLED' if ($env:SIMULATE_INSTALL_FAILURE -eq '1') { $global:LASTEXITCODE = 25; return } $script:uvInstalled = $true $global:LASTEXITCODE = 0 } function uv { if ($env:SIMULATE_SYNC_FAILURE -eq '1' -and $args[0] -eq 'sync') { $global:LASTEXITCODE = 9 return } & $realUv @args } try { $python = Initialize-ArtemisPython -ProjectRoot $PSScriptRoot Write-Host "PYTHON_READY=$python" exit 0 } catch { Write-Host "EXPECTED_ERROR=$_"; exit 1 } """, encoding="ascii", ) env = dict(os.environ) env.update( BOOTSTRAP_SCRIPT=str(ROOT / "scripts/bootstrap_python.ps1"), SIMULATE_MISSING_UV=str(int(missing_uv)), SIMULATE_INSTALL_FAILURE=str(int(installer_failure)), SIMULATE_SYNC_FAILURE=str(int(sync_failure)), UV_OFFLINE="0" if mirror_index else "1", TEST_MIRROR=mirror_index, ) return subprocess.run( ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", str(script)], env=env, capture_output=True, text=True, timeout=60, ) def test_first_setup_and_repeat_reuse_environment(project): first = run_bootstrap(project, missing_uv=True) assert first.returncode == 0, first.stdout + first.stderr assert "INSTALLER_CALLED" in first.stdout assert "Creating .venv" in first.stdout config = project / ".venv/pyvenv.cfg" before = config.stat().st_mtime_ns second = run_bootstrap(project) assert second.returncode == 0, second.stdout + second.stderr assert "INSTALLER_CALLED" not in second.stdout assert "Reusing the existing" in second.stdout assert "dependencies are already ready" in second.stdout assert config.stat().st_mtime_ns == before def test_installer_failure_stops_before_environment_creation(project): result = run_bootstrap(project, missing_uv=True, installer_failure=True) assert result.returncode == 1 assert "uv installation failed" in result.stdout assert not (project / ".venv").exists() def test_dependency_failure_stops_before_launch(project): result = run_bootstrap(project, sync_failure=True) assert result.returncode == 1 assert "Dependency installation failed" in result.stdout assert "PYTHON_READY=" not in result.stdout def test_incomplete_environment_is_preserved(project): (project / ".venv").mkdir() sentinel = project / ".venv/keep.txt" sentinel.write_text("existing data") result = run_bootstrap(project) assert result.returncode == 1 assert "incomplete" in result.stdout assert sentinel.read_text() == "existing data" @pytest.mark.parametrize("exit_code", [0, 7]) def test_batch_preserves_exit_code_and_handles_space_and_exclamation_paths(tmp_path, exit_code): root = tmp_path / "source with spaces!" (root / "scripts").mkdir(parents=True) shutil.copy2(ROOT / "start.bat", root / "start.bat") (root / "scripts/start.ps1").write_text( "param([switch]$SetupOnly)\n" "if (-not $SetupOnly) { exit 19 }\n" "if ((Get-Location).Path -ne (Split-Path $PSScriptRoot -Parent)) { exit 20 }\n" f"exit {exit_code}\n", encoding="ascii", ) result = subprocess.run( ["cmd.exe", "/d", "/c", "call", str(root / "start.bat"), "-SetupOnly"], cwd=tmp_path, env={**os.environ, "ARTEMIS_NO_PAUSE": "1"}, capture_output=True, timeout=30, ) assert result.returncode == exit_code, result.stdout + result.stderr