| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- package tools
- import (
- "context"
- "encoding/json"
- "os"
- "path/filepath"
- "strings"
- "testing"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- func TestLsTool_Info(t *testing.T) {
- tool := NewLsTool()
- info := tool.Info()
- assert.Equal(t, LSToolName, info.Name)
- assert.NotEmpty(t, info.Description)
- assert.Contains(t, info.Parameters, "path")
- assert.Contains(t, info.Parameters, "ignore")
- assert.Contains(t, info.Required, "path")
- }
- func TestLsTool_Run(t *testing.T) {
- // Create a temporary directory for testing
- tempDir, err := os.MkdirTemp("", "ls_tool_test")
- require.NoError(t, err)
- defer os.RemoveAll(tempDir)
- // Create a test directory structure
- testDirs := []string{
- "dir1",
- "dir2",
- "dir2/subdir1",
- "dir2/subdir2",
- "dir3",
- "dir3/.hidden_dir",
- "__pycache__",
- }
- testFiles := []string{
- "file1.txt",
- "file2.txt",
- "dir1/file3.txt",
- "dir2/file4.txt",
- "dir2/subdir1/file5.txt",
- "dir2/subdir2/file6.txt",
- "dir3/file7.txt",
- "dir3/.hidden_file.txt",
- "__pycache__/cache.pyc",
- ".hidden_root_file.txt",
- }
- // Create directories
- for _, dir := range testDirs {
- dirPath := filepath.Join(tempDir, dir)
- err := os.MkdirAll(dirPath, 0755)
- require.NoError(t, err)
- }
- // Create files
- for _, file := range testFiles {
- filePath := filepath.Join(tempDir, file)
- err := os.WriteFile(filePath, []byte("test content"), 0644)
- require.NoError(t, err)
- }
- t.Run("lists directory successfully", func(t *testing.T) {
- tool := NewLsTool()
- params := LSParams{
- Path: tempDir,
- }
- paramsJSON, err := json.Marshal(params)
- require.NoError(t, err)
- call := ToolCall{
- Name: LSToolName,
- Input: string(paramsJSON),
- }
- response, err := tool.Run(context.Background(), call)
- require.NoError(t, err)
- // Check that visible directories and files are included
- assert.Contains(t, response.Content, "dir1")
- assert.Contains(t, response.Content, "dir2")
- assert.Contains(t, response.Content, "dir3")
- assert.Contains(t, response.Content, "file1.txt")
- assert.Contains(t, response.Content, "file2.txt")
- // Check that hidden files and directories are not included
- assert.NotContains(t, response.Content, ".hidden_dir")
- assert.NotContains(t, response.Content, ".hidden_file.txt")
- assert.NotContains(t, response.Content, ".hidden_root_file.txt")
- // Check that __pycache__ is not included
- assert.NotContains(t, response.Content, "__pycache__")
- })
- t.Run("handles non-existent path", func(t *testing.T) {
- tool := NewLsTool()
- params := LSParams{
- Path: filepath.Join(tempDir, "non_existent_dir"),
- }
- paramsJSON, err := json.Marshal(params)
- require.NoError(t, err)
- call := ToolCall{
- Name: LSToolName,
- Input: string(paramsJSON),
- }
- response, err := tool.Run(context.Background(), call)
- require.NoError(t, err)
- assert.Contains(t, response.Content, "path does not exist")
- })
- t.Run("handles empty path parameter", func(t *testing.T) {
- // For this test, we need to mock the config.WorkingDirectory function
- // Since we can't easily do that, we'll just check that the response doesn't contain an error message
- tool := NewLsTool()
- params := LSParams{
- Path: "",
- }
- paramsJSON, err := json.Marshal(params)
- require.NoError(t, err)
- call := ToolCall{
- Name: LSToolName,
- Input: string(paramsJSON),
- }
- response, err := tool.Run(context.Background(), call)
- require.NoError(t, err)
- // The response should either contain a valid directory listing or an error
- // We'll just check that it's not empty
- assert.NotEmpty(t, response.Content)
- })
- t.Run("handles invalid parameters", func(t *testing.T) {
- tool := NewLsTool()
- call := ToolCall{
- Name: LSToolName,
- Input: "invalid json",
- }
- response, err := tool.Run(context.Background(), call)
- require.NoError(t, err)
- assert.Contains(t, response.Content, "error parsing parameters")
- })
- t.Run("respects ignore patterns", func(t *testing.T) {
- tool := NewLsTool()
- params := LSParams{
- Path: tempDir,
- Ignore: []string{"file1.txt", "dir1"},
- }
- paramsJSON, err := json.Marshal(params)
- require.NoError(t, err)
- call := ToolCall{
- Name: LSToolName,
- Input: string(paramsJSON),
- }
- response, err := tool.Run(context.Background(), call)
- require.NoError(t, err)
- // The output format is a tree, so we need to check for specific patterns
- // Check that file1.txt is not directly mentioned
- assert.NotContains(t, response.Content, "- file1.txt")
- // Check that dir1/ is not directly mentioned
- assert.NotContains(t, response.Content, "- dir1/")
- })
- t.Run("handles relative path", func(t *testing.T) {
- // Save original working directory
- origWd, err := os.Getwd()
- require.NoError(t, err)
- defer func() {
- os.Chdir(origWd)
- }()
- // Change to a directory above the temp directory
- parentDir := filepath.Dir(tempDir)
- err = os.Chdir(parentDir)
- require.NoError(t, err)
- tool := NewLsTool()
- params := LSParams{
- Path: filepath.Base(tempDir),
- }
- paramsJSON, err := json.Marshal(params)
- require.NoError(t, err)
- call := ToolCall{
- Name: LSToolName,
- Input: string(paramsJSON),
- }
- response, err := tool.Run(context.Background(), call)
- require.NoError(t, err)
- // Should list the temp directory contents
- assert.Contains(t, response.Content, "dir1")
- assert.Contains(t, response.Content, "file1.txt")
- })
- }
- func TestShouldSkip(t *testing.T) {
- testCases := []struct {
- name string
- path string
- ignorePatterns []string
- expected bool
- }{
- {
- name: "hidden file",
- path: "/path/to/.hidden_file",
- ignorePatterns: []string{},
- expected: true,
- },
- {
- name: "hidden directory",
- path: "/path/to/.hidden_dir",
- ignorePatterns: []string{},
- expected: true,
- },
- {
- name: "pycache directory",
- path: "/path/to/__pycache__/file.pyc",
- ignorePatterns: []string{},
- expected: true,
- },
- {
- name: "node_modules directory",
- path: "/path/to/node_modules/package",
- ignorePatterns: []string{},
- expected: false, // The shouldSkip function doesn't directly check for node_modules in the path
- },
- {
- name: "normal file",
- path: "/path/to/normal_file.txt",
- ignorePatterns: []string{},
- expected: false,
- },
- {
- name: "normal directory",
- path: "/path/to/normal_dir",
- ignorePatterns: []string{},
- expected: false,
- },
- {
- name: "ignored by pattern",
- path: "/path/to/ignore_me.txt",
- ignorePatterns: []string{"ignore_*.txt"},
- expected: true,
- },
- {
- name: "not ignored by pattern",
- path: "/path/to/keep_me.txt",
- ignorePatterns: []string{"ignore_*.txt"},
- expected: false,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- result := shouldSkip(tc.path, tc.ignorePatterns)
- assert.Equal(t, tc.expected, result)
- })
- }
- }
- func TestCreateFileTree(t *testing.T) {
- paths := []string{
- "/path/to/file1.txt",
- "/path/to/dir1/file2.txt",
- "/path/to/dir1/subdir/file3.txt",
- "/path/to/dir2/file4.txt",
- }
- tree := createFileTree(paths)
- // Check the structure of the tree
- assert.Len(t, tree, 1) // Should have one root node
- // Check the root node
- rootNode := tree[0]
- assert.Equal(t, "path", rootNode.Name)
- assert.Equal(t, "directory", rootNode.Type)
- assert.Len(t, rootNode.Children, 1)
- // Check the "to" node
- toNode := rootNode.Children[0]
- assert.Equal(t, "to", toNode.Name)
- assert.Equal(t, "directory", toNode.Type)
- assert.Len(t, toNode.Children, 3) // file1.txt, dir1, dir2
- // Find the dir1 node
- var dir1Node *TreeNode
- for _, child := range toNode.Children {
- if child.Name == "dir1" {
- dir1Node = child
- break
- }
- }
- require.NotNil(t, dir1Node)
- assert.Equal(t, "directory", dir1Node.Type)
- assert.Len(t, dir1Node.Children, 2) // file2.txt and subdir
- }
- func TestPrintTree(t *testing.T) {
- // Create a simple tree
- tree := []*TreeNode{
- {
- Name: "dir1",
- Path: "dir1",
- Type: "directory",
- Children: []*TreeNode{
- {
- Name: "file1.txt",
- Path: "dir1/file1.txt",
- Type: "file",
- },
- {
- Name: "subdir",
- Path: "dir1/subdir",
- Type: "directory",
- Children: []*TreeNode{
- {
- Name: "file2.txt",
- Path: "dir1/subdir/file2.txt",
- Type: "file",
- },
- },
- },
- },
- },
- {
- Name: "file3.txt",
- Path: "file3.txt",
- Type: "file",
- },
- }
- result := printTree(tree, "/root")
- // Check the output format
- assert.Contains(t, result, "- /root/")
- assert.Contains(t, result, " - dir1/")
- assert.Contains(t, result, " - file1.txt")
- assert.Contains(t, result, " - subdir/")
- assert.Contains(t, result, " - file2.txt")
- assert.Contains(t, result, " - file3.txt")
- }
- func TestListDirectory(t *testing.T) {
- // Create a temporary directory for testing
- tempDir, err := os.MkdirTemp("", "list_directory_test")
- require.NoError(t, err)
- defer os.RemoveAll(tempDir)
- // Create a test directory structure
- testDirs := []string{
- "dir1",
- "dir1/subdir1",
- ".hidden_dir",
- }
- testFiles := []string{
- "file1.txt",
- "file2.txt",
- "dir1/file3.txt",
- "dir1/subdir1/file4.txt",
- ".hidden_file.txt",
- }
- // Create directories
- for _, dir := range testDirs {
- dirPath := filepath.Join(tempDir, dir)
- err := os.MkdirAll(dirPath, 0755)
- require.NoError(t, err)
- }
- // Create files
- for _, file := range testFiles {
- filePath := filepath.Join(tempDir, file)
- err := os.WriteFile(filePath, []byte("test content"), 0644)
- require.NoError(t, err)
- }
- t.Run("lists files with no limit", func(t *testing.T) {
- files, truncated, err := listDirectory(tempDir, []string{}, 1000)
- require.NoError(t, err)
- assert.False(t, truncated)
- // Check that visible files and directories are included
- containsPath := func(paths []string, target string) bool {
- targetPath := filepath.Join(tempDir, target)
- for _, path := range paths {
- if strings.HasPrefix(path, targetPath) {
- return true
- }
- }
- return false
- }
- assert.True(t, containsPath(files, "dir1"))
- assert.True(t, containsPath(files, "file1.txt"))
- assert.True(t, containsPath(files, "file2.txt"))
- assert.True(t, containsPath(files, "dir1/file3.txt"))
- // Check that hidden files and directories are not included
- assert.False(t, containsPath(files, ".hidden_dir"))
- assert.False(t, containsPath(files, ".hidden_file.txt"))
- })
- t.Run("respects limit and returns truncated flag", func(t *testing.T) {
- files, truncated, err := listDirectory(tempDir, []string{}, 2)
- require.NoError(t, err)
- assert.True(t, truncated)
- assert.Len(t, files, 2)
- })
- t.Run("respects ignore patterns", func(t *testing.T) {
- files, truncated, err := listDirectory(tempDir, []string{"*.txt"}, 1000)
- require.NoError(t, err)
- assert.False(t, truncated)
- // Check that no .txt files are included
- for _, file := range files {
- assert.False(t, strings.HasSuffix(file, ".txt"), "Found .txt file: %s", file)
- }
- // But directories should still be included
- containsDir := false
- for _, file := range files {
- if strings.Contains(file, "dir1") {
- containsDir = true
- break
- }
- }
- assert.True(t, containsDir)
- })
- }
|