Files
crewAI/lib/crewai-tools/tests/tools/glob_tool_test.py
2026-02-04 12:34:13 -08:00

183 lines
6.7 KiB
Python

"""Unit tests for GlobTool."""
from pathlib import Path
import pytest
from crewai_tools import GlobTool
@pytest.fixture
def sample_dir(tmp_path: Path) -> Path:
"""Create a temp directory with sample files for testing."""
# src/main.py
src = tmp_path / "src"
src.mkdir()
(src / "main.py").write_text("print('hello')")
(src / "utils.py").write_text("def helper(): pass")
# src/components/button.tsx
components = src / "components"
components.mkdir()
(components / "button.tsx").write_text("export const Button = () => {}")
(components / "input.tsx").write_text("export const Input = () => {}")
# tests/test_main.py
tests = tmp_path / "tests"
tests.mkdir()
(tests / "test_main.py").write_text("def test_hello(): pass")
(tests / "test_utils.py").write_text("def test_helper(): pass")
# config files
(tmp_path / "config.yaml").write_text("key: value")
(tmp_path / "settings.json").write_text("{}")
# hidden file
(tmp_path / ".hidden").write_text("secret")
# empty directory
(tmp_path / "empty_dir").mkdir()
return tmp_path
class TestGlobTool:
"""Tests for GlobTool."""
def setup_method(self) -> None:
"""Set up test fixtures."""
self.tool = GlobTool()
def test_tool_metadata(self) -> None:
"""Test tool has correct name and description."""
assert self.tool.name == "glob"
assert "pattern" in self.tool.description.lower()
def test_args_schema(self) -> None:
"""Test that args_schema has correct fields."""
schema = self.tool.args_schema
fields = schema.model_fields
assert "pattern" in fields
assert fields["pattern"].is_required()
assert "path" in fields
assert not fields["path"].is_required()
assert "output_mode" in fields
assert not fields["output_mode"].is_required()
assert "include_hidden" in fields
assert not fields["include_hidden"].is_required()
def test_find_python_files(self, sample_dir: Path) -> None:
"""Test finding Python files with *.py pattern."""
result = self.tool._run(pattern="*.py", path=str(sample_dir))
assert "main.py" in result
assert "utils.py" in result
assert "test_main.py" in result
assert "test_utils.py" in result
def test_find_specific_extension(self, sample_dir: Path) -> None:
"""Test finding files with specific extension."""
result = self.tool._run(pattern="*.tsx", path=str(sample_dir))
assert "button.tsx" in result
assert "input.tsx" in result
assert "main.py" not in result
def test_find_test_files(self, sample_dir: Path) -> None:
"""Test finding test files with test_*.py pattern."""
result = self.tool._run(pattern="test_*.py", path=str(sample_dir))
assert "test_main.py" in result
assert "test_utils.py" in result
# Verify non-test files are not included (check for exact filename, not substring)
lines = result.split("\n")
file_lines = [l for l in lines if l.endswith(".py")]
assert not any(l.endswith("/main.py") or l == "main.py" for l in file_lines)
assert not any(l.endswith("/utils.py") or l == "utils.py" for l in file_lines)
def test_recursive_pattern(self, sample_dir: Path) -> None:
"""Test explicit recursive pattern **/*.py."""
result = self.tool._run(pattern="**/*.py", path=str(sample_dir))
assert "main.py" in result
assert "test_main.py" in result
def test_hidden_files_excluded_by_default(self, sample_dir: Path) -> None:
"""Test hidden files are excluded by default."""
result = self.tool._run(pattern="*", path=str(sample_dir))
assert ".hidden" not in result
def test_hidden_files_included(self, sample_dir: Path) -> None:
"""Test hidden files included when include_hidden=True."""
result = self.tool._run(
pattern=".*", path=str(sample_dir), include_hidden=True
)
assert ".hidden" in result
def test_output_mode_paths(self, sample_dir: Path) -> None:
"""Test paths output mode shows file paths."""
result = self.tool._run(
pattern="*.py", path=str(sample_dir), output_mode="paths"
)
# Should contain full paths
assert str(sample_dir) in result or "main.py" in result
def test_output_mode_detailed(self, sample_dir: Path) -> None:
"""Test detailed output mode shows sizes."""
result = self.tool._run(
pattern="*.py", path=str(sample_dir), output_mode="detailed"
)
# Should contain size indicators
assert "B" in result # Bytes indicator
def test_output_mode_tree(self, sample_dir: Path) -> None:
"""Test tree output mode shows directory structure."""
result = self.tool._run(
pattern="*.py", path=str(sample_dir), output_mode="tree"
)
assert str(sample_dir) in result
def test_dirs_only(self, sample_dir: Path) -> None:
"""Test dirs_only=True only returns directories."""
result = self.tool._run(
pattern="*",
path=str(sample_dir),
dirs_only=True,
files_only=False,
)
assert "src" in result or "tests" in result or "empty_dir" in result
# Should not contain file extensions
assert ".py" not in result
assert ".yaml" not in result
def test_path_not_found(self) -> None:
"""Test error message when path doesn't exist."""
result = self.tool._run(pattern="*.py", path="/nonexistent/path")
assert "Error" in result
assert "does not exist" in result
def test_path_is_file(self, sample_dir: Path) -> None:
"""Test error message when path is a file, not directory."""
file_path = sample_dir / "config.yaml"
result = self.tool._run(pattern="*.py", path=str(file_path))
assert "Error" in result
assert "not a directory" in result
def test_no_matches(self, sample_dir: Path) -> None:
"""Test message when no files match pattern."""
result = self.tool._run(pattern="*.xyz", path=str(sample_dir))
assert "No files found" in result
def test_found_count_in_output(self, sample_dir: Path) -> None:
"""Test that result includes count of found files."""
result = self.tool._run(pattern="*.py", path=str(sample_dir))
assert "Found" in result
assert "file(s)" in result
def test_run_with_kwargs(self, sample_dir: Path) -> None:
"""Test _run ignores extra kwargs."""
result = self.tool._run(
pattern="*.py", path=str(sample_dir), extra_arg="ignored"
)
assert "main.py" in result