from __future__ import annotations

import asyncio
import builtins
import importlib
import io
import os
import sys
import tarfile
import types
from collections.abc import Callable
from pathlib import Path, PureWindowsPath
from typing import Any, NoReturn, cast

import pytest
from pydantic import Field, PrivateAttr

from agents.sandbox import Manifest
from agents.sandbox.config import DEFAULT_PYTHON_SANDBOX_IMAGE
from agents.sandbox.entries import (
    File,
    GCSMount,
    InContainerMountStrategy,
    Mount,
    MountpointMountPattern,
    R2Mount,
    S3Mount,
)
from agents.sandbox.entries.mounts.base import InContainerMountAdapter
from agents.sandbox.errors import (
    InvalidManifestPathError,
    MountConfigError,
    WorkspaceArchiveReadError,
)
from agents.sandbox.files import EntryKind
from agents.sandbox.manifest import Environment
from agents.sandbox.materialization import MaterializedFile
from agents.sandbox.session.base_sandbox_session import BaseSandboxSession
from agents.sandbox.session.runtime_helpers import (
    RESOLVE_WORKSPACE_PATH_HELPER,
    WORKSPACE_FINGERPRINT_HELPER,
)
from agents.sandbox.snapshot import LocalSnapshot
from agents.sandbox.types import ExecResult


def _with_aio(fn: Callable[..., object]) -> Callable[..., object]:
    def _sync(*args: object, **kwargs: object) -> object:
        return fn(*args, **kwargs)

    async def _aio(*args: object, **kwargs: object) -> object:
        return fn(*args, **kwargs)

    _sync.aio = _aio  # type: ignore[attr-defined]
    return _sync


def _set_aio_attr(obj: object, name: str, fn: Callable[..., object]) -> None:
    setattr(obj, name, _with_aio(fn))


class _RecordingMount(Mount):
    type: str = "modal_recording_mount"
    mount_strategy: InContainerMountStrategy = Field(
        default_factory=lambda: InContainerMountStrategy(pattern=MountpointMountPattern())
    )
    _events: list[tuple[str, str]] = PrivateAttr(default_factory=list)
    _teardown_error: str | None = PrivateAttr(default=None)

    def bind_events(self, events: list[tuple[str, str]]) -> _RecordingMount:
        self._events = events
        return self

    def bind_teardown_error(self, message: str) -> _RecordingMount:
        self._teardown_error = message
        return self

    def supported_in_container_patterns(
        self,
    ) -> tuple[builtins.type[MountpointMountPattern], ...]:
        return (MountpointMountPattern,)

    def build_docker_volume_driver_config(
        self,
        strategy: object,
    ) -> tuple[str, dict[str, str], bool]:
        _ = strategy
        raise MountConfigError(
            message="docker-volume mounts are not supported for this mount type",
            context={"mount_type": self.type},
        )

    def in_container_adapter(self) -> InContainerMountAdapter:
        mount = self

        class _Adapter(InContainerMountAdapter):
            def validate(self, strategy: InContainerMountStrategy) -> None:
                _ = strategy

            async def activate(
                self,
                strategy: InContainerMountStrategy,
                session: BaseSandboxSession,
                dest: Path,
                base_dir: Path,
            ) -> list[MaterializedFile]:
                _ = (strategy, session, dest, base_dir)
                return []

            async def deactivate(
                self,
                strategy: InContainerMountStrategy,
                session: BaseSandboxSession,
                dest: Path,
                base_dir: Path,
            ) -> None:
                _ = (strategy, session, dest, base_dir)

            async def teardown_for_snapshot(
                self,
                strategy: InContainerMountStrategy,
                session: BaseSandboxSession,
                path: Path,
            ) -> None:
                _ = (strategy, session)
                if mount._teardown_error is not None:
                    raise RuntimeError(mount._teardown_error)
                mount._events.append(("unmount", path.as_posix()))

            async def restore_after_snapshot(
                self,
                strategy: InContainerMountStrategy,
                session: BaseSandboxSession,
                path: Path,
            ) -> None:
                _ = (strategy, session)
                mount._events.append(("mount", path.as_posix()))

        return _Adapter(self)


def _load_modal_module(
    monkeypatch: pytest.MonkeyPatch,
) -> tuple[Any, list[dict[str, object]], list[str]]:
    create_calls: list[dict[str, object]] = []
    registry_tags: list[str] = []

    class _FakeImage:
        object_id = "im-123"
        from_id_calls: list[str] = []

        def __init__(self, object_id: str | None = None) -> None:
            if object_id is not None:
                self.object_id = object_id
            self.cmd_calls: list[list[str]] = []

        @staticmethod
        def from_registry(_tag: str) -> _FakeImage:
            registry_tags.append(_tag)
            return _FakeImage()

        @staticmethod
        def from_id(_image_id: str) -> _FakeImage:
            _FakeImage.from_id_calls.append(_image_id)
            return _FakeImage(object_id=_image_id)

        def cmd(self, command: list[str]) -> _FakeImage:
            self.cmd_calls.append(command)
            return self

    class _FakeSandboxInstance:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.terminate_calls = 0
            self.terminate_kwargs: list[dict[str, object]] = []
            self.mount_image_calls: list[tuple[str, str | None]] = []
            self.terminate = _with_aio(self._terminate)
            self.poll = _with_aio(self._poll)
            self.tunnels = _with_aio(self._tunnels)
            self.exec = _with_aio(self._exec)
            self.snapshot_directory = _with_aio(self._snapshot_directory)
            self.mount_image = _with_aio(self._mount_image)

        def _terminate(self, **kwargs: object) -> None:
            self.terminate_calls += 1
            self.terminate_kwargs.append(kwargs)

        def _poll(self) -> None:
            return None

        def _tunnels(self, timeout: int = 50) -> dict[int, object]:
            _ = timeout
            return {
                8765: types.SimpleNamespace(
                    host="sandbox.example.test",
                    port=443,
                    unencrypted_host="",
                    unencrypted_port=0,
                )
            }

        def _snapshot_directory(self, _path: str) -> _FakeImage:
            return _FakeImage()

        def _mount_image(self, path: str, image: object) -> None:
            self.mount_image_calls.append((path, getattr(image, "object_id", None)))

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            resolve_helper_path = str(RESOLVE_WORKSPACE_PATH_HELPER.install_path)
            fingerprint_helper_path = str(WORKSPACE_FINGERPRINT_HELPER.install_path)

            class _FakeStream:
                def __init__(self, payload: bytes = b"") -> None:
                    self.read = _with_aio(lambda: payload)

            stdout = b""
            if (
                command[:2] == ("sh", "-c")
                and isinstance(command[2], str)
                and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in command[2]
            ):
                return types.SimpleNamespace(
                    stdout=_FakeStream(),
                    stderr=_FakeStream(),
                    wait=_with_aio(lambda: 0),
                )
            if command and command[0] == resolve_helper_path:
                stdout = str(command[2]).encode("utf-8")
            if command and command[0] == fingerprint_helper_path:
                stdout = (
                    b'{"fingerprint":"fake-workspace-fingerprint",'
                    b'"version":"workspace_tar_sha256_v1"}\n'
                )
            if command == ("test", "-d", "/workspace"):
                return types.SimpleNamespace(
                    stdout=_FakeStream(),
                    stderr=_FakeStream(),
                    wait=_with_aio(lambda: 1),
                )

            return types.SimpleNamespace(
                stdout=_FakeStream(stdout),
                stderr=_FakeStream(),
                wait=_with_aio(lambda: 0),
            )

    class _FakeSandbox:
        from_id_calls: list[str] = []
        create: Any
        from_id: Any

        @staticmethod
        def _create(**kwargs: object) -> _FakeSandboxInstance:
            create_calls.append(
                dict(
                    kwargs,
                    modal_image_builder_version_env=os.environ.get("MODAL_IMAGE_BUILDER_VERSION"),
                )
            )
            return _FakeSandboxInstance()

        @staticmethod
        def _from_id(_sandbox_id: str) -> _FakeSandboxInstance:
            _FakeSandbox.from_id_calls.append(_sandbox_id)
            return _FakeSandboxInstance()

    class _FakeApp:
        lookup: Any

        @staticmethod
        def _lookup(_name: str, *, create_if_missing: bool = False) -> object:
            _ = create_if_missing
            return object()

    class _FakeSecret:
        def __init__(
            self,
            value: dict[str, str] | None = None,
            *,
            name: str | None = None,
            environment_name: str | None = None,
        ) -> None:
            self.value = value
            self.name = name
            self.environment_name = environment_name

        @staticmethod
        def from_dict(value: dict[str, str]) -> _FakeSecret:
            return _FakeSecret(value)

        @staticmethod
        def from_name(name: str, *, environment_name: str | None = None) -> _FakeSecret:
            return _FakeSecret(name=name, environment_name=environment_name)

    class _FakeCloudBucketMount:
        def __init__(
            self,
            *,
            bucket_name: str,
            bucket_endpoint_url: str | None = None,
            key_prefix: str | None = None,
            secret: _FakeSecret | None = None,
            read_only: bool = True,
        ) -> None:
            self.bucket_name = bucket_name
            self.bucket_endpoint_url = bucket_endpoint_url
            self.key_prefix = key_prefix
            self.secret = secret
            self.read_only = read_only

    class _FakeConfig:
        override_calls: list[tuple[str, str]] = []

        @staticmethod
        def override_locally(key: str, value: str) -> None:
            _FakeConfig.override_calls.append((key, value))
            os.environ["MODAL_" + key.upper()] = value

    class _FakeModalError(Exception):
        pass

    class _FakeModalConnectionError(_FakeModalError):
        pass

    class _FakeModalExecTimeoutError(TimeoutError):
        pass

    class _FakeModalInternalFailure(_FakeModalError):
        pass

    class _FakeModalInvalidError(_FakeModalError):
        pass

    class _FakeModalNotFoundError(_FakeModalError):
        pass

    _FakeSandbox.create = staticmethod(_with_aio(_FakeSandbox._create))
    _FakeSandbox.from_id = staticmethod(_with_aio(_FakeSandbox._from_id))
    _FakeApp.lookup = staticmethod(_with_aio(_FakeApp._lookup))

    fake_modal: Any = types.ModuleType("modal")
    fake_modal.Image = _FakeImage
    fake_modal.App = _FakeApp
    fake_modal.Sandbox = _FakeSandbox
    fake_modal.Secret = _FakeSecret
    fake_modal.CloudBucketMount = _FakeCloudBucketMount

    fake_modal_exception: Any = types.ModuleType("modal.exception")
    fake_modal_exception.ConnectionError = _FakeModalConnectionError
    fake_modal_exception.ExecTimeoutError = _FakeModalExecTimeoutError
    fake_modal_exception.InternalFailure = _FakeModalInternalFailure
    fake_modal_exception.InvalidError = _FakeModalInvalidError
    fake_modal_exception.NotFoundError = _FakeModalNotFoundError
    fake_modal.exception = fake_modal_exception

    fake_modal_config: Any = types.ModuleType("modal.config")
    fake_modal_config.config = _FakeConfig

    fake_container_process: Any = types.ModuleType("modal.container_process")
    fake_container_process.ContainerProcess = object

    monkeypatch.setitem(sys.modules, "modal", fake_modal)
    monkeypatch.setitem(sys.modules, "modal.exception", fake_modal_exception)
    monkeypatch.setitem(sys.modules, "modal.config", fake_modal_config)
    monkeypatch.setitem(sys.modules, "modal.container_process", fake_container_process)
    sys.modules.pop("agents.extensions.sandbox.modal.sandbox", None)
    sys.modules.pop("agents.extensions.sandbox.modal.mounts", None)
    sys.modules.pop("agents.extensions.sandbox.modal", None)

    module: Any = importlib.import_module("agents.extensions.sandbox.modal.sandbox")
    return module, create_calls, registry_tags


def test_modal_package_re_exports_backend_symbols(monkeypatch: pytest.MonkeyPatch) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    package_module = importlib.import_module("agents.extensions.sandbox.modal")

    assert package_module.ModalSandboxClient is modal_module.ModalSandboxClient
    assert (
        package_module.ModalCloudBucketMountStrategy is modal_module.ModalCloudBucketMountStrategy
    )


@pytest.mark.asyncio
async def test_modal_sandbox_create_passes_manifest_environment(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        manifest=Manifest(environment=Environment(value={"SANDBOX_FLAG": "enabled"})),
        options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"),
    )

    assert create_calls
    assert create_calls[0]["env"] == {"SANDBOX_FLAG": "enabled"}
    assert create_calls[0]["modal_image_builder_version_env"] == "2025.06"
    assert registry_tags == [DEFAULT_PYTHON_SANDBOX_IMAGE]
    image = cast(Any, create_calls[0]["image"])
    assert image.cmd_calls == [["sleep", "infinity"]]
    assert os.environ.get("MODAL_IMAGE_BUILDER_VERSION") is None


@pytest.mark.asyncio
async def test_modal_sandbox_create_passes_idle_timeout(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    session = await client.create(
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            idle_timeout=60,
        ),
    )

    assert create_calls
    assert create_calls[0]["idle_timeout"] == 60
    assert session.state.idle_timeout == 60


@pytest.mark.asyncio
async def test_modal_sandbox_create_sets_default_cmd_for_custom_registry_image(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient(
        image=modal_module.ModalImageSelector.from_tag("debian:bookworm-slim")
    )
    await client.create(
        options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"),
    )

    assert create_calls
    assert registry_tags == ["debian:bookworm-slim"]
    image = cast(Any, create_calls[0]["image"])
    assert image.cmd_calls == [["sleep", "infinity"]]


@pytest.mark.asyncio
async def test_modal_sandbox_create_can_opt_out_of_default_cmd(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            use_sleep_cmd=False,
        ),
    )

    assert create_calls
    assert registry_tags == [DEFAULT_PYTHON_SANDBOX_IMAGE]
    image = cast(Any, create_calls[0]["image"])
    assert image.cmd_calls == []


@pytest.mark.asyncio
async def test_modal_sandbox_create_uses_custom_image_builder_version(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    session = await client.create(
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            image_builder_version="PREVIEW",
        ),
    )

    assert create_calls
    assert create_calls[0]["modal_image_builder_version_env"] == "PREVIEW"
    assert session.state.image_builder_version == "PREVIEW"
    assert os.environ.get("MODAL_IMAGE_BUILDER_VERSION") is None


@pytest.mark.asyncio
async def test_modal_sandbox_create_uses_existing_config_when_image_builder_version_is_none(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)
    monkeypatch.setenv("MODAL_IMAGE_BUILDER_VERSION", "USER-CONFIGURED")

    client = modal_module.ModalSandboxClient()
    session = await client.create(
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            image_builder_version=None,
        ),
    )

    assert create_calls
    assert create_calls[0]["modal_image_builder_version_env"] == "USER-CONFIGURED"
    assert session.state.image_builder_version is None
    assert os.environ.get("MODAL_IMAGE_BUILDER_VERSION") == "USER-CONFIGURED"


def test_modal_deserialize_session_state_defaults_missing_image_builder_version(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        image_builder_version="PREVIEW",
    )
    payload = state.model_dump(mode="json")
    payload.pop("image_builder_version")

    restored = modal_module.ModalSandboxClient().deserialize_session_state(
        cast(dict[str, object], payload)
    )

    assert restored.image_builder_version == "2025.06"


def test_modal_deserialize_session_state_defaults_missing_idle_timeout(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        idle_timeout=60,
    )
    payload = state.model_dump(mode="json")
    payload.pop("idle_timeout")

    restored = modal_module.ModalSandboxClient().deserialize_session_state(
        cast(dict[str, object], payload)
    )

    assert restored.idle_timeout is None


@pytest.mark.asyncio
async def test_modal_sandbox_create_passes_modal_cloud_bucket_mounts(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        manifest=Manifest(
            entries={
                "remote": S3Mount(
                    bucket="bucket",
                    access_key_id="access-key",
                    secret_access_key="secret-key",
                    prefix="nested/prefix/",
                    mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
                    read_only=False,
                )
            }
        ),
        options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"),
    )

    assert create_calls
    volumes = create_calls[0]["volumes"]
    assert isinstance(volumes, dict)
    assert volumes.keys() == {"/workspace/remote"}
    mount = volumes["/workspace/remote"]
    assert mount.bucket_name == "bucket"
    assert mount.bucket_endpoint_url is None
    assert mount.key_prefix == "nested/prefix/"
    assert mount.secret.value == {
        "AWS_ACCESS_KEY_ID": "access-key",
        "AWS_SECRET_ACCESS_KEY": "secret-key",
    }
    assert mount.read_only is False


@pytest.mark.asyncio
async def test_modal_sandbox_create_passes_named_modal_secret_for_cloud_bucket_mount(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        manifest=Manifest(
            entries={
                "remote": S3Mount(
                    bucket="bucket",
                    prefix="nested/prefix/",
                    mount_strategy=modal_module.ModalCloudBucketMountStrategy(
                        secret_name="named-modal-secret"
                    ),
                    read_only=False,
                )
            }
        ),
        options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"),
    )

    assert create_calls
    volumes = create_calls[0]["volumes"]
    assert isinstance(volumes, dict)
    assert volumes.keys() == {"/workspace/remote"}
    mount = volumes["/workspace/remote"]
    assert mount.bucket_name == "bucket"
    assert mount.bucket_endpoint_url is None
    assert mount.key_prefix == "nested/prefix/"
    assert mount.secret.name == "named-modal-secret"
    assert mount.secret.value is None
    assert mount.read_only is False


@pytest.mark.asyncio
async def test_modal_sandbox_create_passes_named_modal_secret_environment_for_cloud_bucket_mount(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        manifest=Manifest(
            entries={
                "remote": S3Mount(
                    bucket="bucket",
                    prefix="nested/prefix/",
                    mount_strategy=modal_module.ModalCloudBucketMountStrategy(
                        secret_name="named-modal-secret",
                        secret_environment_name="staging",
                    ),
                    read_only=False,
                )
            }
        ),
        options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"),
    )

    assert create_calls
    volumes = create_calls[0]["volumes"]
    assert isinstance(volumes, dict)
    mount = volumes["/workspace/remote"]
    assert mount.secret.name == "named-modal-secret"
    assert mount.secret.environment_name == "staging"
    assert mount.secret.value is None


def test_modal_cloud_bucket_mount_strategy_round_trips_through_manifest_parse(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    manifest = Manifest.model_validate(
        {
            "entries": {
                "remote": {
                    "type": "s3_mount",
                    "bucket": "bucket",
                    "mount_strategy": {"type": "modal_cloud_bucket"},
                }
            }
        }
    )

    mount = manifest.entries["remote"]

    assert isinstance(mount, S3Mount)
    assert isinstance(mount.mount_strategy, modal_module.ModalCloudBucketMountStrategy)


def test_modal_cloud_bucket_mount_strategy_round_trips_secret_name_through_manifest_parse(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    manifest = Manifest.model_validate(
        {
            "entries": {
                "remote": {
                    "type": "s3_mount",
                    "bucket": "bucket",
                    "mount_strategy": {
                        "type": "modal_cloud_bucket",
                        "secret_name": "named-modal-secret",
                    },
                }
            }
        }
    )

    mount = manifest.entries["remote"]

    assert isinstance(mount, S3Mount)
    assert isinstance(mount.mount_strategy, modal_module.ModalCloudBucketMountStrategy)
    assert mount.mount_strategy.secret_name == "named-modal-secret"


def test_modal_cloud_bucket_mount_strategy_round_trips_secret_env_name(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    manifest = Manifest.model_validate(
        {
            "entries": {
                "remote": {
                    "type": "s3_mount",
                    "bucket": "bucket",
                    "mount_strategy": {
                        "type": "modal_cloud_bucket",
                        "secret_name": "named-modal-secret",
                        "secret_environment_name": "staging",
                    },
                }
            }
        }
    )

    mount = manifest.entries["remote"]

    assert isinstance(mount, S3Mount)
    assert isinstance(mount.mount_strategy, modal_module.ModalCloudBucketMountStrategy)
    assert mount.mount_strategy.secret_name == "named-modal-secret"
    assert mount.mount_strategy.secret_environment_name == "staging"


def test_modal_cloud_bucket_mount_strategy_builds_s3_config(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy()
    mount = S3Mount(
        bucket="bucket",
        access_key_id="access-key",
        secret_access_key="secret-key",
        session_token="session-token",
        prefix="nested/prefix/",
        endpoint_url="https://s3.example.test",
        mount_strategy=strategy,
        read_only=False,
    )

    config = strategy._build_modal_cloud_bucket_mount_config(mount)  # noqa: SLF001

    assert config.bucket_name == "bucket"
    assert config.bucket_endpoint_url == "https://s3.example.test"
    assert config.key_prefix == "nested/prefix/"
    assert config.credentials == {
        "AWS_ACCESS_KEY_ID": "access-key",
        "AWS_SECRET_ACCESS_KEY": "secret-key",
        "AWS_SESSION_TOKEN": "session-token",
    }
    assert config.read_only is False


def test_modal_cloud_bucket_mount_strategy_builds_s3_config_with_named_secret(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy(secret_name="named-modal-secret")
    mount = S3Mount(
        bucket="bucket",
        prefix="nested/prefix/",
        mount_strategy=strategy,
        read_only=False,
    )

    config = strategy._build_modal_cloud_bucket_mount_config(mount)  # noqa: SLF001

    assert config.bucket_name == "bucket"
    assert config.bucket_endpoint_url is None
    assert config.key_prefix == "nested/prefix/"
    assert config.credentials is None
    assert config.secret_name == "named-modal-secret"
    assert config.secret_environment_name is None
    assert config.read_only is False


def test_modal_cloud_bucket_mount_strategy_builds_s3_config_with_named_secret_environment(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy(
        secret_name="named-modal-secret",
        secret_environment_name="staging",
    )
    mount = S3Mount(
        bucket="bucket",
        prefix="nested/prefix/",
        mount_strategy=strategy,
        read_only=False,
    )

    config = strategy._build_modal_cloud_bucket_mount_config(mount)  # noqa: SLF001

    assert config.bucket_name == "bucket"
    assert config.credentials is None
    assert config.secret_name == "named-modal-secret"
    assert config.secret_environment_name == "staging"
    assert config.read_only is False


def test_modal_cloud_bucket_mount_strategy_builds_r2_config(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy()
    mount = R2Mount(
        bucket="bucket",
        account_id="abc123accountid",
        access_key_id="access-key",
        secret_access_key="secret-key",
        mount_strategy=strategy,
    )

    config = strategy._build_modal_cloud_bucket_mount_config(mount)  # noqa: SLF001

    assert config.bucket_name == "bucket"
    assert config.bucket_endpoint_url == "https://abc123accountid.r2.cloudflarestorage.com"
    assert config.key_prefix is None
    assert config.credentials == {
        "AWS_ACCESS_KEY_ID": "access-key",
        "AWS_SECRET_ACCESS_KEY": "secret-key",
    }
    assert config.read_only is True


def test_modal_cloud_bucket_mount_strategy_builds_gcs_hmac_config(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy()
    mount = GCSMount(
        bucket="bucket",
        access_id="access-id",
        secret_access_key="secret-key",
        prefix="nested/prefix/",
        mount_strategy=strategy,
        read_only=False,
    )

    config = strategy._build_modal_cloud_bucket_mount_config(mount)  # noqa: SLF001

    assert config.bucket_name == "bucket"
    assert config.bucket_endpoint_url == "https://storage.googleapis.com"
    assert config.key_prefix == "nested/prefix/"
    assert config.credentials == {
        "GOOGLE_ACCESS_KEY_ID": "access-id",
        "GOOGLE_ACCESS_KEY_SECRET": "secret-key",
    }
    assert config.read_only is False


def test_modal_cloud_bucket_mount_strategy_builds_gcs_hmac_config_with_named_secret(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy(secret_name="named-modal-secret")
    mount = GCSMount(
        bucket="bucket",
        prefix="nested/prefix/",
        mount_strategy=strategy,
        read_only=False,
    )

    config = strategy._build_modal_cloud_bucket_mount_config(mount)  # noqa: SLF001

    assert config.bucket_name == "bucket"
    assert config.bucket_endpoint_url == "https://storage.googleapis.com"
    assert config.key_prefix == "nested/prefix/"
    assert config.credentials is None
    assert config.secret_name == "named-modal-secret"
    assert config.secret_environment_name is None
    assert config.read_only is False


def test_modal_cloud_bucket_mount_strategy_rejects_secret_environment_name_without_secret_name(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy(secret_environment_name="staging")

    with pytest.raises(
        MountConfigError,
        match="secret_environment_name requires secret_name to also be set",
    ):
        strategy._build_modal_cloud_bucket_mount_config(  # noqa: SLF001
            S3Mount(bucket="bucket", mount_strategy=strategy)
        )


def test_modal_cloud_bucket_mount_strategy_rejects_mixed_inline_credentials_and_secret_name(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    strategy = modal_module.ModalCloudBucketMountStrategy(secret_name="named-modal-secret")

    with pytest.raises(
        MountConfigError,
        match="do not support both inline credentials and secret_name",
    ):
        strategy._build_modal_cloud_bucket_mount_config(  # noqa: SLF001
            S3Mount(
                bucket="bucket",
                access_key_id="access-key",
                secret_access_key="secret-key",
                mount_strategy=strategy,
            )
        )


def test_modal_cloud_bucket_mount_strategy_rejects_gcs_native_auth(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    with pytest.raises(
        MountConfigError,
        match="gcs modal cloud bucket mounts require access_id and secret_access_key",
    ):
        GCSMount(
            bucket="bucket",
            service_account_file="/data/config/gcs.json",
            mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
        )


def _load_modal_runner_module(monkeypatch: pytest.MonkeyPatch) -> Any:
    _load_modal_module(monkeypatch)
    monkeypatch.delitem(sys.modules, "agents.extensions.sandbox", raising=False)
    monkeypatch.delitem(sys.modules, "examples.sandbox.extensions.modal_runner", raising=False)
    return importlib.import_module("examples.sandbox.extensions.modal_runner")


def test_modal_runner_builds_s3_native_bucket_by_default(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    runner = _load_modal_runner_module(monkeypatch)
    monkeypatch.setenv("AWS_ACCESS_KEY_ID", "access-key")
    monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "secret-key")

    manifest = runner._build_manifest(native_cloud_bucket_name="bucket")  # noqa: SLF001

    mount = manifest.entries["cloud-bucket"]
    assert isinstance(mount, S3Mount)
    assert mount.bucket == "bucket"
    assert mount.access_key_id == "access-key"
    assert mount.secret_access_key == "secret-key"


def test_modal_runner_builds_s3_native_bucket_with_named_secret(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    runner = _load_modal_runner_module(monkeypatch)
    monkeypatch.setenv("AWS_ACCESS_KEY_ID", "access-key")
    monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "secret-key")

    manifest = runner._build_manifest(  # noqa: SLF001
        native_cloud_bucket_name="bucket",
        native_cloud_bucket_secret_name="named-modal-secret",
    )

    mount = manifest.entries["cloud-bucket"]
    assert isinstance(mount, S3Mount)
    assert mount.bucket == "bucket"
    assert mount.access_key_id is None
    assert mount.secret_access_key is None
    assert mount.session_token is None
    strategy = mount.mount_strategy
    assert isinstance(strategy, runner.ModalCloudBucketMountStrategy)
    assert strategy.secret_name == "named-modal-secret"
    assert strategy.secret_environment_name is None


def test_modal_runner_builds_gcs_hmac_native_bucket(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    runner = _load_modal_runner_module(monkeypatch)
    monkeypatch.setenv("GCS_HMAC_ACCESS_KEY_ID", "access-id")
    monkeypatch.setenv("GCS_HMAC_SECRET_ACCESS_KEY", "secret-key")

    manifest = runner._build_manifest(  # noqa: SLF001
        native_cloud_bucket_name="bucket",
        native_cloud_bucket_provider="gcs-hmac",
        native_cloud_bucket_mount_path="mounted",
        native_cloud_bucket_key_prefix="nested/prefix/",
    )

    mount = manifest.entries["cloud-bucket"]
    assert isinstance(mount, GCSMount)
    assert mount.bucket == "bucket"
    assert mount.access_id == "access-id"
    assert mount.secret_access_key == "secret-key"
    assert mount.mount_path == Path("mounted")
    assert mount.prefix == "nested/prefix/"
    assert runner._native_cloud_bucket_mount_path(manifest) == Path("/workspace/mounted")


def test_modal_runner_builds_gcs_hmac_native_bucket_with_named_secret(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    runner = _load_modal_runner_module(monkeypatch)
    monkeypatch.setenv("GCS_HMAC_ACCESS_KEY_ID", "access-id")
    monkeypatch.setenv("GCS_HMAC_SECRET_ACCESS_KEY", "secret-key")

    manifest = runner._build_manifest(  # noqa: SLF001
        native_cloud_bucket_name="bucket",
        native_cloud_bucket_provider="gcs-hmac",
        native_cloud_bucket_secret_name="named-modal-secret",
    )

    mount = manifest.entries["cloud-bucket"]
    assert isinstance(mount, GCSMount)
    assert mount.bucket == "bucket"
    assert mount.access_id is None
    assert mount.secret_access_key is None
    strategy = mount.mount_strategy
    assert isinstance(strategy, runner.ModalCloudBucketMountStrategy)
    assert strategy.secret_name == "named-modal-secret"
    assert strategy.secret_environment_name is None


@pytest.mark.asyncio
async def test_modal_start_ensures_sandbox_before_running_commands(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    session = await client.create(
        options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"),
    )

    assert session._inner._sandbox is not None  # noqa: SLF001
    assert len(create_calls) == 1

    await session.start()

    assert session._inner._sandbox is not None  # noqa: SLF001
    assert len(create_calls) == 1


@pytest.mark.asyncio
async def test_modal_sandbox_create_exposes_declared_ports(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            exposed_ports=(8765,),
        ),
    )

    assert create_calls
    assert create_calls[0]["encrypted_ports"] == (8765,)


@pytest.mark.asyncio
async def test_modal_resume_eagerly_reconnects_sandbox(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-existing",
    )

    client = modal_module.ModalSandboxClient()
    session = await client.resume(state)

    assert session._inner._sandbox is not None  # noqa: SLF001
    assert create_calls == []
    assert sys.modules["modal"].Sandbox.from_id_calls == ["sb-existing"]


@pytest.mark.asyncio
async def test_modal_resume_marks_reconnected_sandbox_preserved_before_snapshot_reuse(
    monkeypatch: pytest.MonkeyPatch,
    tmp_path: Path,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)
    snapshot = LocalSnapshot(id="modal-snapshot", base_path=tmp_path)
    await snapshot.persist(
        io.BytesIO(modal_module._encode_snapshot_filesystem_ref(snapshot_id="snap-123"))
    )
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=snapshot,
        app_name="sandbox-tests",
        sandbox_id="sb-existing",
        workspace_persistence="snapshot_filesystem",
        snapshot_fingerprint="fake-workspace-fingerprint",
        snapshot_fingerprint_version="workspace_tar_sha256_v1",
        workspace_root_ready=True,
    )

    client = modal_module.ModalSandboxClient()
    session = await client.resume(state)

    assert session._inner._running is True  # noqa: SLF001
    assert session._inner._workspace_state_preserved_on_start() is True  # noqa: SLF001
    assert session._inner._system_state_preserved_on_start() is True  # noqa: SLF001

    await session.start()

    assert create_calls == []
    assert sys.modules["modal"].Sandbox.from_id_calls == ["sb-existing"]
    assert sys.modules["modal"].Image.from_id_calls == []


@pytest.mark.asyncio
async def test_modal_resume_restores_snapshot_when_workspace_readiness_unproven(
    monkeypatch: pytest.MonkeyPatch,
    tmp_path: Path,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)
    snapshot = LocalSnapshot(id="modal-snapshot", base_path=tmp_path)
    await snapshot.persist(
        io.BytesIO(modal_module._encode_snapshot_filesystem_ref(snapshot_id="snap-123"))
    )
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=snapshot,
        app_name="sandbox-tests",
        sandbox_id="sb-existing",
        workspace_persistence="snapshot_filesystem",
        snapshot_fingerprint="fake-workspace-fingerprint",
        snapshot_fingerprint_version="workspace_tar_sha256_v1",
    )

    client = modal_module.ModalSandboxClient()
    session = await client.resume(state)

    assert session._inner._running is True  # noqa: SLF001
    assert session._inner._workspace_state_preserved_on_start() is True  # noqa: SLF001
    assert session._inner._can_reuse_preserved_workspace_on_resume() is False  # noqa: SLF001

    await session.start()

    assert len(create_calls) == 1
    assert create_calls[0]["workdir"] == "/workspace"
    assert sys.modules["modal"].Sandbox.from_id_calls == ["sb-existing"]
    assert sys.modules["modal"].Image.from_id_calls == ["snap-123"]


@pytest.mark.asyncio
async def test_modal_resume_restores_directory_snapshot_when_workspace_readiness_unproven(
    monkeypatch: pytest.MonkeyPatch,
    tmp_path: Path,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)
    snapshot = LocalSnapshot(id="modal-snapshot", base_path=tmp_path)
    await snapshot.persist(
        io.BytesIO(modal_module._encode_snapshot_directory_ref(snapshot_id="snap-dir-123"))
    )
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=snapshot,
        app_name="sandbox-tests",
        sandbox_id="sb-existing",
        workspace_persistence="snapshot_directory",
        snapshot_fingerprint="fake-workspace-fingerprint",
        snapshot_fingerprint_version="workspace_tar_sha256_v1",
    )

    client = modal_module.ModalSandboxClient()
    session = await client.resume(state)
    inner = session._inner  # noqa: SLF001

    assert inner._running is True  # noqa: SLF001
    assert inner._workspace_state_preserved_on_start() is True  # noqa: SLF001
    assert inner._can_reuse_preserved_workspace_on_resume() is False  # noqa: SLF001

    await session.start()

    assert create_calls == []
    assert sys.modules["modal"].Sandbox.from_id_calls == ["sb-existing"]
    assert sys.modules["modal"].Image.from_id_calls == ["snap-dir-123"]
    assert inner._sandbox is not None  # noqa: SLF001
    assert inner._sandbox.mount_image_calls == [("/workspace", "snap-dir-123")]  # noqa: SLF001


@pytest.mark.asyncio
async def test_modal_resume_resets_workspace_readiness_when_sandbox_is_recreated(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _StoppedSandboxInstance:
        object_id = "sb-stopped"

        def __init__(self) -> None:
            self.poll = _with_aio(lambda: 1)

    def _from_stopped_id(_sandbox_id: str) -> object:
        sys.modules["modal"].Sandbox.from_id_calls.append(_sandbox_id)
        return _StoppedSandboxInstance()

    sys.modules["modal"].Sandbox.from_id = staticmethod(_with_aio(_from_stopped_id))
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-stopped",
        workspace_root_ready=True,
        image_builder_version="PREVIEW",
    )

    client = modal_module.ModalSandboxClient()
    session = await client.resume(state)

    assert session._inner._workspace_state_preserved_on_start() is False  # noqa: SLF001
    assert state.workspace_root_ready is False
    assert create_calls
    assert create_calls[0]["modal_image_builder_version_env"] == "PREVIEW"
    assert state.sandbox_id == "sb-123"
    assert os.environ.get("MODAL_IMAGE_BUILDER_VERSION") is None


@pytest.mark.asyncio
async def test_modal_resume_bounds_reconnect_and_poll(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_create_timeout_s=12.5,
        sandbox_id="sb-existing",
    )

    session = modal_module.ModalSandboxSession.from_state(state)
    call_timeouts: list[float | None] = []

    real_call_modal = session._call_modal  # noqa: SLF001

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        call_timeouts.append(call_timeout)
        return await real_call_modal(fn, *args, call_timeout=call_timeout, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    await session._ensure_sandbox()  # noqa: SLF001

    assert session._sandbox is not None  # noqa: SLF001
    assert create_calls == []
    assert call_timeouts == [12.5, modal_module._DEFAULT_TIMEOUT_S]  # noqa: SLF001


@pytest.mark.asyncio
async def test_modal_ensure_sandbox_bounds_app_lookup(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )

    session = modal_module.ModalSandboxSession.from_state(state)
    call_timeouts: list[float | None] = []

    real_call_modal = session._call_modal  # noqa: SLF001

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        call_timeouts.append(call_timeout)
        return await real_call_modal(fn, *args, call_timeout=call_timeout, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    await session._ensure_sandbox()  # noqa: SLF001

    assert session._sandbox is not None  # noqa: SLF001
    assert len(create_calls) == 1
    assert call_timeouts == [10.0, modal_module._DEFAULT_TIMEOUT_S]  # noqa: SLF001


@pytest.mark.asyncio
async def test_modal_ensure_sandbox_bounds_image_id_lookup(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        image_id="im-existing",
    )

    session = modal_module.ModalSandboxSession.from_state(state)
    call_names: list[str] = []
    call_timeouts: list[float | None] = []

    real_call_modal = session._call_modal  # noqa: SLF001

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        call_names.append(getattr(fn, "__name__", ""))
        call_timeouts.append(call_timeout)
        return await real_call_modal(fn, *args, call_timeout=call_timeout, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    await session._ensure_sandbox()  # noqa: SLF001

    assert session._sandbox is not None  # noqa: SLF001
    assert len(create_calls) == 1
    assert sys.modules["modal"].Image.from_id_calls == ["im-existing"]
    assert call_names == ["_sync"]
    assert call_timeouts == [10.0]


@pytest.mark.asyncio
async def test_modal_resolve_exposed_port_reads_tunnel_metadata(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    sandbox = sys.modules["modal"].Sandbox.create()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        exposed_ports=(8765,),
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)

    endpoint = await session.resolve_exposed_port(8765)

    assert endpoint.host == "sandbox.example.test"
    assert endpoint.port == 443
    assert endpoint.tls is True


@pytest.mark.asyncio
async def test_modal_stop_is_persistence_only_and_shutdown_terminates(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    sandbox = sys.modules["modal"].Sandbox.create()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    session._running = True
    call_timeouts: list[float | None] = []

    real_call_modal = session._call_modal  # noqa: SLF001

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        call_timeouts.append(call_timeout)
        return await real_call_modal(fn, *args, call_timeout=call_timeout, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    await session.stop()

    assert sandbox.terminate_calls == 0
    assert session.state.sandbox_id == "sb-123"
    assert await session.running() is True

    await session.shutdown()

    assert sandbox.terminate_calls == 1
    assert sandbox.terminate_kwargs == [{}]
    assert session.state.sandbox_id is None
    assert await session.running() is False
    assert call_timeouts == [modal_module._DEFAULT_TIMEOUT_S]  # noqa: SLF001


@pytest.mark.asyncio
async def test_modal_shutdown_rehydrates_sandbox_and_terminates_without_wait_kwarg(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    sandbox = sys.modules["modal"].Sandbox.create()

    def _from_id(_sandbox_id: str) -> object:
        sys.modules["modal"].Sandbox.from_id_calls.append(_sandbox_id)
        return sandbox

    sys.modules["modal"].Sandbox.from_id = staticmethod(_with_aio(_from_id))
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-existing",
    )
    session = modal_module.ModalSandboxSession.from_state(state)
    call_timeouts: list[float | None] = []

    real_call_modal = session._call_modal  # noqa: SLF001

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        call_timeouts.append(call_timeout)
        return await real_call_modal(fn, *args, call_timeout=call_timeout, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    await session.shutdown()

    assert sys.modules["modal"].Sandbox.from_id_calls == ["sb-existing"]
    assert sandbox.terminate_kwargs == [{}]
    assert session.state.sandbox_id is None
    assert await session.running() is False
    assert call_timeouts == [
        modal_module._DEFAULT_TIMEOUT_S,
        modal_module._DEFAULT_TIMEOUT_S,
    ]  # noqa: SLF001


@pytest.mark.asyncio
async def test_modal_tar_persist_respects_runtime_skip_paths(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-123",
    )
    session = modal_module.ModalSandboxSession.from_state(state)
    session.register_persist_workspace_skip_path(Path("logs/events.jsonl"))

    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        return ExecResult(stdout=b"fake-tar-bytes", stderr=b"", exit_code=0)

    monkeypatch.setattr(session, "exec", _fake_exec)

    archive = await session.persist_workspace()

    assert archive.read() == b"fake-tar-bytes"
    assert commands == [
        [
            "tar",
            "cf",
            "-",
            "--exclude",
            "./logs/events.jsonl",
            "-C",
            "/workspace",
            ".",
        ]
    ]


@pytest.mark.asyncio
async def test_modal_snapshot_failure_restores_ephemeral_paths(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeRestoreProcess:
        def __init__(self, owner: Any) -> None:
            self._owner = owner
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.stdin = self._FakeStdin(owner)
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.wait = _with_aio(self._wait)

        class _FakeStdin:
            def __init__(self, owner: Any) -> None:
                self._owner = owner
                self._buffer = bytearray()

            def write(self, data: bytes) -> None:
                self._buffer.extend(data)

            def write_eof(self) -> None:
                return

            def drain(self) -> None:
                return

        def _wait(self) -> int:
            self._owner.restore_payloads.append(bytes(self.stdin._buffer))
            return 0

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.restore_payloads: list[bytes] = []
            self.snapshot_filesystem = _with_aio(self._snapshot_filesystem)
            self.exec = _with_aio(self._exec)

        def _snapshot_filesystem(self) -> str:
            raise RuntimeError("snapshot failed")

        def _exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess:
            _ = kwargs
            assert command[:3] == ("tar", "xf", "-")
            return _FakeRestoreProcess(self)

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={"tmp.txt": File(content=b"ephemeral", ephemeral=True)},
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_filesystem",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        if rendered[:2] == ["sh", "-lc"]:
            return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0)
        if rendered[:3] == ["rm", "-rf", "--"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = call_timeout
        return fn(*args, **kwargs)

    monkeypatch.setattr(session, "exec", _fake_exec)
    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert exc_info.value.context["reason"] == "snapshot_filesystem_failed"
    assert sandbox.restore_payloads == [b"ephemeral-backup"]


@pytest.mark.asyncio
async def test_modal_snapshot_cleanup_failure_raises_before_snapshot(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeRestoreProcess:
        def __init__(self, owner: Any) -> None:
            self._owner = owner
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.stdin = self._FakeStdin(owner)
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.wait = _with_aio(self._wait)

        class _FakeStdin:
            def __init__(self, owner: Any) -> None:
                self._owner = owner
                self._buffer = bytearray()

            def write(self, data: bytes) -> None:
                self._buffer.extend(data)

            def write_eof(self) -> None:
                return

            def drain(self) -> None:
                return

        def _wait(self) -> int:
            self._owner.restore_payloads.append(bytes(self.stdin._buffer))
            return 0

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.restore_payloads: list[bytes] = []
            self.snapshot_calls = 0
            self.snapshot_filesystem = _with_aio(self._snapshot_filesystem)
            self.exec = _with_aio(self._exec)

        def _snapshot_filesystem(self) -> str:
            self.snapshot_calls += 1
            return "snap-123"

        def _exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess:
            _ = kwargs
            assert command[:3] == ("tar", "xf", "-")
            return _FakeRestoreProcess(self)

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={"tmp.txt": File(content=b"ephemeral", ephemeral=True)},
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_filesystem",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        if rendered[:2] == ["sh", "-lc"]:
            return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0)
        if rendered[:3] == ["rm", "-rf", "--"]:
            return ExecResult(stdout=b"", stderr=b"rm failed", exit_code=1)
        raise AssertionError(f"unexpected command: {rendered!r}")

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = call_timeout
        return fn(*args, **kwargs)

    monkeypatch.setattr(session, "exec", _fake_exec)
    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert exc_info.value.context["reason"] == "snapshot_filesystem_ephemeral_remove_failed"
    assert exc_info.value.context["exit_code"] == 1
    assert exc_info.value.context["stderr"] == "rm failed"
    assert sandbox.snapshot_calls == 0
    assert sandbox.restore_payloads == [b"ephemeral-backup"]


@pytest.mark.asyncio
async def test_modal_normalize_path_preserves_safe_leaf_symlink_path(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        if (
            rendered[:2] == ["sh", "-c"]
            and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in rendered[2]
        ):
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered and rendered[0] == str(RESOLVE_WORKSPACE_PATH_HELPER.install_path):
            return ExecResult(stdout=b"/workspace/target.txt", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    normalized = await session._validate_path_access("link.txt")  # noqa: SLF001

    assert normalized.as_posix() == "/workspace/link.txt"


@pytest.mark.asyncio
async def test_modal_normalize_path_uses_posix_commands_for_windows_paths(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if (
            rendered[:2] == ["sh", "-c"]
            and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in rendered[2]
        ):
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered and rendered[0] == str(RESOLVE_WORKSPACE_PATH_HELPER.install_path):
            return ExecResult(stdout=b"/workspace/link.txt", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    normalized = await session._validate_path_access(PureWindowsPath("/workspace/link.txt"))  # noqa: SLF001

    helper_path = str(RESOLVE_WORKSPACE_PATH_HELPER.install_path)
    assert normalized.as_posix() == "/workspace/link.txt"
    assert commands[-1] == [helper_path, "/workspace", "/workspace/link.txt", "0"]
    assert all("\\" not in arg for arg in commands[-1])


@pytest.mark.asyncio
async def test_modal_normalize_path_rejects_windows_drive_absolute_paths(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state)

    async def _fake_exec(*args: object, **kwargs: object) -> ExecResult:
        _ = (args, kwargs)
        raise AssertionError("path validation should reject before remote helper execution")

    monkeypatch.setattr(session, "exec", _fake_exec)

    with pytest.raises(InvalidManifestPathError) as exc_info:
        await session._validate_path_access(PureWindowsPath("C:/tmp/link.txt"))  # noqa: SLF001

    assert str(exc_info.value) == "manifest path must be relative: C:/tmp/link.txt"
    assert exc_info.value.context == {"rel": "C:/tmp/link.txt", "reason": "absolute"}


@pytest.mark.asyncio
async def test_modal_normalize_path_rejects_symlink_escape(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        if (
            rendered[:2] == ["sh", "-c"]
            and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in rendered[2]
        ):
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered and rendered[0] == str(RESOLVE_WORKSPACE_PATH_HELPER.install_path):
            return ExecResult(stdout=b"", stderr=b"workspace escape", exit_code=111)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    with pytest.raises(InvalidManifestPathError, match="must not escape root"):
        await session._validate_path_access("link/secret.txt")  # noqa: SLF001


@pytest.mark.asyncio
async def test_modal_normalize_path_reinstalls_helper_after_runtime_replacement(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-old",
    )
    session = modal_module.ModalSandboxSession.from_state(state)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if (
            rendered[:2] == ["sh", "-c"]
            and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in rendered[2]
        ):
            if state.sandbox_id is None:
                state.sandbox_id = "sb-new"
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered == ["test", "-x", str(RESOLVE_WORKSPACE_PATH_HELPER.install_path)]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered and rendered[0] == str(RESOLVE_WORKSPACE_PATH_HELPER.install_path):
            return ExecResult(stdout=b"/workspace/target.txt", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    assert (await session._validate_path_access("link.txt")).as_posix() == "/workspace/link.txt"
    first_run_commands = list(commands)
    commands.clear()

    state.sandbox_id = None
    assert (await session._validate_path_access("link.txt")).as_posix() == "/workspace/link.txt"
    second_run_commands = list(commands)
    commands.clear()

    assert (await session._validate_path_access("link.txt")).as_posix() == "/workspace/link.txt"

    helper_path = str(RESOLVE_WORKSPACE_PATH_HELPER.install_path)
    assert any(
        cmd[:2] == ["sh", "-c"] and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in cmd[2]
        for cmd in first_run_commands
    )
    assert any(
        cmd[:2] == ["sh", "-c"] and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in cmd[2]
        for cmd in second_run_commands
    )
    assert any(cmd and cmd[0] == helper_path for cmd in second_run_commands)
    assert commands == [
        ["test", "-x", helper_path],
        [helper_path, "/workspace", "/workspace/link.txt", "0"],
    ]


@pytest.mark.asyncio
async def test_modal_snapshot_filesystem_uses_resolved_mount_paths_for_backup_and_removal(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeRestoreProcess:
        def __init__(self) -> None:
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.stdin = self._FakeStdin()
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.wait = _with_aio(self._wait)

        class _FakeStdin:
            def write(self, data: bytes) -> None:
                _ = data

            def write_eof(self) -> None:
                return

            def drain(self) -> None:
                return

        def _wait(self) -> int:
            return 0

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.snapshot_filesystem = _with_aio(self._snapshot_filesystem)
            self.exec = _with_aio(self._exec)

        def _snapshot_filesystem(self) -> str:
            return "snap-123"

        def _exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess:
            _ = kwargs
            assert command[:3] == ("tar", "xf", "-")
            return _FakeRestoreProcess()

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "logical": _RecordingMount(
                    mount_path=Path("actual"),
                    ephemeral=False,
                ),
                "logs/events.jsonl": File(content=b"skip", ephemeral=True),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_filesystem",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    def _snapshot_filesystem() -> str:
        return "snap-123"

    sandbox.snapshot_filesystem = _with_aio(_snapshot_filesystem)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered[:2] == ["sh", "-lc"]:
            return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0)
        if rendered[:3] == ["rm", "-rf", "--"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    archive = await session.persist_workspace()

    assert archive.read() == modal_module._encode_snapshot_filesystem_ref(snapshot_id="snap-123")
    assert commands[0][0:2] == ["sh", "-lc"]
    assert "logs/events.jsonl" in commands[0][2]
    assert "actual" not in commands[0][2]
    assert "logical" not in commands[0][2]
    assert commands[1] == ["rm", "-rf", "--", "/workspace/logs/events.jsonl"]


@pytest.mark.asyncio
async def test_modal_snapshot_directory_uses_resolved_mount_paths_for_backup_and_removal(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeRestoreProcess:
        def __init__(self) -> None:
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.stdin = self._FakeStdin()
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.wait = _with_aio(self._wait)

        class _FakeStdin:
            def write(self, data: bytes) -> None:
                _ = data

            def write_eof(self) -> None:
                return

            def drain(self) -> None:
                return

        def _wait(self) -> int:
            return 0

    class _FakeSnapshotSandbox:
        object_id = "sb-123"
        snapshot_directory: Any

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess:
            _ = kwargs
            assert command[:3] == ("tar", "xf", "-")
            return _FakeRestoreProcess()

    sandbox = _FakeSnapshotSandbox()
    mount = _RecordingMount()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "logical": mount,
                "logs/events.jsonl": File(content=b"skip", ephemeral=True),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_directory",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    def _snapshot_directory(path: str) -> str:
        assert path == "/workspace"
        return "snap-dir-123"

    sandbox.snapshot_directory = _with_aio(_snapshot_directory)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered[:2] == ["sh", "-lc"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered[:3] == ["rm", "-rf", "--"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    archive = await session.persist_workspace()

    assert archive.read() == modal_module._encode_snapshot_directory_ref(snapshot_id="snap-dir-123")
    assert commands[0][0:2] == ["sh", "-lc"]
    assert "logs/events.jsonl" in commands[0][2]
    assert "logical" not in commands[0][2]
    assert "/tmp/openai-agents/session-state/" in commands[0][2]
    assert "modal-snapshot-directory-ephemeral.tar" in commands[0][2]
    assert "for rel in logs/events.jsonl;" in commands[0][2]
    assert "tar cf" in commands[0][2]
    assert "-T -" in commands[0][2]
    assert commands[1] == ["rm", "-rf", "--", "/workspace/logs/events.jsonl"]
    assert commands[2][0:2] == ["sh", "-lc"]
    assert "modal-snapshot-directory-ephemeral.tar" in commands[2][2]
    assert "tar xf" in commands[2][2]


@pytest.mark.asyncio
async def test_modal_snapshot_directory_backup_failure_aborts_before_removing_ephemeral_paths(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeSnapshotSandbox:
        object_id = "sb-123"
        snapshot_directory: Any

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "tmp.txt": File(content=b"skip", ephemeral=True),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_directory",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    def _snapshot_directory(_path: str) -> str:
        raise AssertionError("snapshot_directory should not run after backup failure")

    sandbox.snapshot_directory = _with_aio(_snapshot_directory)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered[:2] == ["sh", "-lc"]:
            return ExecResult(stdout=b"", stderr=b"mkdir failed", exit_code=1)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert exc_info.value.context["reason"] == "snapshot_directory_ephemeral_backup_failed"
    assert exc_info.value.context["exit_code"] == 1
    assert exc_info.value.context["stderr"] == "mkdir failed"
    assert commands == [
        [
            "sh",
            "-lc",
            "mkdir -p -- /tmp/openai-agents/session-state/"
            f"{session.state.session_id.hex} && "
            "cd -- /workspace && "
            '{ for rel in tmp.txt; do if [ -e "$rel" ]; '
            "then printf '%s\\n' \"$rel\"; fi; done; } | tar cf "
            f"/tmp/openai-agents/session-state/{session.state.session_id.hex}/"
            "modal-snapshot-directory-ephemeral.tar -T - 2>/dev/null && test -f "
            f"/tmp/openai-agents/session-state/{session.state.session_id.hex}/"
            "modal-snapshot-directory-ephemeral.tar",
        ]
    ]


@pytest.mark.asyncio
async def test_modal_snapshot_directory_teardown_failure_restores_partial_cleanup(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)
    events: list[tuple[str, str]] = []

    class _FakeSnapshotSandbox:
        object_id = "sb-123"
        snapshot_directory: Any

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "tmp.txt": File(content=b"skip", ephemeral=True),
                "first": _RecordingMount(
                    mount_path=Path("actual-1"),
                    ephemeral=False,
                ).bind_events(events),
                "second": _RecordingMount(
                    mount_path=Path("actual-2"),
                    ephemeral=False,
                )
                .bind_events(events)
                .bind_teardown_error("teardown failed"),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_directory",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    def _snapshot_directory(_path: str) -> str:
        raise AssertionError("snapshot_directory should not run after teardown failure")

    sandbox.snapshot_directory = _with_aio(_snapshot_directory)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered[:2] == ["sh", "-lc"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered[:3] == ["rm", "-rf", "--"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert isinstance(exc_info.value.cause, RuntimeError)
    assert str(exc_info.value.cause) == "teardown failed"
    assert events == [("unmount", "/workspace/actual-1"), ("mount", "/workspace/actual-1")]
    assert commands[0][0:2] == ["sh", "-lc"]
    assert "for rel in tmp.txt;" in commands[0][2]
    assert commands[1] == ["rm", "-rf", "--", "/workspace/tmp.txt"]
    assert commands[2][0:2] == ["sh", "-lc"]
    assert "modal-snapshot-directory-ephemeral.tar" in commands[2][2]
    assert "tar xf" in commands[2][2]


@pytest.mark.asyncio
async def test_modal_snapshot_directory_tolerates_missing_ephemeral_paths_in_backup_command(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeSnapshotSandbox:
        object_id = "sb-123"
        snapshot_directory: Any

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "tmp.txt": File(content=b"skip", ephemeral=True),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_directory",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    def _snapshot_directory(path: str) -> str:
        assert path == "/workspace"
        return "snap-dir-123"

    sandbox.snapshot_directory = _with_aio(_snapshot_directory)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered[:2] == ["sh", "-lc"]:
            if "for rel in tmp.txt;" in rendered[2]:
                assert "-T -" in rendered[2]
            else:
                assert "tar xf" in rendered[2]
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered[:3] == ["rm", "-rf", "--"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    monkeypatch.setattr(session, "exec", _fake_exec)

    archive = await session.persist_workspace()

    assert archive.read() == modal_module._encode_snapshot_directory_ref(snapshot_id="snap-dir-123")
    assert commands[1] == ["rm", "-rf", "--", "/workspace/tmp.txt"]


@pytest.mark.asyncio
async def test_modal_snapshot_unexpected_return_restores_live_session_before_raising(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeRestoreProcess:
        def __init__(self, owner: Any) -> None:
            self._owner = owner
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.stdin = self._FakeStdin(owner)
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.wait = _with_aio(self._wait)

        class _FakeStdin:
            def __init__(self, owner: Any) -> None:
                self._owner = owner
                self._buffer = bytearray()

            def write(self, data: bytes) -> None:
                self._buffer.extend(data)

            def write_eof(self) -> None:
                return

            def drain(self) -> None:
                return

        def _wait(self) -> int:
            self._owner.restore_payloads.append(bytes(self.stdin._buffer))
            return 0

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.restore_payloads: list[bytes] = []
            self.snapshot_filesystem = _with_aio(self._snapshot_filesystem)
            self.exec = _with_aio(self._exec)

        def _snapshot_filesystem(self) -> object:
            return object()

        def _exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess:
            _ = kwargs
            assert command == ("tar", "xf", "-", "-C", "/workspace")
            return _FakeRestoreProcess(self)

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "logical": _RecordingMount(
                    mount_path=Path("actual"),
                    ephemeral=False,
                ),
                "tmp.txt": File(content=b"ephemeral", ephemeral=True),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_filesystem",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []
    events: list[tuple[str, str]] = []

    def _snapshot_filesystem() -> object:
        events.append(("snapshot", ""))
        return object()

    sandbox.snapshot_filesystem = _with_aio(_snapshot_filesystem)

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered == [
            "sh",
            "-lc",
            "cd -- /workspace && (tar cf - -- tmp.txt 2>/dev/null || true)",
        ]:
            return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0)
        if rendered == ["rm", "-rf", "--", "/workspace/tmp.txt"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = call_timeout
        if getattr(fn, "__name__", "") == "snapshot_filesystem":
            events.append(("snapshot", ""))
        return fn(*args, **kwargs)

    monkeypatch.setattr(session, "exec", _fake_exec)
    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert exc_info.value.context == {
        "path": "/workspace",
        "reason": "snapshot_filesystem_unexpected_return",
        "type": "object",
    }
    assert sandbox.restore_payloads == [b"ephemeral-backup"]
    assert commands == [
        ["sh", "-lc", "cd -- /workspace && (tar cf - -- tmp.txt 2>/dev/null || true)"],
        ["rm", "-rf", "--", "/workspace/tmp.txt"],
    ]
    assert events == [("snapshot", "")]


@pytest.mark.asyncio
async def test_modal_snapshot_unexpected_return_skips_restore_for_empty_ephemeral_backup(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.snapshot_filesystem = _with_aio(self._snapshot_filesystem)
            self.exec = _with_aio(self._exec)

        def _snapshot_filesystem(self) -> object:
            return object()

        def _exec(self, *command: object, **kwargs: object) -> NoReturn:
            _ = kwargs
            raise AssertionError(f"restore should be skipped for empty backup: {command!r}")

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={"tmp.txt": File(content=b"ephemeral", ephemeral=True)},
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_filesystem",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if rendered == [
            "sh",
            "-lc",
            "cd -- /workspace && (tar cf - -- tmp.txt 2>/dev/null || true)",
        ]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        if rendered == ["rm", "-rf", "--", "/workspace/tmp.txt"]:
            return ExecResult(stdout=b"", stderr=b"", exit_code=0)
        raise AssertionError(f"unexpected command: {rendered!r}")

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = call_timeout
        return fn(*args, **kwargs)

    monkeypatch.setattr(session, "exec", _fake_exec)
    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert exc_info.value.context == {
        "path": "/workspace",
        "reason": "snapshot_filesystem_unexpected_return",
        "type": "object",
    }
    assert commands == [
        ["sh", "-lc", "cd -- /workspace && (tar cf - -- tmp.txt 2>/dev/null || true)"],
        ["rm", "-rf", "--", "/workspace/tmp.txt"],
    ]


@pytest.mark.asyncio
async def test_modal_tar_persist_uses_resolved_mount_paths_for_excludes(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "logical": GCSMount(
                    bucket="bucket",
                    mount_path=Path("actual"),
                    ephemeral=False,
                    mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
                )
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=None)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        return ExecResult(stdout=b"tar-bytes", stderr=b"", exit_code=0)

    monkeypatch.setattr(session, "exec", _fake_exec)

    archive = await session.persist_workspace()

    assert archive.read() == b"tar-bytes"
    assert commands == [
        [
            "tar",
            "cf",
            "-",
            "--exclude",
            "./actual",
            "-C",
            "/workspace",
            ".",
        ]
    ]


@pytest.mark.asyncio
async def test_modal_tar_persist_retries_wrapped_exec_transport_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=None)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        if len(commands) == 1:
            raise modal_module.ExecTransportError(
                command=tuple(rendered),
                message="modal transport failed",
            )
        return ExecResult(stdout=b"tar-bytes", stderr=b"", exit_code=0)

    monkeypatch.setattr(session, "exec", _fake_exec)

    archive = await session.persist_workspace()

    assert archive.read() == b"tar-bytes"
    assert commands == [
        ["tar", "cf", "-", "-C", "/workspace", "."],
        ["tar", "cf", "-", "-C", "/workspace", "."],
    ]


@pytest.mark.asyncio
async def test_modal_tar_persist_does_not_retry_wrapped_non_retryable_exec_transport_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=None)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        rendered = [str(part) for part in command]
        commands.append(rendered)
        raise modal_module.ExecTransportError(
            command=tuple(rendered),
            message="modal transport failed permanently",
            retryable=False,
        )

    monkeypatch.setattr(session, "exec", _fake_exec)

    with pytest.raises(WorkspaceArchiveReadError) as exc_info:
        await session.persist_workspace()

    assert str(exc_info.value) == "failed to read archive for path: /workspace"
    assert isinstance(exc_info.value.cause, modal_module.ExecTransportError)
    assert str(exc_info.value.cause) == "modal transport failed permanently"
    assert commands == [["tar", "cf", "-", "-C", "/workspace", "."]]


@pytest.mark.asyncio
async def test_modal_snapshot_filesystem_rejects_escaping_mount_paths_before_exec(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.snapshot_calls = 0

        def snapshot_filesystem(self) -> str:
            self.snapshot_calls += 1
            return "snap-123"

    sandbox = _FakeSnapshotSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "logical": GCSMount(
                    bucket="bucket",
                    mount_path=Path("/workspace/../../tmp"),
                    mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
                )
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
        workspace_persistence="snapshot_filesystem",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    commands: list[list[str]] = []

    async def _fake_exec(
        *command: object,
        timeout: float | None = None,
        shell: bool | list[str] = True,
        user: object | None = None,
    ) -> ExecResult:
        _ = (timeout, shell, user)
        commands.append([str(part) for part in command])
        raise AssertionError("exec() should not run for escaping mount paths")

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = (fn, args, call_timeout, kwargs)
        raise AssertionError("snapshot_filesystem() should not run for escaping mount paths")

    monkeypatch.setattr(session, "exec", _fake_exec)
    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    with pytest.raises(InvalidManifestPathError, match="must not escape root"):
        await session.persist_workspace()

    assert commands == []
    assert sandbox.snapshot_calls == 0


@pytest.mark.asyncio
async def test_modal_write_chunks_large_payload_before_draining(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeWaitResult:
        def __init__(self, *, stdout: bytes = b"", stderr: bytes = b"") -> None:
            self.stdout = types.SimpleNamespace(read=_with_aio(lambda: stdout))
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: stderr))
            self.wait = _with_aio(self._wait)

        def _wait(self) -> int:
            return 0

    class _FakeStdin:
        def __init__(self, *, limit: int) -> None:
            self._limit = limit
            self._buffer = bytearray()
            self.chunks: list[bytes] = []
            self.write_eof_calls = 0
            self.drain_calls = 0

        def write(self, data: bytes | bytearray | memoryview) -> None:
            rendered = bytes(data)
            if len(self._buffer) + len(rendered) > self._limit:
                raise BufferError("Buffer size exceed limit. Call drain to flush the buffer.")
            self._buffer.extend(rendered)

        def write_eof(self) -> None:
            self.write_eof_calls += 1

        def drain(self) -> None:
            self.chunks.append(bytes(self._buffer))
            self._buffer.clear()
            self.drain_calls += 1

    class _FakeProcess:
        def __init__(self, *, limit: int) -> None:
            self.stdin = _FakeStdin(limit=limit)
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.wait = _with_aio(self._wait)

        def _wait(self) -> int:
            return 0

    class _FakeSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.processes: list[_FakeProcess] = []
            self.commands: list[tuple[object, ...]] = []
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = kwargs
            self.commands.append(command)
            helper_path = str(RESOLVE_WORKSPACE_PATH_HELPER.install_path)
            if command[:3] == ("mkdir", "-p", "--"):
                return _FakeWaitResult()
            if (
                command[:2] == ("sh", "-c")
                and isinstance(command[2], str)
                and RESOLVE_WORKSPACE_PATH_HELPER.install_marker in command[2]
            ):
                return _FakeWaitResult()
            if command == ("test", "-x", helper_path):
                return _FakeWaitResult()
            if command and command[0] == helper_path:
                return _FakeWaitResult(stdout=b"/workspace/nested/file.bin")
            process = _FakeProcess(limit=5)
            self.processes.append(process)
            return process

    sandbox = _FakeSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    monkeypatch.setattr(modal_module, "_MODAL_STDIN_CHUNK_SIZE", 5)

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = call_timeout
        return fn(*args, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    payload = b"abcdefghijklm"
    await session.write(Path("nested/file.bin"), io.BytesIO(payload))

    assert sandbox.commands[-2:] == [
        ("mkdir", "-p", "--", "/workspace/nested"),
        ("sh", "-lc", "cat > /workspace/nested/file.bin"),
    ]
    assert len(sandbox.processes) == 1
    assert sandbox.processes[0].stdin.chunks == [b"abcde", b"fghij", b"klm", b""]
    assert sandbox.processes[0].stdin.write_eof_calls == 1
    assert sandbox.processes[0].stdin.drain_calls == 4


@pytest.mark.asyncio
async def test_modal_hydrate_tar_chunks_large_payload_before_draining(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeWaitResult:
        def __init__(self) -> None:
            self.wait = _with_aio(self._wait)

        def _wait(self) -> int:
            return 0

    class _FakeStdin:
        def __init__(self, *, limit: int) -> None:
            self._limit = limit
            self._buffer = bytearray()
            self.chunks: list[bytes] = []
            self.write_eof_calls = 0
            self.drain_calls = 0

        def write(self, data: bytes | bytearray | memoryview) -> None:
            rendered = bytes(data)
            if len(self._buffer) + len(rendered) > self._limit:
                raise BufferError("Buffer size exceed limit. Call drain to flush the buffer.")
            self._buffer.extend(rendered)

        def write_eof(self) -> None:
            self.write_eof_calls += 1

        def drain(self) -> None:
            self.chunks.append(bytes(self._buffer))
            self._buffer.clear()
            self.drain_calls += 1

    class _FakeProcess:
        def __init__(self, *, limit: int) -> None:
            self.stdin = _FakeStdin(limit=limit)
            _set_aio_attr(self.stdin, "drain", self.stdin.drain)
            self.stderr = types.SimpleNamespace(read=_with_aio(lambda: b""))
            self.wait = _with_aio(self._wait)

        def _wait(self) -> int:
            return 0

    class _FakeSandbox:
        object_id = "sb-123"

        def __init__(self) -> None:
            self.processes: list[_FakeProcess] = []
            self.commands: list[tuple[object, ...]] = []
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = kwargs
            self.commands.append(command)
            if command[:3] == ("mkdir", "-p", "--"):
                return _FakeWaitResult()
            process = _FakeProcess(limit=7)
            self.processes.append(process)
            return process

    sandbox = _FakeSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)
    monkeypatch.setattr(modal_module, "_MODAL_STDIN_CHUNK_SIZE", 7)

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        _ = call_timeout
        return fn(*args, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    tar_payload = io.BytesIO()
    with tarfile.open(fileobj=tar_payload, mode="w") as tar:
        info = tarfile.TarInfo(name="large.txt")
        contents = b"abcdefghijklmno"
        info.size = len(contents)
        tar.addfile(info, io.BytesIO(contents))
    tar_payload.seek(0)

    await session.hydrate_workspace(tar_payload)

    assert sandbox.commands == [
        ("mkdir", "-p", "--", "/workspace"),
        ("tar", "xf", "-", "-C", "/workspace"),
    ]
    assert len(sandbox.processes) == 1
    assert b"".join(sandbox.processes[0].stdin.chunks[:-1]) == tar_payload.getvalue()
    assert sandbox.processes[0].stdin.write_eof_calls == 1
    assert sandbox.processes[0].stdin.drain_calls >= 2


@pytest.mark.asyncio
async def test_modal_snapshot_filesystem_restore_preserves_exposed_ports(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        workspace_persistence="snapshot_filesystem",
        exposed_ports=(8765,),
        idle_timeout=60,
    )
    session = modal_module.ModalSandboxSession.from_state(state)
    call_names: list[str] = []
    call_timeouts: list[float | None] = []

    real_call_modal = session._call_modal  # noqa: SLF001

    async def _fake_call_modal(
        fn: Callable[..., object],
        *args: object,
        call_timeout: float | None = None,
        **kwargs: object,
    ) -> object:
        call_names.append(getattr(fn, "__name__", ""))
        call_timeouts.append(call_timeout)
        return await real_call_modal(fn, *args, call_timeout=call_timeout, **kwargs)

    monkeypatch.setattr(session, "_call_modal", _fake_call_modal)

    await session.hydrate_workspace(
        io.BytesIO(modal_module._encode_snapshot_filesystem_ref(snapshot_id="snap-123"))
    )

    assert create_calls
    assert create_calls[0]["encrypted_ports"] == (8765,)
    assert create_calls[0]["idle_timeout"] == 60
    assert sys.modules["modal"].Image.from_id_calls == ["snap-123"]
    assert call_names == []
    assert call_timeouts == []


@pytest.mark.asyncio
async def test_modal_snapshot_directory_restore_preserves_exposed_ports(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        workspace_persistence="snapshot_directory",
        exposed_ports=(8765,),
    )
    session = modal_module.ModalSandboxSession.from_state(state)

    await session.hydrate_workspace(
        io.BytesIO(modal_module._encode_snapshot_directory_ref(snapshot_id="snap-dir-123"))
    )

    assert create_calls
    assert create_calls[0]["encrypted_ports"] == (8765,)
    assert session._sandbox is not None  # noqa: SLF001
    assert session._sandbox.mount_image_calls == [("/workspace", "snap-dir-123")]  # noqa: SLF001
    assert sys.modules["modal"].Image.from_id_calls == ["snap-dir-123"]


@pytest.mark.asyncio
async def test_modal_snapshot_directory_restore_reactivates_durable_workspace_mounts(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)
    events: list[tuple[str, str]] = []

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "remote": _RecordingMount(
                    mount_path=Path("actual"),
                    ephemeral=False,
                ).bind_events(events)
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        workspace_persistence="snapshot_directory",
        exposed_ports=(8765,),
    )
    session = modal_module.ModalSandboxSession.from_state(state)

    await session.hydrate_workspace(
        io.BytesIO(modal_module._encode_snapshot_directory_ref(snapshot_id="snap-dir-123"))
    )

    assert create_calls
    assert session._sandbox is not None  # noqa: SLF001
    assert session._sandbox.mount_image_calls == [("/workspace", "snap-dir-123")]  # noqa: SLF001
    assert events == [("mount", "/workspace/actual")]


@pytest.mark.asyncio
async def test_modal_snapshot_directory_persist_only_detaches_durable_workspace_mounts(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)
    events: list[tuple[str, str]] = []

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            root="/workspace",
            entries={
                "inside": _RecordingMount(
                    mount_path=Path("actual"),
                    ephemeral=False,
                ).bind_events(events),
                "outside": _RecordingMount(
                    mount_path=Path("/mnt/remote"),
                    ephemeral=False,
                ).bind_events(events),
            },
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        workspace_persistence="snapshot_directory",
        exposed_ports=(8765,),
    )
    session = modal_module.ModalSandboxSession.from_state(state)

    archive = await session.persist_workspace()

    assert create_calls
    assert session._sandbox is not None  # noqa: SLF001
    assert archive.read() == modal_module._encode_snapshot_directory_ref(snapshot_id="im-123")
    assert events == [("unmount", "/workspace/actual"), ("mount", "/workspace/actual")]


@pytest.mark.asyncio
async def test_modal_create_allows_snapshot_filesystem_with_modal_cloud_bucket_mounts(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        manifest=Manifest(
            entries={
                "remote": S3Mount(
                    bucket="bucket",
                    mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
                )
            }
        ),
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            workspace_persistence="snapshot_filesystem",
        ),
    )

    assert create_calls
    volumes = cast(dict[str, object], create_calls[0]["volumes"])
    assert volumes.keys() == {"/workspace/remote"}


@pytest.mark.asyncio
async def test_modal_snapshot_filesystem_falls_back_to_tar_for_non_detachable_mounts(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeSnapshotSandbox:
        object_id = "sb-123"

        def snapshot_filesystem(self) -> str:
            raise AssertionError("snapshot_filesystem() should not run for non-detachable mounts")

    session = modal_module.ModalSandboxSession.from_state(
        modal_module.ModalSandboxSessionState(
            manifest=Manifest(
                root="/workspace",
                entries={
                    "remote": S3Mount(
                        bucket="bucket",
                        mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
                    )
                },
            ),
            snapshot=modal_module.resolve_snapshot(None, "snapshot"),
            app_name="sandbox-tests",
            sandbox_id="sb-123",
            workspace_persistence="snapshot_filesystem",
        ),
        sandbox=_FakeSnapshotSandbox(),
    )

    async def _fake_tar_persist() -> io.BytesIO:
        return io.BytesIO(b"tar-fallback")

    monkeypatch.setattr(session, "_persist_workspace_via_tar", _fake_tar_persist)

    archive = await session.persist_workspace()

    assert archive.read() == b"tar-fallback"


@pytest.mark.asyncio
async def test_modal_create_rejects_snapshot_directory_with_cloud_bucket_mount_under_workspace(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    with pytest.raises(
        MountConfigError,
        match=(
            "snapshot_directory is not supported when a Modal cloud bucket mount "
            "lives at or under the workspace root"
        ),
    ):
        await client.create(
            manifest=Manifest(
                entries={
                    "remote": S3Mount(
                        bucket="bucket",
                        mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
                    )
                }
            ),
            options=modal_module.ModalSandboxClientOptions(
                app_name="sandbox-tests",
                workspace_persistence="snapshot_directory",
            ),
        )

    assert create_calls == []


@pytest.mark.asyncio
async def test_modal_create_allows_snapshot_directory_with_cloud_bucket_mount_outside_workspace(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, create_calls, _registry_tags = _load_modal_module(monkeypatch)

    client = modal_module.ModalSandboxClient()
    await client.create(
        manifest=Manifest(
            entries={
                "remote": S3Mount(
                    bucket="bucket",
                    mount_path=Path("/mnt/remote"),
                    mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
                )
            }
        ),
        options=modal_module.ModalSandboxClientOptions(
            app_name="sandbox-tests",
            workspace_persistence="snapshot_directory",
        ),
    )

    assert create_calls
    volumes = cast(dict[str, object], create_calls[0]["volumes"])
    assert volumes.keys() == {"/mnt/remote"}


@pytest.mark.asyncio
async def test_modal_clear_workspace_root_on_resume_preserves_nested_cloud_bucket_mounts(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(
            entries={
                "a/b": S3Mount(
                    bucket="bucket",
                    mount_strategy=modal_module.ModalCloudBucketMountStrategy(),
                ),
            }
        ),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
    )
    session = modal_module.ModalSandboxSession.from_state(state)
    ls_calls: list[Path] = []
    rm_calls: list[tuple[Path, bool]] = []

    async def _fake_ls(path: Path | str) -> list[object]:
        rendered = Path(path)
        ls_calls.append(rendered)
        if rendered == Path("/workspace"):
            return [
                types.SimpleNamespace(path="/workspace/a", kind=EntryKind.DIRECTORY),
                types.SimpleNamespace(path="/workspace/root.txt", kind=EntryKind.FILE),
            ]
        if rendered == Path("/workspace/a"):
            return [
                types.SimpleNamespace(path="/workspace/a/b", kind=EntryKind.DIRECTORY),
                types.SimpleNamespace(path="/workspace/a/local.txt", kind=EntryKind.FILE),
            ]
        raise AssertionError(f"unexpected ls path: {rendered}")

    async def _fake_rm(path: Path | str, *, recursive: bool = False) -> None:
        rm_calls.append((Path(path), recursive))

    monkeypatch.setattr(session, "ls", _fake_ls)
    monkeypatch.setattr(session, "rm", _fake_rm)

    await session._clear_workspace_root_on_resume()  # noqa: SLF001

    assert ls_calls == [Path("/workspace"), Path("/workspace/a")]
    assert rm_calls == [
        (Path("/workspace/a/local.txt"), True),
        (Path("/workspace/root.txt"), True),
    ]


@pytest.mark.asyncio
async def test_modal_pty_start_and_write_stdin(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeStream:
        def __init__(self, chunks: list[bytes]) -> None:
            self._chunks = chunks
            self._chunk_event = asyncio.Event()
            if self._chunks:
                self._chunk_event.set()
            self.read = _with_aio(self._read)

        def __aiter__(self) -> _FakeStream:
            return self

        async def __anext__(self) -> bytes:
            while not self._chunks:
                self._chunk_event.clear()
                await self._chunk_event.wait()
            chunk = self._chunks.pop(0)
            if not self._chunks:
                self._chunk_event.clear()
            return chunk

        def append(self, chunk: bytes) -> None:
            self._chunks.append(chunk)
            self._chunk_event.set()

        def _read(self, size: int | None = None) -> bytes:
            if size is None:
                raise AssertionError("PTY polling should not call read() with no size")
            if self._chunks:
                return self._chunks.pop(0)
            return b""

    class _FakeStdin:
        def __init__(self, stdout: _FakeStream) -> None:
            self.writes: list[bytes] = []
            self._stdout = stdout
            self.write = _with_aio(self._write)
            self.drain = _with_aio(lambda: None)

        def _write(self, payload: bytes) -> None:
            self.writes.append(payload)
            if payload == b"5 + 5\n":
                self._stdout.append(b"10\n")

    class _FakeProcess:
        def __init__(self) -> None:
            self.stdout = _FakeStream([b">>> "])
            self.stderr = _FakeStream([])
            self.stdin = _FakeStdin(self.stdout)
            self.poll = _with_aio(lambda: None)
            self.terminate = _with_aio(lambda: None)

    class _FakeSandbox:
        object_id = "sb-pty"

        def __init__(self) -> None:
            self.process = _FakeProcess()
            self.exec_calls: list[tuple[tuple[object, ...], dict[str, object]]] = []
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            self.exec_calls.append((command, kwargs))
            return self.process

    sandbox = _FakeSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)

    started = await session.pty_exec_start("python3", shell=False, tty=True, yield_time_s=0.05)

    assert started.process_id is not None
    assert b">>>" in started.output
    assert sandbox.exec_calls == [
        (("python3",), {"text": False, "timeout": None, "pty": True}),
    ]

    updated = await session.pty_write_stdin(
        session_id=started.process_id,
        chars="5 + 5\n",
        yield_time_s=0.05,
    )

    assert updated.process_id == started.process_id
    assert b"10" in updated.output
    assert sandbox.process.stdin.writes == [b"5 + 5\n"]

    await session.pty_terminate_all()


@pytest.mark.asyncio
async def test_modal_pty_start_drains_all_buffered_output_after_exit(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeStream:
        def __init__(self, chunks: list[bytes]) -> None:
            self._chunks = chunks
            self.read = _with_aio(self._read)

        def __aiter__(self) -> _FakeStream:
            return self

        async def __anext__(self) -> bytes:
            if self._chunks:
                return self._chunks.pop(0)
            raise StopAsyncIteration

        def _read(self, _size: int | None = None) -> bytes:
            raise AssertionError("PTY output collection should use stream iteration")

    class _FakeProcess:
        def __init__(self) -> None:
            self.stdout = _FakeStream([b"out-1", b"out-2", b"out-3"])
            self.stderr = _FakeStream([b"err-1", b"err-2"])
            self.poll = _with_aio(lambda: 0)
            self.terminate = _with_aio(lambda: None)

    class _FakeSandbox:
        object_id = "sb-exited"

        def __init__(self) -> None:
            self.process = _FakeProcess()
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            return self.process

    sandbox = _FakeSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)

    started = await session.pty_exec_start("python3", shell=False, tty=True, yield_time_s=0.05)

    assert started.process_id is None
    assert started.exit_code == 0
    assert started.output == b"out-1err-1out-2out-3err-2"


@pytest.mark.asyncio
async def test_modal_pty_start_wraps_startup_failures(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FailingSandbox:
        object_id = "sb-fail"

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            raise FileNotFoundError("missing-shell")

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-fail",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=_FailingSandbox())

    with pytest.raises(modal_module.ExecTransportError) as exc_info:
        await session.pty_exec_start("python3", shell=False, tty=True)
    assert str(exc_info.value) == "Modal exec failed: FileNotFoundError: missing-shell"
    assert exc_info.value.context["backend"] == "modal"
    assert exc_info.value.context["provider_error"] == "FileNotFoundError: missing-shell"


@pytest.mark.asyncio
async def test_modal_pty_start_marks_typed_not_found_non_retryable(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FailingSandbox:
        object_id = "sb-fail"

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            raise modal_module.modal.exception.NotFoundError("sandbox not found")

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-fail",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=_FailingSandbox())

    with pytest.raises(modal_module.ExecTransportError) as exc_info:
        await session.pty_exec_start("python3", shell=False, tty=True)

    assert exc_info.value.retryable is False
    assert exc_info.value.context["backend"] == "modal"
    assert exc_info.value.context["reason"] == "_FakeModalNotFoundError"
    assert exc_info.value.context["provider_error"] == "_FakeModalNotFoundError: sandbox not found"


@pytest.mark.asyncio
async def test_modal_pty_start_marks_typed_internal_failure_retryable(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FailingSandbox:
        object_id = "sb-fail"

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            raise modal_module.modal.exception.InternalFailure("internal failure")

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-fail",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=_FailingSandbox())

    with pytest.raises(modal_module.ExecTransportError) as exc_info:
        await session.pty_exec_start("python3", shell=False, tty=True)

    assert exc_info.value.retryable is True
    assert exc_info.value.context["backend"] == "modal"
    assert exc_info.value.context["reason"] == "_FakeModalInternalFailure"
    assert exc_info.value.context["provider_error"] == "_FakeModalInternalFailure: internal failure"


@pytest.mark.asyncio
async def test_modal_start_wraps_exec_details(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FailingSandbox:
        object_id = "sb-fail"

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)
            self.poll = _with_aio(lambda: None)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            raise FileNotFoundError("missing-shell")

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-fail",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=_FailingSandbox())

    with pytest.raises(modal_module.WorkspaceStartError) as exc_info:
        await session.start()

    assert str(exc_info.value) == (
        "failed to start session: Modal exec failed: FileNotFoundError: missing-shell"
    )
    assert exc_info.value.context["backend"] == "modal"


@pytest.mark.asyncio
async def test_modal_pty_start_maps_timeout_failures(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _TimeoutSandbox:
        object_id = "sb-timeout"

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            raise asyncio.TimeoutError()

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-timeout",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=_TimeoutSandbox())

    with pytest.raises(modal_module.ExecTimeoutError):
        await session.pty_exec_start("python3", shell=False, tty=True, timeout=2.0)


@pytest.mark.asyncio
async def test_modal_pty_start_maps_modal_exec_timeout_failures(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _TimeoutSandbox:
        object_id = "sb-timeout"

        def __init__(self) -> None:
            self.exec = _with_aio(self._exec)

        def _exec(self, *command: object, **kwargs: object) -> object:
            _ = (command, kwargs)
            raise modal_module.modal.exception.ExecTimeoutError("command timed out")

    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id="sb-timeout",
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=_TimeoutSandbox())

    with pytest.raises(modal_module.ExecTimeoutError) as exc_info:
        await session.pty_exec_start("python3", shell=False, tty=True, timeout=2.0)

    assert exc_info.value.retryable is False


@pytest.mark.asyncio
async def test_modal_pty_start_cleans_up_unregistered_process_on_cancellation(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch)

    class _FakeStream:
        def __init__(self) -> None:
            self.read = _with_aio(lambda: b"")

    class _FakeProcess:
        def __init__(self) -> None:
            self.stdout = _FakeStream()
            self.stderr = _FakeStream()
            self.poll = _with_aio(lambda: None)
            self.terminate_calls = 0
            self.terminate = _with_aio(self._terminate)

        def _terminate(self) -> None:
            self.terminate_calls += 1

    class _FakeSandbox:
        object_id = "sb-cancel"

        def __init__(self) -> None:
            self.process = _FakeProcess()
            self.exec = _with_aio(lambda *args, **kwargs: self.process)

    sandbox = _FakeSandbox()
    state = modal_module.ModalSandboxSessionState(
        manifest=Manifest(root="/workspace"),
        snapshot=modal_module.resolve_snapshot(None, "snapshot"),
        app_name="sandbox-tests",
        sandbox_id=sandbox.object_id,
    )
    session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox)

    async def _raise_cancelled() -> None:
        raise asyncio.CancelledError()

    monkeypatch.setattr(session, "_prune_pty_processes_if_needed", _raise_cancelled)

    with pytest.raises(asyncio.CancelledError):
        await session.pty_exec_start("python3", shell=False, tty=True)

    assert sandbox.process.terminate_calls == 1
    assert session._pty_processes == {}  # noqa: SLF001
