import pytest from pathlib import Path from backend.agent.servers import mcp_server_file_search as server # ========================================================= # FIXTURES # ========================================================= @pytest.fixture() def workspace(tmp_path, monkeypatch): """ Erstellt einen isolierten Workspace für jeden Test. """ ws = tmp_path / "workspace" ws.mkdir() monkeypatch.setattr(server, "ALLOWED_DIR", ws) return ws # ========================================================= # BASIC TESTS (1–10) # ========================================================= # --------------------------------------------------------- # 1. _safe_path erlaubt gültige Pfade # --------------------------------------------------------- def test_safe_path_valid(workspace): result = server._safe_path("test.txt") assert result == workspace / "test.txt" # --------------------------------------------------------- # 2. _safe_path blockiert Path Traversal # --------------------------------------------------------- def test_safe_path_blocks_traversal(workspace): with pytest.raises(ValueError): server._safe_path("../secret.txt") # --------------------------------------------------------- # 3. list_files liefert leeren Hinweis # --------------------------------------------------------- def test_list_files_empty(workspace): result = server.list_files() assert result == "No files found in the project directory." # --------------------------------------------------------- # 4. list_files findet Dateien rekursiv # --------------------------------------------------------- def test_list_files_recursive(workspace): src = workspace / "src" src.mkdir() (src / "main.py").write_text("print('hello')") result = server.list_files() assert "src/main.py" in result # --------------------------------------------------------- # 5. read_file liest Datei korrekt # --------------------------------------------------------- def test_read_file_success(workspace): file = workspace / "hello.txt" file.write_text("Hello World") result = server.read_file("hello.txt") assert result == "Hello World" # --------------------------------------------------------- # 6. read_file erkennt fehlende Datei # --------------------------------------------------------- def test_read_file_missing(workspace): result = server.read_file("missing.txt") assert "does not exist" in result # --------------------------------------------------------- # 7. write_new_file erstellt Datei # --------------------------------------------------------- def test_write_new_file_success(workspace): result = server.write_new_file("new.txt", "content") assert "OK:" in result assert (workspace / "new.txt").exists() # --------------------------------------------------------- # 8. write_new_file verhindert Überschreiben # --------------------------------------------------------- def test_write_new_file_existing(workspace): file = workspace / "exists.txt" file.write_text("old") result = server.write_new_file("exists.txt", "new") assert "already exists" in result # --------------------------------------------------------- # 9. create_new_directory erstellt Verzeichnis # --------------------------------------------------------- def test_create_new_directory_success(workspace): result = server.create_new_directory("mydir") assert "OK:" in result assert (workspace / "mydir").is_dir() # --------------------------------------------------------- # 10. search_files findet Inhalte # --------------------------------------------------------- def test_search_files_content_match(workspace): file = workspace / "notes.txt" file.write_text("Python MCP Server") result = server.search_files("mcp") assert "[content]" in result # ========================================================= # EDGE CASE TESTS (11–20) # ========================================================= # --------------------------------------------------------- # 11. Mehrfaches Traversal blockieren # --------------------------------------------------------- def test_safe_path_double_traversal(workspace): with pytest.raises(ValueError): server._safe_path("../../../../etc/passwd") # --------------------------------------------------------- # 12. Symlink Escape verhindern # --------------------------------------------------------- def test_safe_path_symlink_escape(workspace): outside = workspace.parent / "outside" outside.mkdir() target = outside / "evil.txt" target.write_text("bad") link = workspace / "link" link.symlink_to(outside) with pytest.raises(ValueError): server._safe_path("link/evil.txt") # --------------------------------------------------------- # 13. Dateien ohne Extension blockieren # --------------------------------------------------------- def test_write_file_without_extension(workspace): result = server.write_new_file("README", "test") assert "can only write" in result # --------------------------------------------------------- # 14. Hidden Files blockieren # --------------------------------------------------------- def test_write_hidden_file(workspace): result = server.write_new_file(".env", "SECRET=123") assert "can only write" in result # --------------------------------------------------------- # 15. Binary Files korrekt behandeln # --------------------------------------------------------- def test_read_binary_file(workspace): binary = workspace / "data.bin" binary.write_bytes(b"\xFF\xFE\xFD") result = server.read_file("data.bin") assert "not a text file" in result # --------------------------------------------------------- # 16. Sehr große Zeilen durchsuchen # --------------------------------------------------------- def test_search_huge_line(workspace): huge_text = "A" * 1_000_000 + "needle" file = workspace / "huge.txt" file.write_text(huge_text) result = server.search_files("needle") assert "[content]" in result # --------------------------------------------------------- # 17. Leere Dateien lesen # --------------------------------------------------------- def test_read_empty_file(workspace): file = workspace / "empty.txt" file.write_text("") result = server.read_file("empty.txt") assert result == "" # --------------------------------------------------------- # 18. Sonderzeichen im Query # --------------------------------------------------------- def test_search_special_characters(workspace): file = workspace / "test.txt" file.write_text("hello [world] (test)") result = server.search_files("[world]") assert "[content]" in result # --------------------------------------------------------- # 19. Unicode-Dateinamen unterstützen # --------------------------------------------------------- def test_write_unicode_filename(workspace): filename = "🔥_überraschung.txt" result = server.write_new_file(filename, "unicode") assert "OK:" in result assert (workspace / filename).exists() # --------------------------------------------------------- # 20. Tiefe Verzeichnisstrukturen # --------------------------------------------------------- def test_list_files_deep_nesting(workspace): current = workspace for i in range(50): current = current / f"dir_{i}" current.mkdir() file = current / "deep.txt" file.write_text("deep") result = server.list_files() assert "deep.txt" in result