Files
2026-07-13 12:39:17 +08:00

215 lines
6.4 KiB
Python

from pathlib import Path
import pytest
from agents.sandbox.entries import (
Dir,
File,
GCSMount,
InContainerMountStrategy,
MountpointMountPattern,
)
from agents.sandbox.errors import InvalidManifestPathError
from agents.sandbox.manifest import Manifest
from agents.sandbox.manifest_render import _truncate_manifest_description
def test_manifest_rejects_nested_child_paths_that_escape_workspace() -> None:
manifest = Manifest(
entries={
"safe": Dir(
children={
"../outside.txt": File(content=b"nope"),
}
)
}
)
with pytest.raises(InvalidManifestPathError, match="must not escape root"):
manifest.validated_entries()
def test_manifest_rejects_nested_absolute_child_paths() -> None:
manifest = Manifest(
entries={
"safe": Dir(
children={
"/tmp/outside.txt": File(content=b"nope"),
}
)
}
)
with pytest.raises(InvalidManifestPathError, match="must be relative"):
manifest.validated_entries()
def test_manifest_rejects_windows_drive_absolute_entry_paths() -> None:
manifest = Manifest(entries={"C:\\tmp\\outside.txt": File(content=b"nope")})
with pytest.raises(InvalidManifestPathError) as exc_info:
manifest.validated_entries()
assert str(exc_info.value) == "manifest path must be relative: C:/tmp/outside.txt"
assert exc_info.value.context == {"rel": "C:/tmp/outside.txt", "reason": "absolute"}
def test_manifest_ephemeral_entry_paths_include_nested_children() -> None:
manifest = Manifest(
entries={
"dir": Dir(
children={
"keep.txt": File(content=b"keep"),
"tmp.txt": File(content=b"tmp", ephemeral=True),
}
)
}
)
assert manifest.ephemeral_entry_paths() == {Path("dir/tmp.txt")}
def test_manifest_ephemeral_persistence_paths_include_resolved_mount_targets() -> None:
manifest = Manifest(
root="/workspace",
entries={
"logical": GCSMount(
bucket="bucket",
mount_path=Path("actual"),
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
),
"dir": Dir(
children={
"tmp.txt": File(content=b"tmp", ephemeral=True),
}
),
},
)
assert manifest.ephemeral_persistence_paths() == {
Path("logical"),
Path("actual"),
Path("dir/tmp.txt"),
}
def test_manifest_ephemeral_mount_targets_sort_by_resolved_depth() -> None:
parent = GCSMount(
bucket="parent",
mount_path=Path("repo"),
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
)
child = GCSMount(
bucket="child",
mount_path=Path("repo/sub"),
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
)
manifest = Manifest(
root="/workspace",
entries={
"parent": parent,
"nested": Dir(children={"child": child}),
},
)
assert manifest.ephemeral_mount_targets() == [
(child, Path("/workspace/repo/sub")),
(parent, Path("/workspace/repo")),
]
def test_manifest_ephemeral_mount_targets_normalize_non_escaping_mount_paths() -> None:
mount = GCSMount(
bucket="bucket",
mount_path=Path("/workspace/repo/../actual"),
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
)
manifest = Manifest(root="/workspace", entries={"logical": mount})
assert manifest.ephemeral_mount_targets() == [
(mount, Path("/workspace/actual")),
]
assert manifest.ephemeral_persistence_paths() == {
Path("logical"),
Path("actual"),
}
def test_manifest_ephemeral_mount_targets_reject_escaping_mount_paths() -> None:
manifest = Manifest(
root="/workspace",
entries={
"logical": GCSMount(
bucket="bucket",
mount_path=Path("/workspace/../../tmp"),
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
),
},
)
with pytest.raises(InvalidManifestPathError, match="must not escape root"):
manifest.ephemeral_mount_targets()
with pytest.raises(InvalidManifestPathError, match="must not escape root"):
manifest.ephemeral_persistence_paths()
def test_manifest_ephemeral_mount_targets_reject_windows_drive_mount_path() -> None:
manifest = Manifest(
root="/workspace",
entries={
"logical": GCSMount(
bucket="bucket",
mount_path=Path("C:\\tmp\\mount"),
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
),
},
)
with pytest.raises(InvalidManifestPathError) as exc_info:
manifest.ephemeral_mount_targets()
assert str(exc_info.value) == "manifest path must be relative: C:/tmp/mount"
assert exc_info.value.context == {"rel": "C:/tmp/mount", "reason": "absolute"}
def test_manifest_describe_preserves_tree_rendering_after_renderer_extract() -> None:
manifest = Manifest(
root="/workspace",
entries={
"repo": Dir(
description="project root",
children={
"README.md": File(content=b"hi", description="overview"),
},
),
"data": GCSMount(
bucket="bucket",
description="shared data",
mount_strategy=InContainerMountStrategy(pattern=MountpointMountPattern()),
),
},
)
description = manifest.describe(depth=2)
assert description.startswith("/workspace\n")
assert "data/" in description
assert "/workspace/data" in description
assert "repo/" in description
assert "/workspace/repo/README.md" in description
def test_manifest_description_truncation_respects_short_limits() -> None:
description = "0123456789" * 20
for max_chars in range(0, 40):
truncated = _truncate_manifest_description(description, max_chars)
assert len(truncated) <= max_chars
def test_manifest_description_truncation_preserves_unbounded_description() -> None:
description = "short"
assert _truncate_manifest_description(description, None) == description