# coding=utf-8 """ @Time :2026/6/30 @Author :tao.chen """ from pathlib import Path import pytest from files_mcp.core import path_guard from files_mcp.tools import files @pytest.fixture def sandbox_root(tmp_path, monkeypatch): monkeypatch.setenv("FILES_MCP_ROOT", str(tmp_path)) path_guard.set_root_for_testing(tmp_path.resolve()) return tmp_path.resolve() class TestCreateFile: def test_creates_file(self, sandbox_root: Path): result = files.create_file(path="new.txt", content="hello") assert result["status"] == "CREATED" assert (sandbox_root / "new.txt").read_text(encoding="utf-8") == "hello" def test_escape_raises_value_error(self, sandbox_root: Path): with pytest.raises(ValueError, match="escapes sandbox"): files.create_file(path="../escape.txt", content="x") def test_existing_raises_file_exists(self, sandbox_root: Path): (sandbox_root / "f.txt").write_text("old", encoding="utf-8") with pytest.raises(FileExistsError): files.create_file(path="f.txt", content="new", overwrite=False) class TestReadFile: def test_reads_existing(self, sandbox_root: Path): (sandbox_root / "f.txt").write_text("hi", encoding="utf-8") out = files.read_file(path="f.txt") assert out["content"] == "hi" assert out["encoding"] == "utf-8" def test_escape_raises_value_error(self, sandbox_root: Path): with pytest.raises(ValueError, match="escapes sandbox"): files.read_file(path="/etc/passwd") class TestUpdateFile: def test_updates_existing(self, sandbox_root: Path): (sandbox_root / "f.txt").write_text("old", encoding="utf-8") result = files.update_file(path="f.txt", content="new") assert result["status"] == "UPDATED" assert (sandbox_root / "f.txt").read_text(encoding="utf-8") == "new" def test_missing_raises_keyerror(self, sandbox_root: Path): with pytest.raises(KeyError, match="not found"): files.update_file(path="nope.txt", content="x") class TestDeleteFile: def test_deletes_file(self, sandbox_root: Path): (sandbox_root / "f.txt").write_text("x", encoding="utf-8") result = files.delete_file(path="f.txt") assert result["status"] == "DELETED" class TestListDir: def test_returns_entries(self, sandbox_root: Path): (sandbox_root / "a.txt").write_text("a", encoding="utf-8") (sandbox_root / "b").mkdir() out = files.list_dir(path=".", recursive=False, max_depth=1) assert out["path"] == str(sandbox_root) names = sorted(e["name"] for e in out["entries"]) assert names == ["a.txt", "b"] class TestStat: def test_returns_file_entry(self, sandbox_root: Path): (sandbox_root / "f.txt").write_text("hi", encoding="utf-8") out = files.stat(path="f.txt") assert out["name"] == "f.txt" assert out["type"] == "file" class TestSearch: def test_finds_matches(self, sandbox_root: Path): (sandbox_root / "a.py").write_text("a", encoding="utf-8") (sandbox_root / "b.txt").write_text("b", encoding="utf-8") out = files.search(path=".", pattern="*.py", recursive=False) assert out["pattern"] == "*.py" assert len(out["matches"]) == 1 assert out["matches"][0].endswith("a.py") class TestMove: def test_moves_file(self, sandbox_root: Path): (sandbox_root / "a.txt").write_text("x", encoding="utf-8") result = files.move(src="a.txt", dst="b.txt") assert result["status"] == "MOVED" assert not (sandbox_root / "a.txt").exists() assert (sandbox_root / "b.txt").exists() def test_escape_on_dst_raises(self, sandbox_root: Path): (sandbox_root / "a.txt").write_text("x", encoding="utf-8") with pytest.raises(ValueError, match="escapes sandbox"): files.move(src="a.txt", dst="../escape.txt") class TestCopy: def test_copies_file(self, sandbox_root: Path): (sandbox_root / "a.txt").write_text("x", encoding="utf-8") result = files.copy(src="a.txt", dst="b.txt") assert result["status"] == "COPIED" assert (sandbox_root / "a.txt").exists() # source unchanged assert (sandbox_root / "b.txt").read_text(encoding="utf-8") == "x"