libs/standard-tests/langchain_tests/integration_tests/sandboxes.py PYTHON 1,888 lines View on github.com → Search inside
1"""Integration tests for the deepagents sandbox backend abstraction.23Implementers should subclass this test suite and provide a fixture that returns a4clean `SandboxBackendProtocol` instance.56Example:7```python8from __future__ import annotations910from collections.abc import Iterator1112import pytest13from deepagents.backends.protocol import SandboxBackendProtocol14from langchain_tests.integration_tests import SandboxIntegrationTests1516from my_pkg import make_sandbox171819class TestMySandboxStandard(SandboxIntegrationTests):20    @pytest.fixture(scope="class")21    def sandbox(self) -> Iterator[SandboxBackendProtocol]:22        backend = make_sandbox()23        try:24            yield backend25        finally:26            backend.delete()27```2829"""3031# ruff: noqa: E402, S1083233from __future__ import annotations3435import asyncio36import base6437import shlex38import sys39from abc import abstractmethod40from typing import TYPE_CHECKING4142import pytest4344deepagents = pytest.importorskip("deepagents")4546from deepagents.backends.protocol import (47    ExecuteResponse,48    FileDownloadResponse,49    FileUploadResponse,50    ReadResult,51    SandboxBackendProtocol,52)5354from langchain_tests.base import BaseStandardTests5556if TYPE_CHECKING:57    from collections.abc import Iterator585960def _quote(path: str) -> str:61    return shlex.quote(path)626364class SandboxIntegrationTests(BaseStandardTests):65    """Standard integration tests for a `SandboxBackendProtocol` implementation."""6667    @property68    def sandbox_root_dir(self) -> str:69        """Base directory used by sandbox file-operation tests."""70        return "/tmp/test_sandbox_ops/"7172    def sandbox_path(self, relative_path: str, *, root_dir: str | None = None) -> str:73        """Build a path under the configured sandbox test directory."""74        root = root_dir or self.sandbox_root_dir75        return f"{root.rstrip('/')}/{relative_path.lstrip('/')}"7677    @pytest.fixture(scope="class")78    def sandbox_backend(79        self, sandbox: SandboxBackendProtocol80    ) -> SandboxBackendProtocol:81        """Provide the sandbox backend under test.8283        Resets the shared test directory before yielding.84        """85        return sandbox8687    @abstractmethod88    @pytest.fixture(scope="class")89    def sandbox(self) -> Iterator[SandboxBackendProtocol]:90        """Yield a clean sandbox backend and tear it down after the class."""9192    @property93    def has_sync(self) -> bool:94        """Whether the sandbox supports sync methods."""95        return True9697    @property98    def has_async(self) -> bool:99        """Whether the sandbox supports async methods."""100        return True101102    @pytest.fixture(autouse=True)103    def sandbox_test_root(self, request: pytest.FixtureRequest) -> str:104        """Create an isolated sandbox root directory for each test case."""105        if not self.has_sync:106            pytest.skip("Sync tests not supported.")107        node_name = request.node.name.replace("/", "_").replace(" ", "_")108        return self.sandbox_path(node_name)109110    def test_write_new_file(111        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str112    ) -> None:113        """Write a new file and verify it can be read back via command execution."""114        if not self.has_sync:115            pytest.skip("Sync tests not supported.")116        test_path = self.sandbox_path("new_file.txt", root_dir=sandbox_test_root)117        content = "Hello, sandbox!\nLine 2\nLine 3"118        result = sandbox_backend.write(test_path, content)119        assert result.error is None120        assert result.path == test_path121        exec_result = sandbox_backend.execute(f"cat {test_path}")122        assert exec_result.output.strip() == content123124    def test_read_basic_file(125        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str126    ) -> None:127        """Write a file and verify `read()` returns expected contents."""128        if not self.has_sync:129            pytest.skip("Sync tests not supported.")130        test_path = self.sandbox_path("read_test.txt", root_dir=sandbox_test_root)131        content = "Line 1\nLine 2\nLine 3"132        sandbox_backend.write(test_path, content)133        result = sandbox_backend.read(test_path)134        assert isinstance(result, ReadResult)135        assert result.error is None136        assert result.file_data is not None137        assert all(138            line in result.file_data["content"]139            for line in ("Line 1", "Line 2", "Line 3")140        )141142    def test_read_binary_file(143        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str144    ) -> None:145        """Upload a binary file and verify `read()` returns base64-encoded content."""146        if not self.has_sync:147            pytest.skip("Sync tests not supported.")148        test_path = self.sandbox_path("binary.png", root_dir=sandbox_test_root)149        raw_bytes = bytes(range(256))150        sandbox_backend.upload_files([(test_path, raw_bytes)])151        result = sandbox_backend.read(test_path)152        assert isinstance(result, ReadResult)153        assert result.error is None154        assert result.file_data is not None155        assert result.file_data["encoding"] == "base64"156        assert base64.b64decode(result.file_data["content"]) == raw_bytes157158    def test_read_binary_file_100_kib(159        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str160    ) -> None:161        """Read should return base64 content for a 100 KiB binary file."""162        if not self.has_sync:163            pytest.skip("Sync tests not supported.")164165        test_path = self.sandbox_path("binary_100kib.png", root_dir=sandbox_test_root)166        chunk = bytes(range(256))167        raw_bytes = chunk * 400168169        sandbox_backend.upload_files([(test_path, raw_bytes)])170        result = sandbox_backend.read(test_path)171172        assert isinstance(result, ReadResult)173        assert result.error is None174        assert result.file_data is not None175        assert result.file_data["encoding"] == "base64"176        assert base64.b64decode(result.file_data["content"]) == raw_bytes177178    def test_read_binary_file_1_mib_returns_error(179        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str180    ) -> None:181        """Read should error when a binary file exceeds the preview size limit."""182        if not self.has_sync:183            pytest.skip("Sync tests not supported.")184185        test_path = self.sandbox_path("binary_1mib.png", root_dir=sandbox_test_root)186        chunk = bytes(range(256))187        raw_bytes = chunk * 4096188189        sandbox_backend.upload_files([(test_path, raw_bytes)])190        result = sandbox_backend.read(test_path)191192        assert isinstance(result, ReadResult)193        assert result.file_data is None194        expected_error = (195            f"File '{test_path}': Binary file exceeds maximum preview size of "196            "512000 bytes"197        )198        assert result.error == expected_error199200    def test_execute_large_stdout_payload(201        self, sandbox_backend: SandboxBackendProtocol202    ) -> None:203        """Execute should handle a command that emits about 500 KiB of stdout."""204        if not self.has_sync:205            pytest.skip("Sync tests not supported.")206207        command = "python -c \"import sys; sys.stdout.write('x' * (500 * 1024))\""208        result = sandbox_backend.execute(command)209210        assert result.exit_code == 0211        assert result.truncated is False212        assert len(result.output) >= 500 * 1024213        assert result.output.startswith("x")214215    def test_edit_single_occurrence(216        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str217    ) -> None:218        """Edit a file and assert exactly one occurrence was replaced."""219        if not self.has_sync:220            pytest.skip("Sync tests not supported.")221        test_path = self.sandbox_path("edit_single.txt", root_dir=sandbox_test_root)222        content = "Hello world\nGoodbye world\nHello again"223        sandbox_backend.write(test_path, content)224        result = sandbox_backend.edit(test_path, "Goodbye", "Farewell")225        assert result.error is None226        assert result.occurrences == 1227        file_result = sandbox_backend.read(test_path)228        assert isinstance(file_result, ReadResult)229        assert file_result.error is None230        assert file_result.file_data is not None231        assert "Farewell world" in file_result.file_data["content"]232        assert "Goodbye" not in file_result.file_data["content"]233234    def test_ls_lists_files(235        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str236    ) -> None:237        """Create files and verify `ls()` lists them."""238        if not self.has_sync:239            pytest.skip("Sync tests not supported.")240        sandbox_backend.write(241            self.sandbox_path("a.txt", root_dir=sandbox_test_root), "a"242        )243        sandbox_backend.write(244            self.sandbox_path("b.txt", root_dir=sandbox_test_root), "b"245        )246        result = sandbox_backend.ls(sandbox_test_root)247        assert result.error is None248        assert result.entries is not None249        paths = sorted([i["path"] for i in result.entries])250        assert self.sandbox_path("a.txt", root_dir=sandbox_test_root) in paths251        assert self.sandbox_path("b.txt", root_dir=sandbox_test_root) in paths252253    def test_glob(254        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str255    ) -> None:256        """Create files and verify `glob()` returns expected matches."""257        if not self.has_sync:258            pytest.skip("Sync tests not supported.")259        sandbox_backend.write(260            self.sandbox_path("x.py", root_dir=sandbox_test_root), "print('x')"261        )262        sandbox_backend.write(263            self.sandbox_path("y.txt", root_dir=sandbox_test_root), "y"264        )265        result = sandbox_backend.glob("*.py", path=sandbox_test_root)266        assert result.error is None267        assert result.matches is not None268        assert [m["path"] for m in result.matches] == ["x.py"]269270    def test_grep_literal(271        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str272    ) -> None:273        """Verify `grep()` performs literal matching on special characters."""274        if not self.has_sync:275            pytest.skip("Sync tests not supported.")276        sandbox_backend.write(277            self.sandbox_path("grep.txt", root_dir=sandbox_test_root),278            "a (b)\nstr | int\n",279        )280        result = sandbox_backend.grep("str | int", path=sandbox_test_root)281        assert result.error is None282        assert result.matches is not None283        assert len(result.matches) > 0284        assert result.matches[0]["path"].endswith("/grep.txt")285        assert result.matches[0]["text"].strip() == "str | int"286287    def test_upload_single_file(288        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str289    ) -> None:290        """Upload one file and verify its contents on the sandbox."""291        if not self.has_sync:292            pytest.skip("Sync tests not supported.")293294        test_path = self.sandbox_path(295            "test_upload_single.txt", root_dir=sandbox_test_root296        )297        test_content = b"Hello, Sandbox!"298299        upload_responses = sandbox_backend.upload_files([(test_path, test_content)])300301        assert len(upload_responses) == 1302        assert upload_responses[0].path == test_path303        assert upload_responses[0].error is None304305        result = sandbox_backend.execute(f"cat {test_path}")306        assert result.output.strip() == test_content.decode()307308    def test_download_single_file(309        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str310    ) -> None:311        """Upload then download a file and verify bytes match."""312        if not self.has_sync:313            pytest.skip("Sync tests not supported.")314315        test_path = self.sandbox_path(316            "test_download_single.txt", root_dir=sandbox_test_root317        )318        test_content = b"Download test content"319320        sandbox_backend.upload_files([(test_path, test_content)])321322        download_responses = sandbox_backend.download_files([test_path])323324        assert len(download_responses) == 1325        assert download_responses[0].path == test_path326        assert download_responses[0].content == test_content327        assert download_responses[0].error is None328329    def test_upload_download_roundtrip(330        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str331    ) -> None:332        """Upload then download and verify bytes survive a roundtrip."""333        if not self.has_sync:334            pytest.skip("Sync tests not supported.")335336        test_path = self.sandbox_path("test_roundtrip.txt", root_dir=sandbox_test_root)337        test_content = b"Roundtrip test: special chars \n\t\r\x00"338339        upload_responses = sandbox_backend.upload_files([(test_path, test_content)])340        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]341342        download_responses = sandbox_backend.download_files([test_path])343        assert download_responses == [344            FileDownloadResponse(path=test_path, content=test_content, error=None)345        ]346347    def test_upload_multiple_files_order_preserved(348        self,349        sandbox_backend: SandboxBackendProtocol,350        sandbox_test_root: str,351    ) -> None:352        """Uploading multiple files should preserve input order in responses."""353        if not self.has_sync:354            pytest.skip("Sync tests not supported.")355356        files = [357            (358                self.sandbox_path("test_multi_1.txt", root_dir=sandbox_test_root),359                b"Content 1",360            ),361            (362                self.sandbox_path("test_multi_2.txt", root_dir=sandbox_test_root),363                b"Content 2",364            ),365            (366                self.sandbox_path("test_multi_3.txt", root_dir=sandbox_test_root),367                b"Content 3",368            ),369        ]370371        upload_responses = sandbox_backend.upload_files(files)372373        assert upload_responses == [374            FileUploadResponse(path=files[0][0], error=None),375            FileUploadResponse(path=files[1][0], error=None),376            FileUploadResponse(path=files[2][0], error=None),377        ]378379    def test_download_multiple_files_order_preserved(380        self,381        sandbox_backend: SandboxBackendProtocol,382        sandbox_test_root: str,383    ) -> None:384        """Downloading multiple files should preserve input order in responses."""385        if not self.has_sync:386            pytest.skip("Sync tests not supported.")387388        files = [389            (390                self.sandbox_path("test_batch_1.txt", root_dir=sandbox_test_root),391                b"Batch 1",392            ),393            (394                self.sandbox_path("test_batch_2.txt", root_dir=sandbox_test_root),395                b"Batch 2",396            ),397            (398                self.sandbox_path("test_batch_3.txt", root_dir=sandbox_test_root),399                b"Batch 3",400            ),401        ]402        sandbox_backend.upload_files(files)403404        paths = [p for p, _ in files]405        download_responses = sandbox_backend.download_files(paths)406407        assert download_responses == [408            FileDownloadResponse(path=files[0][0], content=files[0][1], error=None),409            FileDownloadResponse(path=files[1][0], content=files[1][1], error=None),410            FileDownloadResponse(path=files[2][0], content=files[2][1], error=None),411        ]412413    def test_upload_binary_content_roundtrip(414        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str415    ) -> None:416        """Upload and download binary bytes (0..255) without corruption."""417        if not self.has_sync:418            pytest.skip("Sync tests not supported.")419420        test_path = self.sandbox_path("binary_file.bin", root_dir=sandbox_test_root)421        test_content = bytes(range(256))422423        upload_responses = sandbox_backend.upload_files([(test_path, test_content)])424        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]425426        download_responses = sandbox_backend.download_files([test_path])427        assert download_responses == [428            FileDownloadResponse(path=test_path, content=test_content, error=None)429        ]430431    def test_upload_large_file_reports_expected_size(432        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str433    ) -> None:434        """Upload a ~10 MiB file, verify its size, then download it again."""435        if not self.has_sync:436            pytest.skip("Sync tests not supported.")437438        test_path = self.sandbox_path("large_upload.txt", root_dir=sandbox_test_root)439        chunk = b"0123456789abcdef" * 1024440        repeat_count = 640441        test_content = chunk * repeat_count442443        assert len(test_content) == 10 * 1024 * 1024444445        upload_responses = sandbox_backend.upload_files([(test_path, test_content)])446        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]447448        exec_result = sandbox_backend.execute(f"wc -c {_quote(test_path)}")449        assert exec_result.exit_code == 0450        assert str(len(test_content)) in exec_result.output451452        download_responses = sandbox_backend.download_files([test_path])453        assert download_responses == [454            FileDownloadResponse(path=test_path, content=test_content, error=None)455        ]456457    def test_download_error_file_not_found(458        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str459    ) -> None:460        """Downloading a missing file should return `error="file_not_found"`."""461        if not self.has_sync:462            pytest.skip("Sync tests not supported.")463464        missing_path = self.sandbox_path(465            "nonexistent_test_file.txt", root_dir=sandbox_test_root466        )467468        responses = sandbox_backend.download_files([missing_path])469470        assert responses == [471            FileDownloadResponse(472                path=missing_path, content=None, error="file_not_found"473            )474        ]475476    def test_download_error_is_directory(477        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str478    ) -> None:479        """Downloading a directory should fail with a reasonable error code."""480        if not self.has_sync:481            pytest.skip("Sync tests not supported.")482483        dir_path = self.sandbox_path("test_directory", root_dir=sandbox_test_root)484        sandbox_backend.execute(f"rm -rf {dir_path} && mkdir -p {dir_path}")485486        responses = sandbox_backend.download_files([dir_path])487488        assert len(responses) == 1489        assert responses[0].path == dir_path490        assert responses[0].content is None491        assert responses[0].error in {"is_directory", "file_not_found", "invalid_path"}492493    def test_download_error_permission_denied(494        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str495    ) -> None:496        """Downloading a chmod 000 file should fail with a reasonable error code."""497        if not self.has_sync:498            pytest.skip("Sync tests not supported.")499500        test_path = self.sandbox_path("test_no_read.txt", root_dir=sandbox_test_root)501        sandbox_backend.execute(502            f"rm -f {test_path} && echo secret > {test_path} && chmod 000 {test_path}"503        )504505        try:506            responses = sandbox_backend.download_files([test_path])507        finally:508            sandbox_backend.execute(f"chmod 644 {test_path} || true")509510        assert len(responses) == 1511        assert responses[0].path == test_path512        assert responses[0].content is None513        assert responses[0].error in {514            "permission_denied",515            "file_not_found",516            "invalid_path",517        }518519    def test_download_error_invalid_path_relative(520        self,521        sandbox_backend: SandboxBackendProtocol,522    ) -> None:523        """Downloading a relative path should fail with `error="invalid_path"`."""524        if not self.has_sync:525            pytest.skip("Sync tests not supported.")526527        responses = sandbox_backend.download_files(["relative/path.txt"])528529        assert responses == [530            FileDownloadResponse(531                path="relative/path.txt",532                content=None,533                error="invalid_path",534            )535        ]536537    def test_upload_missing_parent_dir_or_roundtrip(538        self,539        sandbox_backend: SandboxBackendProtocol,540        sandbox_test_root: str,541    ) -> None:542        """Uploading into a missing parent dir should error or roundtrip.543544        Some sandboxes auto-create parent directories; others return an error.545        """546        if not self.has_sync:547            pytest.skip("Sync tests not supported.")548549        dir_path = self.sandbox_path(550            "test_upload_missing_parent_dir", root_dir=sandbox_test_root551        )552        path = f"{dir_path}/deepagents_test_upload.txt"553        content = b"nope"554        sandbox_backend.execute(f"rm -rf {dir_path}")555556        responses = sandbox_backend.upload_files([(path, content)])557        assert len(responses) == 1558        assert responses[0].path == path559560        if responses[0].error is not None:561            assert responses[0].error in {562                "invalid_path",563                "permission_denied",564                "file_not_found",565            }566            return567568        download = sandbox_backend.download_files([path])569        assert download == [570            FileDownloadResponse(path=path, content=content, error=None)571        ]572573    def test_upload_relative_path_returns_invalid_path(574        self,575        sandbox_backend: SandboxBackendProtocol,576    ) -> None:577        """Uploading to a relative path should fail with `error="invalid_path"`."""578        if not self.has_sync:579            pytest.skip("Sync tests not supported.")580581        path = "relative_upload.txt"582        content = b"nope"583        responses = sandbox_backend.upload_files([(path, content)])584585        assert responses == [FileUploadResponse(path=path, error="invalid_path")]586587    def test_write_creates_parent_dirs(588        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str589    ) -> None:590        """Writing into a missing nested directory should succeed."""591        if not self.has_sync:592            pytest.skip("Sync tests not supported.")593594        test_path = self.sandbox_path(595            "deep/nested/dir/file.txt", root_dir=sandbox_test_root596        )597        content = "Nested file content"598599        result = sandbox_backend.write(test_path, content)600601        assert result.error is None602        assert result.path == test_path603        exec_result = sandbox_backend.execute(f"cat {_quote(test_path)}")604        assert exec_result.output.strip() == content605606    def test_write_existing_file_fails(607        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str608    ) -> None:609        """Writing to an existing file should return an error without overwriting."""610        if not self.has_sync:611            pytest.skip("Sync tests not supported.")612613        test_path = self.sandbox_path("existing.txt", root_dir=sandbox_test_root)614        sandbox_backend.write(test_path, "First content")615616        result = sandbox_backend.write(test_path, "Second content")617618        assert result.error is not None619        assert "already exists" in result.error.lower()620        exec_result = sandbox_backend.execute(f"cat {_quote(test_path)}")621        assert exec_result.output.strip() == "First content"622623    def test_write_special_characters(624        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str625    ) -> None:626        """Writing should preserve shell-sensitive characters exactly."""627        if not self.has_sync:628            pytest.skip("Sync tests not supported.")629630        test_path = self.sandbox_path("special.txt", root_dir=sandbox_test_root)631        content = (632            "Special chars: $VAR, `command`, $(subshell), 'quotes', \"quotes\"\n"633            "Tab\there\n"634            "Backslash: \\\\"635        )636637        result = sandbox_backend.write(test_path, content)638639        assert result.error is None640        exec_result = sandbox_backend.execute(f"cat {_quote(test_path)}")641        assert exec_result.output.strip() == content642643    def test_write_empty_file(644        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str645    ) -> None:646        """Writing empty content should still create the file."""647        if not self.has_sync:648            pytest.skip("Sync tests not supported.")649650        test_path = self.sandbox_path("empty.txt", root_dir=sandbox_test_root)651652        result = sandbox_backend.write(test_path, "")653654        assert result.error is None655        exec_result = sandbox_backend.execute(656            f"[ -f {_quote(test_path)} ] && echo exists || echo missing"657        )658        assert "exists" in exec_result.output659660    def test_write_path_with_spaces(661        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str662    ) -> None:663        """Writing should support file paths containing spaces."""664        if not self.has_sync:665            pytest.skip("Sync tests not supported.")666667        test_path = self.sandbox_path(668            "dir with spaces/file name.txt", root_dir=sandbox_test_root669        )670        content = "Content in file with spaces"671672        result = sandbox_backend.write(test_path, content)673674        assert result.error is None675        exec_result = sandbox_backend.execute(f"cat {_quote(test_path)}")676        assert exec_result.output.strip() == content677678    def test_write_unicode_content(679        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str680    ) -> None:681        """Writing should preserve unicode content."""682        if not self.has_sync:683            pytest.skip("Sync tests not supported.")684685        test_path = self.sandbox_path("unicode.txt", root_dir=sandbox_test_root)686        content = "Hello 👋 世界 مرحبا Привет 🌍\nLine with émojis 🎉"687688        result = sandbox_backend.write(test_path, content)689690        assert result.error is None691        exec_result = sandbox_backend.execute(f"cat {_quote(test_path)}")692        assert exec_result.output.strip() == content693694    def test_write_consecutive_slashes_in_path(695        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str696    ) -> None:697        """Writing should tolerate normalized paths with consecutive slashes."""698        if not self.has_sync:699            pytest.skip("Sync tests not supported.")700701        test_path = self.sandbox_path("file.txt", root_dir=sandbox_test_root)702        content = "Content"703704        result = sandbox_backend.write(test_path, content)705706        assert result.error is None707        exec_result = sandbox_backend.execute(f"cat {_quote(test_path)}")708        assert exec_result.output.strip() == content709710    def test_write_very_long_content(711        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str712    ) -> None:713        """Writing moderately long multi-line content should succeed."""714        if not self.has_sync:715            pytest.skip("Sync tests not supported.")716717        test_path = self.sandbox_path("very_long.txt", root_dir=sandbox_test_root)718        content = "\n".join([f"Line {i} with some content here" for i in range(1000)])719720        result = sandbox_backend.write(test_path, content)721722        assert result.error is None723        read_result = sandbox_backend.read(test_path)724        assert read_result.error is None725        assert read_result.file_data is not None726        assert "Line 0 with some content here" in read_result.file_data["content"]727728    def test_write_content_with_only_newlines(729        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str730    ) -> None:731        """Writing newline-only content should preserve the newline count."""732        if not self.has_sync:733            pytest.skip("Sync tests not supported.")734735        test_path = self.sandbox_path("only_newlines.txt", root_dir=sandbox_test_root)736        content = "\n\n\n\n\n"737738        result = sandbox_backend.write(test_path, content)739740        assert result.error is None741        exec_result = sandbox_backend.execute(f"wc -l {_quote(test_path)}")742        assert "5" in exec_result.output743744    def test_read_nonexistent_file(745        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str746    ) -> None:747        """Reading a missing file should return a file-not-found style error."""748        if not self.has_sync:749            pytest.skip("Sync tests not supported.")750751        result = sandbox_backend.read(752            self.sandbox_path("nonexistent.txt", root_dir=sandbox_test_root)753        )754755        assert result.error is not None756        assert (757            "not_found" in result.error.lower() or "not found" in result.error.lower()758        )759760    def test_read_empty_file(761        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str762    ) -> None:763        """Reading an empty file should succeed with empty-or-empty-notice content."""764        if not self.has_sync:765            pytest.skip("Sync tests not supported.")766767        test_path = self.sandbox_path("empty_read.txt", root_dir=sandbox_test_root)768        sandbox_backend.write(test_path, "")769770        result = sandbox_backend.read(test_path)771772        assert result.error is None773        assert result.file_data is not None774        content = result.file_data["content"]775        assert "empty" in content.lower() or content.strip() == ""776777    def test_read_with_offset(778        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str779    ) -> None:780        """Reading with offset should skip the requested number of lines."""781        if not self.has_sync:782            pytest.skip("Sync tests not supported.")783784        test_path = self.sandbox_path("offset_test.txt", root_dir=sandbox_test_root)785        content = "\n".join([f"Row_{i}_content" for i in range(1, 11)])786        sandbox_backend.write(test_path, content)787788        result = sandbox_backend.read(test_path, offset=5)789790        assert result.error is None791        assert result.file_data is not None792        assert "Row_6_content" in result.file_data["content"]793        assert "Row_1_content" not in result.file_data["content"]794795    def test_read_with_limit(796        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str797    ) -> None:798        """Reading with limit should cap the number of returned lines."""799        if not self.has_sync:800            pytest.skip("Sync tests not supported.")801802        test_path = self.sandbox_path("limit_test.txt", root_dir=sandbox_test_root)803        content = "\n".join([f"Row_{i}_content" for i in range(1, 101)])804        sandbox_backend.write(test_path, content)805806        result = sandbox_backend.read(test_path, offset=0, limit=5)807808        assert result.error is None809        assert result.file_data is not None810        assert "Row_1_content" in result.file_data["content"]811        assert "Row_5_content" in result.file_data["content"]812        assert "Row_6_content" not in result.file_data["content"]813814    def test_read_with_offset_and_limit(815        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str816    ) -> None:817        """Reading with offset and limit should return the expected slice."""818        if not self.has_sync:819            pytest.skip("Sync tests not supported.")820821        test_path = self.sandbox_path(822            "offset_limit_test.txt", root_dir=sandbox_test_root823        )824        content = "\n".join([f"Row_{i}_content" for i in range(1, 21)])825        sandbox_backend.write(test_path, content)826827        result = sandbox_backend.read(test_path, offset=10, limit=5)828829        assert result.error is None830        assert result.file_data is not None831        assert "Row_11_content" in result.file_data["content"]832        assert "Row_15_content" in result.file_data["content"]833        assert "Row_10_content" not in result.file_data["content"]834        assert "Row_16_content" not in result.file_data["content"]835836    def test_read_unicode_content(837        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str838    ) -> None:839        """Reading unicode content should preserve non-ASCII text."""840        if not self.has_sync:841            pytest.skip("Sync tests not supported.")842843        test_path = self.sandbox_path("unicode_read.txt", root_dir=sandbox_test_root)844        content = "Hello 👋 世界\nПривет мир\nمرحبا العالم"  # noqa: RUF001845        sandbox_backend.write(test_path, content)846847        result = sandbox_backend.read(test_path)848849        assert result.error is None850        assert result.file_data is not None851        assert "👋" in result.file_data["content"]852        assert "世界" in result.file_data["content"]853        assert "Привет" in result.file_data["content"]854855    def test_read_file_with_very_long_lines(856        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str857    ) -> None:858        """Reading files with long lines should still succeed."""859        if not self.has_sync:860            pytest.skip("Sync tests not supported.")861862        test_path = self.sandbox_path("long_lines.txt", root_dir=sandbox_test_root)863        long_line = "x" * 3000864        content = f"Short line\n{long_line}\nAnother short line"865        sandbox_backend.write(test_path, content)866867        result = sandbox_backend.read(test_path)868869        assert result.error is None870        assert result.file_data is not None871        assert "Short line" in result.file_data["content"]872873    def test_read_with_zero_limit(874        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str875    ) -> None:876        """Reading with `limit=0` should not include file content."""877        if not self.has_sync:878            pytest.skip("Sync tests not supported.")879880        test_path = self.sandbox_path("zero_limit.txt", root_dir=sandbox_test_root)881        sandbox_backend.write(test_path, "Line 1\nLine 2\nLine 3")882883        result = sandbox_backend.read(test_path, offset=0, limit=0)884885        content = result.file_data["content"] if result.file_data else ""886        assert "Line 1" not in content or content.strip() == ""887888    def test_read_offset_beyond_file_length(889        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str890    ) -> None:891        """Reading beyond EOF should return no file lines."""892        if not self.has_sync:893            pytest.skip("Sync tests not supported.")894895        test_path = self.sandbox_path("offset_beyond.txt", root_dir=sandbox_test_root)896        sandbox_backend.write(test_path, "Line 1\nLine 2\nLine 3")897898        result = sandbox_backend.read(test_path, offset=100, limit=10)899900        content = result.file_data["content"] if result.file_data else ""901        error = result.error or ""902        assert "Line 1" not in content903        assert "Line 1" not in error904        assert "Line 2" not in content905        assert "Line 2" not in error906        assert "Line 3" not in content907        assert "Line 3" not in error908909    def test_read_offset_at_exact_file_length(910        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str911    ) -> None:912        """Reading exactly at EOF should return no file lines."""913        if not self.has_sync:914            pytest.skip("Sync tests not supported.")915916        test_path = self.sandbox_path("offset_exact.txt", root_dir=sandbox_test_root)917        content = "\n".join([f"Line {i}" for i in range(1, 6)])918        sandbox_backend.write(test_path, content)919920        result = sandbox_backend.read(test_path, offset=5, limit=10)921922        text = result.file_data["content"] if result.file_data else ""923        error = result.error or ""924        assert "Line 1" not in text925        assert "Line 1" not in error926        assert "Line 5" not in text927        assert "Line 5" not in error928929    def test_read_very_large_file_in_chunks(930        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str931    ) -> None:932        """Repeated offset+limit reads should cover different slices of a large file."""933        if not self.has_sync:934            pytest.skip("Sync tests not supported.")935936        test_path = self.sandbox_path("large_chunked.txt", root_dir=sandbox_test_root)937        content = "\n".join([f"Line_{i:04d}_content" for i in range(1000)])938        sandbox_backend.write(test_path, content)939940        first = sandbox_backend.read(test_path, offset=0, limit=100)941        middle = sandbox_backend.read(test_path, offset=500, limit=100)942        last = sandbox_backend.read(test_path, offset=900, limit=100)943944        assert first.error is None945        assert first.file_data is not None946        assert "Line_0000_content" in first.file_data["content"]947        assert "Line_0099_content" in first.file_data["content"]948        assert "Line_0100_content" not in first.file_data["content"]949950        assert middle.error is None951        assert middle.file_data is not None952        assert "Line_0500_content" in middle.file_data["content"]953        assert "Line_0599_content" in middle.file_data["content"]954        assert "Line_0499_content" not in middle.file_data["content"]955956        assert last.error is None957        assert last.file_data is not None958        assert "Line_0900_content" in last.file_data["content"]959        assert "Line_0999_content" in last.file_data["content"]960961    def test_edit_multiple_occurrences_without_replace_all(962        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str963    ) -> None:964        """Editing multiple matches without `replace_all` should fail."""965        if not self.has_sync:966            pytest.skip("Sync tests not supported.")967968        test_path = self.sandbox_path("edit_multi.txt", root_dir=sandbox_test_root)969        content = "apple\nbanana\napple\norange\napple"970        sandbox_backend.write(test_path, content)971972        result = sandbox_backend.edit(test_path, "apple", "pear", replace_all=False)973974        assert result.error is not None975        assert "multiple" in result.error.lower()976        read_result = sandbox_backend.read(test_path)977        assert read_result.error is None978        assert read_result.file_data is not None979        assert "apple" in read_result.file_data["content"]980        assert "pear" not in read_result.file_data["content"]981982    def test_edit_multiple_occurrences_with_replace_all(983        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str984    ) -> None:985        """Editing multiple matches with `replace_all` should replace each match."""986        if not self.has_sync:987            pytest.skip("Sync tests not supported.")988989        test_path = self.sandbox_path(990            "edit_replace_all.txt", root_dir=sandbox_test_root991        )992        content = "apple\nbanana\napple\norange\napple"993        sandbox_backend.write(test_path, content)994995        result = sandbox_backend.edit(test_path, "apple", "pear", replace_all=True)996997        assert result.error is None998        assert result.occurrences == 3999        read_result = sandbox_backend.read(test_path)1000        assert read_result.error is None1001        assert read_result.file_data is not None1002        assert "apple" not in read_result.file_data["content"]1003        assert read_result.file_data["content"].count("pear") == 310041005    def test_edit_string_not_found(1006        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1007    ) -> None:1008        """Editing a missing string should return a not-found style error."""1009        if not self.has_sync:1010            pytest.skip("Sync tests not supported.")10111012        test_path = self.sandbox_path("edit_not_found.txt", root_dir=sandbox_test_root)1013        sandbox_backend.write(test_path, "Hello world")10141015        result = sandbox_backend.edit(test_path, "nonexistent", "replacement")10161017        assert result.error is not None1018        assert "not found" in result.error.lower()10191020    def test_edit_nonexistent_file(1021        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1022    ) -> None:1023        """Editing a missing file should return a file-not-found style error."""1024        if not self.has_sync:1025            pytest.skip("Sync tests not supported.")10261027        result = sandbox_backend.edit(1028            self.sandbox_path("nonexistent_edit.txt", root_dir=sandbox_test_root),1029            "old",1030            "new",1031        )10321033        assert result.error is not None1034        assert (1035            "not_found" in result.error.lower() or "not found" in result.error.lower()1036        )10371038    def test_edit_special_characters(1039        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1040    ) -> None:1041        """Editing should treat special characters as literal strings."""1042        if not self.has_sync:1043            pytest.skip("Sync tests not supported.")10441045        test_path = self.sandbox_path("edit_special.txt", root_dir=sandbox_test_root)1046        content = "Price: $100.00\nPattern: [a-z]*\nPath: /usr/bin"1047        sandbox_backend.write(test_path, content)10481049        first = sandbox_backend.edit(test_path, "$100.00", "$200.00")1050        second = sandbox_backend.edit(test_path, "[a-z]*", "[0-9]+")10511052        assert first.error is None1053        assert second.error is None1054        read_result = sandbox_backend.read(test_path)1055        assert read_result.error is None1056        assert read_result.file_data is not None1057        assert "$200.00" in read_result.file_data["content"]1058        assert "[0-9]+" in read_result.file_data["content"]10591060    def test_edit_multiline_support(1061        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1062    ) -> None:1063        """Editing should support replacing multi-line strings."""1064        if not self.has_sync:1065            pytest.skip("Sync tests not supported.")10661067        test_path = self.sandbox_path("edit_multiline.txt", root_dir=sandbox_test_root)1068        sandbox_backend.write(test_path, "Line 1\nLine 2\nLine 3")10691070        result = sandbox_backend.edit(test_path, "Line 1\nLine 2", "Combined")10711072        assert result.error is None1073        assert result.occurrences == 11074        read_result = sandbox_backend.read(test_path)1075        assert read_result.error is None1076        assert read_result.file_data is not None1077        assert "Combined" in read_result.file_data["content"]10781079    def test_ls_lists_nested_directories(1080        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1081    ) -> None:1082        """Listing should include nested directories and immediate child files."""1083        if not self.has_sync:1084            pytest.skip("Sync tests not supported.")10851086        base_dir = self.sandbox_path("ls_nested", root_dir=sandbox_test_root)1087        sandbox_backend.execute(1088            f"mkdir -p {_quote(base_dir)}/subdir && touch {_quote(base_dir)}/root.txt"1089        )10901091        result = sandbox_backend.ls(base_dir)10921093        assert result.error is None1094        assert result.entries is not None1095        paths = [entry["path"] for entry in result.entries]1096        assert f"{base_dir}/subdir" in paths1097        assert f"{base_dir}/root.txt" in paths10981099    def test_ls_unicode_filenames(1100        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1101    ) -> None:1102        """Listing should preserve unicode filenames."""1103        if not self.has_sync:1104            pytest.skip("Sync tests not supported.")11051106        base_dir = self.sandbox_path("ls_unicode", root_dir=sandbox_test_root)1107        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1108        sandbox_backend.write(f"{base_dir}/测试文件.txt", "content")1109        sandbox_backend.write(f"{base_dir}/файл.txt", "content")11101111        result = sandbox_backend.ls(base_dir)11121113        assert result.error is None1114        assert result.entries is not None1115        paths = [entry["path"] for entry in result.entries]1116        assert f"{base_dir}/测试文件.txt" in paths1117        assert f"{base_dir}/файл.txt" in paths11181119    def test_ls_large_directory(1120        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1121    ) -> None:1122        """Listing a larger directory should include all created entries."""1123        if not self.has_sync:1124            pytest.skip("Sync tests not supported.")11251126        base_dir = self.sandbox_path("ls_large", root_dir=sandbox_test_root)1127        sandbox_backend.execute(1128            f"mkdir -p {_quote(base_dir)} && "1129            f"cd {_quote(base_dir)} && "1130            "for i in $(seq 0 49); do "1131            "echo content > file_$(printf '%03d' $i).txt; "1132            "done"1133        )11341135        result = sandbox_backend.ls(base_dir)11361137        assert result.error is None1138        assert result.entries is not None1139        assert len(result.entries) == 501140        paths = [entry["path"] for entry in result.entries]1141        assert f"{base_dir}/file_000.txt" in paths1142        assert f"{base_dir}/file_049.txt" in paths11431144    def test_ls_path_with_trailing_slash(1145        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1146    ) -> None:1147        """Listing a path with a trailing slash should match the normalized path."""1148        if not self.has_sync:1149            pytest.skip("Sync tests not supported.")11501151        base_dir = self.sandbox_path("ls_trailing", root_dir=sandbox_test_root)1152        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1153        sandbox_backend.write(f"{base_dir}/file.txt", "content")11541155        result = sandbox_backend.ls(f"{base_dir}/")11561157        assert result.error is None1158        assert result.entries is not None1159        paths = [entry["path"] for entry in result.entries]1160        assert f"{base_dir}/file.txt" in paths11611162    def test_ls_special_characters_in_filenames(1163        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1164    ) -> None:1165        """Listing should preserve filenames with shell metacharacters."""1166        if not self.has_sync:1167            pytest.skip("Sync tests not supported.")11681169        base_dir = self.sandbox_path("ls_special", root_dir=sandbox_test_root)1170        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1171        sandbox_backend.write(f"{base_dir}/file(1).txt", "content")1172        sandbox_backend.write(f"{base_dir}/file[2].txt", "content")1173        sandbox_backend.write(f"{base_dir}/file-3.txt", "content")11741175        result = sandbox_backend.ls(base_dir)11761177        assert result.error is None1178        assert result.entries is not None1179        paths = [entry["path"] for entry in result.entries]1180        assert f"{base_dir}/file(1).txt" in paths1181        assert f"{base_dir}/file[2].txt" in paths1182        assert f"{base_dir}/file-3.txt" in paths11831184    def test_ls_path_is_sanitized(1185        self, sandbox_backend: SandboxBackendProtocol1186    ) -> None:1187        """Listing an injected path should not execute attacker-controlled code."""1188        if not self.has_sync:1189            pytest.skip("Sync tests not supported.")11901191        malicious_path = "'; import os; os.system('echo INJECTED'); #"1192        result = sandbox_backend.ls(malicious_path)11931194        assert result.error is not None or result.entries == []11951196    def test_read_path_is_sanitized(1197        self, sandbox_backend: SandboxBackendProtocol1198    ) -> None:1199        """Reading an injected path should return an error without executing it."""1200        if not self.has_sync:1201            pytest.skip("Sync tests not supported.")12021203        malicious_path = "'; import os; os.system('echo INJECTED'); #"1204        result = sandbox_backend.read(malicious_path)12051206        assert result.error is not None1207        assert result.file_data is None12081209    def test_grep_basic_search(1210        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1211    ) -> None:1212        """Grep should return matches across multiple files."""1213        if not self.has_sync:1214            pytest.skip("Sync tests not supported.")12151216        base_dir = self.sandbox_path("grep_test", root_dir=sandbox_test_root)1217        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1218        sandbox_backend.write(f"{base_dir}/file1.txt", "Hello world\nGoodbye world")1219        sandbox_backend.write(f"{base_dir}/file2.txt", "Hello there\nGoodbye friend")12201221        result = sandbox_backend.grep("Hello", path=base_dir)12221223        assert result.error is None1224        assert result.matches is not None1225        assert len(result.matches) == 21226        paths = [match["path"] for match in result.matches]1227        assert any(path.endswith("file1.txt") for path in paths)1228        assert any(path.endswith("file2.txt") for path in paths)1229        assert all(match["line"] == 1 for match in result.matches)12301231    def test_grep_with_glob_pattern(1232        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1233    ) -> None:1234        """Grep should honor the file glob filter."""1235        if not self.has_sync:1236            pytest.skip("Sync tests not supported.")12371238        base_dir = self.sandbox_path("grep_glob", root_dir=sandbox_test_root)1239        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1240        sandbox_backend.write(f"{base_dir}/test.txt", "pattern")1241        sandbox_backend.write(f"{base_dir}/test.py", "pattern")1242        sandbox_backend.write(f"{base_dir}/test.md", "pattern")12431244        result = sandbox_backend.grep("pattern", path=base_dir, glob="*.py")12451246        assert result.error is None1247        assert result.matches == [1248            {"path": f"{base_dir}/test.py", "line": 1, "text": "pattern"}1249        ]12501251    def test_grep_no_matches(1252        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1253    ) -> None:1254        """Grep with no matches should return an empty match list."""1255        if not self.has_sync:1256            pytest.skip("Sync tests not supported.")12571258        base_dir = self.sandbox_path("grep_empty", root_dir=sandbox_test_root)1259        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1260        sandbox_backend.write(f"{base_dir}/file.txt", "Hello world")12611262        result = sandbox_backend.grep("nonexistent", path=base_dir)12631264        assert result.error is None1265        assert result.matches == []12661267    def test_grep_multiple_matches_per_file(1268        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1269    ) -> None:1270        """Grep should report multiple matches from a single file with line numbers."""1271        if not self.has_sync:1272            pytest.skip("Sync tests not supported.")12731274        base_dir = self.sandbox_path("grep_multi", root_dir=sandbox_test_root)1275        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1276        sandbox_backend.write(1277            f"{base_dir}/fruits.txt", "apple\nbanana\napple\norange\napple"1278        )12791280        result = sandbox_backend.grep("apple", path=base_dir)12811282        assert result.error is None1283        assert result.matches is not None1284        assert len(result.matches) == 31285        assert [match["line"] for match in result.matches] == [1, 3, 5]12861287    def test_grep_literal_string_matching(1288        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1289    ) -> None:1290        """Grep should treat the search pattern literally rather than as regex."""1291        if not self.has_sync:1292            pytest.skip("Sync tests not supported.")12931294        base_dir = self.sandbox_path("grep_literal", root_dir=sandbox_test_root)1295        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1296        sandbox_backend.write(f"{base_dir}/numbers.txt", "test123\ntest456\nabcdef")12971298        result = sandbox_backend.grep("test123", path=base_dir)12991300        assert result.error is None1301        assert result.matches is not None1302        assert len(result.matches) == 11303        assert "test123" in result.matches[0]["text"]13041305    def test_grep_unicode_pattern(1306        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1307    ) -> None:1308        """Grep should match unicode patterns in unicode content."""1309        if not self.has_sync:1310            pytest.skip("Sync tests not supported.")13111312        base_dir = self.sandbox_path("grep_unicode", root_dir=sandbox_test_root)1313        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1314        sandbox_backend.write(1315            f"{base_dir}/unicode.txt",1316            "Hello 世界\nПривет мир\n测试 pattern",  # noqa: RUF0011317        )13181319        result = sandbox_backend.grep("世界", path=base_dir)13201321        assert result.error is None1322        assert result.matches is not None1323        assert len(result.matches) == 11324        assert "世界" in result.matches[0]["text"]13251326    def test_grep_case_sensitivity(1327        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1328    ) -> None:1329        """Grep should be case-sensitive by default."""1330        if not self.has_sync:1331            pytest.skip("Sync tests not supported.")13321333        base_dir = self.sandbox_path("grep_case", root_dir=sandbox_test_root)1334        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1335        sandbox_backend.write(f"{base_dir}/case.txt", "Hello\nhello\nHELLO")13361337        result = sandbox_backend.grep("Hello", path=base_dir)13381339        assert result.error is None1340        assert result.matches is not None1341        assert len(result.matches) == 11342        assert result.matches[0]["text"] == "Hello"13431344    def test_grep_with_special_characters(1345        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1346    ) -> None:1347        """Grep should treat special characters in the pattern literally."""1348        if not self.has_sync:1349            pytest.skip("Sync tests not supported.")13501351        base_dir = self.sandbox_path("grep_special", root_dir=sandbox_test_root)1352        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1353        sandbox_backend.write(1354            f"{base_dir}/special.txt", "Price: $100\nPath: /usr/bin\nPattern: [a-z]*"1355        )13561357        dollar = sandbox_backend.grep("$100", path=base_dir)1358        brackets = sandbox_backend.grep("[a-z]*", path=base_dir)13591360        assert dollar.error is None1361        assert dollar.matches is not None1362        assert len(dollar.matches) == 11363        assert "$100" in dollar.matches[0]["text"]13641365        assert brackets.error is None1366        assert brackets.matches is not None1367        assert len(brackets.matches) == 11368        assert "[a-z]*" in brackets.matches[0]["text"]13691370    def test_grep_empty_directory(1371        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1372    ) -> None:1373        """Grep in an empty directory should return no matches."""1374        if not self.has_sync:1375            pytest.skip("Sync tests not supported.")13761377        base_dir = self.sandbox_path("grep_empty_dir", root_dir=sandbox_test_root)1378        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")13791380        result = sandbox_backend.grep("anything", path=base_dir)13811382        assert result.error is None1383        assert result.matches == []13841385    def test_grep_across_nested_directories(1386        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1387    ) -> None:1388        """Grep should recurse into nested directories."""1389        if not self.has_sync:1390            pytest.skip("Sync tests not supported.")13911392        base_dir = self.sandbox_path("grep_nested", root_dir=sandbox_test_root)1393        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}/sub1/sub2")1394        sandbox_backend.write(f"{base_dir}/root.txt", "target here")1395        sandbox_backend.write(f"{base_dir}/sub1/level1.txt", "target here")1396        sandbox_backend.write(f"{base_dir}/sub1/sub2/level2.txt", "target here")13971398        result = sandbox_backend.grep("target", path=base_dir)13991400        assert result.error is None1401        assert result.matches is not None1402        assert len(result.matches) == 314031404    def test_grep_with_globstar_include_pattern(1405        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1406    ) -> None:1407        """Grep with a glob filter should still find nested matching files."""1408        if not self.has_sync:1409            pytest.skip("Sync tests not supported.")14101411        base_dir = self.sandbox_path("grep_globstar", root_dir=sandbox_test_root)1412        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}/a/b")1413        sandbox_backend.write(f"{base_dir}/a/b/target.py", "needle")1414        sandbox_backend.write(f"{base_dir}/a/ignore.txt", "needle")14151416        result = sandbox_backend.grep("needle", path=base_dir, glob="*.py")14171418        assert result.error is None1419        assert result.matches == [1420            {"path": f"{base_dir}/a/b/target.py", "line": 1, "text": "needle"}1421        ]14221423    def test_grep_reports_correct_line_numbers(1424        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1425    ) -> None:1426        """Grep should report the original file line number for a match."""1427        if not self.has_sync:1428            pytest.skip("Sync tests not supported.")14291430        base_dir = self.sandbox_path("grep_multiline", root_dir=sandbox_test_root)1431        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1432        content = "\n".join([f"Line {i}" for i in range(1, 101)])1433        sandbox_backend.write(f"{base_dir}/long.txt", content)14341435        result = sandbox_backend.grep("Line 50", path=base_dir)14361437        assert result.error is None1438        assert result.matches == [1439            {"path": f"{base_dir}/long.txt", "line": 50, "text": "Line 50"}1440        ]14411442    def test_glob_basic_pattern(1443        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1444    ) -> None:1445        """Glob should match basic wildcard patterns."""1446        if not self.has_sync:1447            pytest.skip("Sync tests not supported.")14481449        base_dir = self.sandbox_path("glob_test", root_dir=sandbox_test_root)1450        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1451        sandbox_backend.write(f"{base_dir}/file1.txt", "content")1452        sandbox_backend.write(f"{base_dir}/file2.txt", "content")1453        sandbox_backend.write(f"{base_dir}/file3.py", "content")14541455        result = sandbox_backend.glob("*.txt", path=base_dir)14561457        assert result.error is None1458        assert result.matches is not None1459        paths = [info["path"] for info in result.matches]1460        assert len(paths) == 21461        assert "file1.txt" in paths1462        assert "file2.txt" in paths1463        assert not any(path.endswith(".py") for path in paths)14641465    def test_glob_recursive_pattern(1466        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1467    ) -> None:1468        """Glob should support recursive patterns with `**`."""1469        if not self.has_sync:1470            pytest.skip("Sync tests not supported.")14711472        base_dir = self.sandbox_path("glob_recursive", root_dir=sandbox_test_root)1473        sandbox_backend.execute(1474            f"mkdir -p {_quote(base_dir)}/subdir1 {_quote(base_dir)}/subdir2"1475        )1476        sandbox_backend.write(f"{base_dir}/root.txt", "content")1477        sandbox_backend.write(f"{base_dir}/subdir1/nested1.txt", "content")1478        sandbox_backend.write(f"{base_dir}/subdir2/nested2.txt", "content")14791480        result = sandbox_backend.glob("**/*.txt", path=base_dir)14811482        assert result.error is None1483        assert result.matches is not None1484        paths = [info["path"] for info in result.matches]1485        assert any(path.endswith("nested1.txt") for path in paths)1486        assert any(path.endswith("nested2.txt") for path in paths)14871488    def test_glob_no_matches(1489        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1490    ) -> None:1491        """Glob with no matches should return an empty match list."""1492        if not self.has_sync:1493            pytest.skip("Sync tests not supported.")14941495        base_dir = self.sandbox_path("glob_empty", root_dir=sandbox_test_root)1496        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1497        sandbox_backend.write(f"{base_dir}/file.txt", "content")14981499        result = sandbox_backend.glob("*.py", path=base_dir)15001501        assert result.error is None1502        assert result.matches == []15031504    def test_glob_with_directories(1505        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1506    ) -> None:1507        """Glob should include directories and mark them with `is_dir`."""1508        if not self.has_sync:1509            pytest.skip("Sync tests not supported.")15101511        base_dir = self.sandbox_path("glob_dirs", root_dir=sandbox_test_root)1512        sandbox_backend.execute(1513            f"mkdir -p {_quote(base_dir)}/dir1 {_quote(base_dir)}/dir2"1514        )1515        sandbox_backend.write(f"{base_dir}/file.txt", "content")15161517        result = sandbox_backend.glob("*", path=base_dir)15181519        assert result.error is None1520        assert result.matches is not None1521        assert len(result.matches) == 31522        dir_count = sum(1 for info in result.matches if info["is_dir"])1523        file_count = sum(1 for info in result.matches if not info["is_dir"])1524        assert dir_count == 21525        assert file_count == 115261527    def test_glob_hidden_files_explicitly(1528        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1529    ) -> None:1530        """Glob should match hidden files when the pattern explicitly requests them."""1531        if not self.has_sync:1532            pytest.skip("Sync tests not supported.")15331534        base_dir = self.sandbox_path("glob_hidden", root_dir=sandbox_test_root)1535        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1536        sandbox_backend.write(f"{base_dir}/.hidden1", "content")1537        sandbox_backend.write(f"{base_dir}/.hidden2", "content")1538        sandbox_backend.write(f"{base_dir}/visible.txt", "content")15391540        result = sandbox_backend.glob(".*", path=base_dir)15411542        assert result.error is None1543        assert result.matches is not None1544        paths = [info["path"] for info in result.matches]1545        assert ".hidden1" in paths or ".hidden2" in paths1546        assert not any(path == "visible.txt" for path in paths)15471548    def test_glob_with_character_class(1549        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1550    ) -> None:1551        """Glob should support character classes in patterns."""1552        if not self.has_sync:1553            pytest.skip("Sync tests not supported.")15541555        base_dir = self.sandbox_path("glob_charclass", root_dir=sandbox_test_root)1556        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1557        sandbox_backend.write(f"{base_dir}/file1.txt", "content")1558        sandbox_backend.write(f"{base_dir}/file2.txt", "content")1559        sandbox_backend.write(f"{base_dir}/file3.txt", "content")1560        sandbox_backend.write(f"{base_dir}/fileA.txt", "content")15611562        result = sandbox_backend.glob("file[1-2].txt", path=base_dir)15631564        assert result.error is None1565        assert result.matches is not None1566        paths = [info["path"] for info in result.matches]1567        assert len(paths) == 21568        assert "file1.txt" in paths1569        assert "file2.txt" in paths1570        assert "file3.txt" not in paths1571        assert "fileA.txt" not in paths15721573    def test_glob_with_question_mark(1574        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1575    ) -> None:1576        """Glob should support single-character wildcards."""1577        if not self.has_sync:1578            pytest.skip("Sync tests not supported.")15791580        base_dir = self.sandbox_path("glob_question", root_dir=sandbox_test_root)1581        sandbox_backend.execute(f"mkdir -p {_quote(base_dir)}")1582        sandbox_backend.write(f"{base_dir}/file1.txt", "content")1583        sandbox_backend.write(f"{base_dir}/file2.txt", "content")1584        sandbox_backend.write(f"{base_dir}/file10.txt", "content")15851586        result = sandbox_backend.glob("file?.txt", path=base_dir)15871588        assert result.error is None1589        assert result.matches is not None1590        paths = [info["path"] for info in result.matches]1591        assert len(paths) == 21592        assert "file1.txt" in paths1593        assert "file2.txt" in paths1594        assert "file10.txt" not in paths15951596    async def test_awrite_aread_large_text_payload(1597        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1598    ) -> None:1599        """Async write should allow a large text file to be read back non-empty."""1600        if not self.has_async:1601            pytest.skip("Async tests not supported.")16021603        test_path = self.sandbox_path(1604            "large_async_text.txt", root_dir=sandbox_test_root1605        )1606        line = "0123456789abcdef" * 2561607        lines = [line for _ in range(2560)]1608        test_content = "\n".join(lines)16091610        write_result = await sandbox_backend.awrite(test_path, test_content)1611        assert write_result.error is None1612        assert write_result.path == test_path16131614        exec_result = await sandbox_backend.aexecute(f"wc -c {_quote(test_path)}")1615        assert exec_result.exit_code == 01616        assert str(len(test_content.encode("utf-8"))) in exec_result.output16171618        read_result = await sandbox_backend.aread(test_path)1619        assert isinstance(read_result, ReadResult)1620        assert read_result.error is None1621        assert read_result.file_data is not None1622        assert read_result.file_data["encoding"] == "utf-8"1623        assert read_result.file_data["content"].startswith(lines[0])16241625    async def test_aread_large_text_payload_paginated_roundtrip(1626        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1627    ) -> None:1628        """Async paginated reads should reconstruct the full large text payload."""1629        if not self.has_async:1630            pytest.skip("Async tests not supported.")16311632        test_path = self.sandbox_path(1633            "large_async_chunked.txt", root_dir=sandbox_test_root1634        )1635        lines = [f"Line_{i:04d}_content" for i in range(2500)]1636        test_content = "\n".join(lines)16371638        write_result = await sandbox_backend.awrite(test_path, test_content)1639        assert write_result.error is None16401641        parts: list[str] = []1642        for offset in range(0, len(lines), 100):1643            page = await sandbox_backend.aread(test_path, offset=offset, limit=100)1644            assert page.error is None1645            assert page.file_data is not None1646            assert page.file_data["content"] == "\n".join(lines[offset : offset + 100])1647            parts.append(page.file_data["content"])16481649        assert "\n".join(parts) == test_content16501651    async def test_adownload_large_text_payload_roundtrip(1652        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1653    ) -> None:1654        """Async download should preserve the full large text payload exactly."""1655        if not self.has_async:1656            pytest.skip("Async tests not supported.")16571658        test_path = self.sandbox_path(1659            "large_async_download.txt", root_dir=sandbox_test_root1660        )1661        line = "0123456789abcdef" * 2561662        lines = [line for _ in range(2560)]1663        test_content = "\n".join(lines)16641665        write_result = await sandbox_backend.awrite(test_path, test_content)1666        assert write_result.error is None16671668        download_responses = await sandbox_backend.adownload_files([test_path])1669        assert download_responses == [1670            FileDownloadResponse(1671                path=test_path,1672                content=test_content.encode("utf-8"),1673                error=None,1674            )1675        ]16761677    def test_write_read_download_large_text_with_escaped_content(1678        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1679    ) -> None:1680        """Sync large-text roundtrips should preserve escaped and unicode content."""1681        if not self.has_sync:1682            pytest.skip("Sync tests not supported.")16831684        test_path = self.sandbox_path(1685            "large_sync_escaped.txt", root_dir=sandbox_test_root1686        )1687        line = (1688            "prefix\t\u2603\u4e16\u754c\u03c0\u22483.14159"1689            " | spaces   preserved"1690            " | quotes ' \""1691            " | brackets [] {{}}"1692            " | shell $VAR `cmd` $(subshell)"1693            " | slash /tmp/path and backslash \\\\"1694            " | control-ish \\r \\n"1695            " | suffix"1696        )1697        lines = [f"{i:04d}:{line}" for i in range(2500)]1698        test_content = "\n".join(lines)16991700        write_result = sandbox_backend.write(test_path, test_content)1701        assert write_result.error is None17021703        pages: list[str] = []1704        for offset in range(0, len(lines), 100):1705            page = sandbox_backend.read(test_path, offset=offset, limit=100)1706            assert page.error is None1707            assert page.file_data is not None1708            assert page.file_data["content"] == "\n".join(lines[offset : offset + 100])1709            pages.append(page.file_data["content"])17101711        assert "\n".join(pages) == test_content17121713        download_responses = sandbox_backend.download_files([test_path])1714        assert download_responses == [1715            FileDownloadResponse(1716                path=test_path,1717                content=test_content.encode("utf-8"),1718                error=None,1719            )1720        ]17211722    async def test_awrite_aread_adownload_large_text_with_escaped_content(1723        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1724    ) -> None:1725        """Async large-text roundtrips should preserve escaped and unicode content."""1726        if not self.has_async:1727            pytest.skip("Async tests not supported.")17281729        test_path = self.sandbox_path(1730            "large_async_escaped.txt", root_dir=sandbox_test_root1731        )1732        line = (1733            "prefix\t\u2603\u4e16\u754c\u03c0\u22483.14159"1734            " | spaces   preserved"1735            " | quotes ' \""1736            " | brackets [] {{}}"1737            " | shell $VAR `cmd` $(subshell)"1738            " | slash /tmp/path and backslash \\\\"1739            " | control-ish \\r \\n"1740            " | suffix"1741        )1742        lines = [f"{i:04d}:{line}" for i in range(2500)]1743        test_content = "\n".join(lines)17441745        write_result = await sandbox_backend.awrite(test_path, test_content)1746        assert write_result.error is None17471748        pages: list[str] = []1749        for offset in range(0, len(lines), 100):1750            page = await sandbox_backend.aread(test_path, offset=offset, limit=100)1751            assert page.error is None1752            assert page.file_data is not None1753            assert page.file_data["content"] == "\n".join(lines[offset : offset + 100])1754            pages.append(page.file_data["content"])17551756        assert "\n".join(pages) == test_content17571758        download_responses = await sandbox_backend.adownload_files([test_path])1759        assert download_responses == [1760            FileDownloadResponse(1761                path=test_path,1762                content=test_content.encode("utf-8"),1763                error=None,1764            )1765        ]17661767    async def test_aread_binary_image_file(1768        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1769    ) -> None:1770        """Async read should return base64-encoded content for a binary image file."""1771        if not self.has_async:1772            pytest.skip("Async tests not supported.")17731774        test_path = self.sandbox_path("async_binary.png", root_dir=sandbox_test_root)1775        raw_bytes = bytes(range(256))17761777        upload_responses = await sandbox_backend.aupload_files([(test_path, raw_bytes)])1778        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]17791780        result = await sandbox_backend.aread(test_path)1781        assert isinstance(result, ReadResult)1782        assert result.error is None1783        assert result.file_data is not None1784        assert result.file_data["encoding"] == "base64"1785        assert base64.b64decode(result.file_data["content"]) == raw_bytes17861787    async def test_aread_binary_file_100_kib(1788        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1789    ) -> None:1790        """Async read should return base64 content for a 100 KiB binary file."""1791        if not self.has_async:1792            pytest.skip("Async tests not supported.")17931794        test_path = self.sandbox_path(1795            "async_binary_100kib.png", root_dir=sandbox_test_root1796        )1797        chunk = bytes(range(256))1798        raw_bytes = chunk * 40017991800        upload_responses = await sandbox_backend.aupload_files([(test_path, raw_bytes)])1801        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]18021803        result = await sandbox_backend.aread(test_path)1804        assert isinstance(result, ReadResult)1805        assert result.error is None1806        assert result.file_data is not None1807        assert result.file_data["encoding"] == "base64"1808        assert base64.b64decode(result.file_data["content"]) == raw_bytes18091810    async def test_aread_binary_file_1_mib_returns_error(1811        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1812    ) -> None:1813        """Async read should error when a binary file exceeds the preview size limit."""1814        if not self.has_async:1815            pytest.skip("Async tests not supported.")18161817        test_path = self.sandbox_path(1818            "async_binary_1mib.png", root_dir=sandbox_test_root1819        )1820        chunk = bytes(range(256))1821        raw_bytes = chunk * 409618221823        upload_responses = await sandbox_backend.aupload_files([(test_path, raw_bytes)])1824        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]18251826        result = await sandbox_backend.aread(test_path)1827        assert isinstance(result, ReadResult)1828        assert result.file_data is None1829        expected_error = (1830            f"File '{test_path}': Binary file exceeds maximum preview size of "1831            "512000 bytes"1832        )1833        assert result.error == expected_error18341835    async def test_aexecute_large_stdout_payload(1836        self, sandbox_backend: SandboxBackendProtocol1837    ) -> None:1838        """Async execute should handle five parallel 500 KiB stdout commands."""1839        if not self.has_async:1840            pytest.skip("Async tests not supported.")18411842        command = "python -c \"import sys; sys.stdout.write('x' * (500 * 1024))\""1843        if sys.version_info >= (3, 11):1844            tasks: list[asyncio.Task[ExecuteResponse]] = []1845            async with asyncio.TaskGroup() as tg:1846                tasks.extend(1847                    tg.create_task(sandbox_backend.aexecute(command)) for _ in range(5)1848                )18491850            for task in tasks:1851                result = task.result()1852                assert result.exit_code == 01853                assert result.truncated is False1854                assert len(result.output) >= 500 * 10241855                assert result.output.startswith("x")1856        else:1857            pytest.skip("asyncio.TaskGroup requires Python 3.11+")18581859    async def test_aupload_adownload_large_file_roundtrip(1860        self, sandbox_backend: SandboxBackendProtocol, sandbox_test_root: str1861    ) -> None:1862        """Async upload/download should preserve a ~10 MiB payload exactly."""1863        if not self.has_async:1864            pytest.skip("Async tests not supported.")18651866        test_path = self.sandbox_path(1867            "large_async_upload.bin", root_dir=sandbox_test_root1868        )1869        chunk = b"0123456789abcdef" * 10241870        repeat_count = 6401871        test_content = chunk * repeat_count18721873        assert len(test_content) == 10 * 1024 * 102418741875        upload_responses = await sandbox_backend.aupload_files(1876            [(test_path, test_content)]1877        )1878        assert upload_responses == [FileUploadResponse(path=test_path, error=None)]18791880        exec_result = await sandbox_backend.aexecute(f"wc -c {_quote(test_path)}")1881        assert exec_result.exit_code == 01882        assert str(len(test_content)) in exec_result.output18831884        download_responses = await sandbox_backend.adownload_files([test_path])1885        assert download_responses == [1886            FileDownloadResponse(path=test_path, content=test_content, error=None)1887        ]

Code quality findings 97

Ensure functions have docstrings for documentation
missing-docstring
def sandbox(self) -> Iterator[SandboxBackendProtocol]:
Ensure functions have docstrings for documentation
missing-docstring
def sandbox_backend(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_new_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_basic_file(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
def test_read_binary_file(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
def test_read_binary_file_100_kib(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
def test_read_binary_file_1_mib_returns_error(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
def test_execute_large_stdout_payload(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_single_occurrence(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(file_result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_lists_files(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob(
Use logging module for better control and configurability
print-statement
self.sandbox_path("x.py", root_dir=sandbox_test_root), "print('x')"
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_literal(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_single_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_download_single_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_download_roundtrip(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_multiple_files_order_preserved(
Ensure functions have docstrings for documentation
missing-docstring
def test_download_multiple_files_order_preserved(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_binary_content_roundtrip(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_large_file_reports_expected_size(
Ensure functions have docstrings for documentation
missing-docstring
def test_download_error_file_not_found(
Ensure functions have docstrings for documentation
missing-docstring
def test_download_error_is_directory(
Ensure functions have docstrings for documentation
missing-docstring
def test_download_error_permission_denied(
Ensure functions have docstrings for documentation
missing-docstring
def test_download_error_invalid_path_relative(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_missing_parent_dir_or_roundtrip(
Ensure functions have docstrings for documentation
missing-docstring
def test_upload_relative_path_returns_invalid_path(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_creates_parent_dirs(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_existing_file_fails(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_special_characters(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_empty_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_path_with_spaces(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_unicode_content(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_consecutive_slashes_in_path(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_very_long_content(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_content_with_only_newlines(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_nonexistent_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_empty_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_with_offset(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_with_limit(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_with_offset_and_limit(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_unicode_content(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_file_with_very_long_lines(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_with_zero_limit(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_offset_beyond_file_length(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_offset_at_exact_file_length(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_very_large_file_in_chunks(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_multiple_occurrences_without_replace_all(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_multiple_occurrences_with_replace_all(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_string_not_found(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_nonexistent_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_special_characters(
Ensure functions have docstrings for documentation
missing-docstring
def test_edit_multiline_support(
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_lists_nested_directories(
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_unicode_filenames(
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_large_directory(
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_path_with_trailing_slash(
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_special_characters_in_filenames(
Ensure functions have docstrings for documentation
missing-docstring
def test_ls_path_is_sanitized(
Ensure functions have docstrings for documentation
missing-docstring
def test_read_path_is_sanitized(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_basic_search(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_with_glob_pattern(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_no_matches(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_multiple_matches_per_file(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_literal_string_matching(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_unicode_pattern(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_case_sensitivity(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_with_special_characters(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_empty_directory(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_across_nested_directories(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_with_globstar_include_pattern(
Ensure functions have docstrings for documentation
missing-docstring
def test_grep_reports_correct_line_numbers(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_basic_pattern(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_recursive_pattern(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_no_matches(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_with_directories(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_hidden_files_explicitly(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_with_character_class(
Ensure functions have docstrings for documentation
missing-docstring
def test_glob_with_question_mark(
Ensure functions have docstrings for documentation
missing-docstring
async def test_awrite_aread_large_text_payload(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(read_result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
async def test_aread_large_text_payload_paginated_roundtrip(
Ensure functions have docstrings for documentation
missing-docstring
async def test_adownload_large_text_payload_roundtrip(
Ensure functions have docstrings for documentation
missing-docstring
def test_write_read_download_large_text_with_escaped_content(
Ensure functions have docstrings for documentation
missing-docstring
async def test_awrite_aread_adownload_large_text_with_escaped_content(
Ensure functions have docstrings for documentation
missing-docstring
async def test_aread_binary_image_file(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
async def test_aread_binary_file_100_kib(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
async def test_aread_binary_file_1_mib_returns_error(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
assert isinstance(result, ReadResult)
Ensure functions have docstrings for documentation
missing-docstring
async def test_aexecute_large_stdout_payload(
Ensure functions have docstrings for documentation
missing-docstring
async def test_aupload_adownload_large_file_roundtrip(

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.