from pathlib import Path import pytest from agentci.adapters.development import ( DevelopmentEnvironment, DevelopmentEnvironmentError, ) def environment( tmp_path: Path, scripts: list[str], *, timeout_seconds: int = 5 ) -> DevelopmentEnvironment: scripts_dir = tmp_path / "scripts" scripts_dir.mkdir() return DevelopmentEnvironment( scripts=scripts, scripts_dir=scripts_dir, tools_dir=tmp_path / "tools", timeout_seconds=timeout_seconds, python_version="3.13", dotnet_channel="10.0", ) def script(path: Path, body: str) -> None: path.write_text(f"#!/bin/sh\nset -eu\n{body}\n") path.chmod(0o755) async def test_runs_custom_scripts_in_order_with_sanitized_environment( tmp_path, monkeypatch ) -> None: workspace = tmp_path / "workspace" workspace.mkdir() development = environment(tmp_path, ["first", "second"]) script( development.scripts_dir / "first", "printf 'first:%s:%s\\n' \"$DEV_TOOLS_DIR\" \"${AGENTCI_SECRET-unset}\" >> order", ) script(development.scripts_dir / "second", "printf 'second\\n' >> order") monkeypatch.setenv("AGENTCI_SECRET", "must-not-leak") await development.prepare(workspace) assert (workspace / "order").read_text().splitlines() == [ f"first:{tmp_path / 'tools'}:unset", "second", ] assert (tmp_path / "tools" / "bin").is_dir() async def test_supplied_script_names_use_the_same_directory(tmp_path) -> None: workspace = tmp_path / "workspace" workspace.mkdir() development = environment(tmp_path, ["python"]) script(development.scripts_dir / "python", "printf 'python\\n' > selected") await development.prepare(workspace) assert (workspace / "selected").read_text() == "python\n" async def test_runs_non_executable_shell_script_from_bind_mount(tmp_path) -> None: workspace = tmp_path / "workspace" workspace.mkdir() development = environment(tmp_path, ["mounted"]) mounted = development.scripts_dir / "mounted" mounted.write_text("#!/bin/sh\nprintf 'mounted\\n' > selected\n") mounted.chmod(0o644) await development.prepare(workspace) assert (workspace / "selected").read_text() == "mounted\n" async def test_reports_script_failure_output(tmp_path) -> None: workspace = tmp_path / "workspace" workspace.mkdir() development = environment(tmp_path, ["broken"]) script(development.scripts_dir / "broken", "printf 'failed detail' >&2; exit 7") with pytest.raises(DevelopmentEnvironmentError, match="exited with 7: failed detail"): await development.prepare(workspace) async def test_times_out_install_script(tmp_path) -> None: workspace = tmp_path / "workspace" workspace.mkdir() development = environment(tmp_path, ["slow"], timeout_seconds=1) script(development.scripts_dir / "slow", "exec sleep 10") with pytest.raises(DevelopmentEnvironmentError, match="exceeded 1 seconds"): await development.prepare(workspace)