package tool import ( "context" "os" "path/filepath" "strings" "testing" "github.com/orca/orca/pkg/sandbox" ) func TestExecTool(t *testing.T) { sb := sandbox.NewProcessSandbox() execT := NewExecTool(sb) if execT.Name() != "exec" { t.Errorf("expected name 'exec', got %q", execT.Name()) } if execT.Description() == "" { t.Error("expected non-empty description") } params := execT.Parameters() if _, ok := params["command"]; !ok { t.Error("expected 'command' parameter") } } func TestExecToolExecute(t *testing.T) { sb := sandbox.NewProcessSandbox() execT := NewExecTool(sb) ctx := context.Background() result, err := execT.Execute(ctx, map[string]interface{}{ "command": "echo hello", }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success, got error: %s", result.Error) } data := result.Data.(map[string]interface{}) stdout := data["stdout"].(string) if strings.TrimSpace(stdout) != "hello" { t.Errorf("expected stdout 'hello', got %q", stdout) } } func TestExecToolMissingCommand(t *testing.T) { sb := sandbox.NewProcessSandbox() execT := NewExecTool(sb) ctx := context.Background() result, err := execT.Execute(ctx, map[string]interface{}{}) if err != nil { t.Fatalf("Execute should not error for invalid args: %v", err) } if result.Success { t.Error("expected failure for missing command") } if !strings.Contains(result.Error, "command") { t.Errorf("error should mention 'command', got: %s", result.Error) } } func TestReadFileTool(t *testing.T) { readT := NewReadFileTool() if readT.Name() != "read_file" { t.Errorf("expected name 'read_file', got %q", readT.Name()) } } func TestReadFileToolExecute(t *testing.T) { // Create a temp file tmpFile, err := os.CreateTemp("", "orca-test-*") if err != nil { t.Fatalf("failed to create temp file: %v", err) } content := "test content\nline 2\n" if _, err := tmpFile.WriteString(content); err != nil { t.Fatalf("failed to write temp file: %v", err) } tmpFile.Close() defer os.Remove(tmpFile.Name()) readT := NewReadFileTool() ctx := context.Background() result, err := readT.Execute(ctx, map[string]interface{}{ "path": tmpFile.Name(), }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success, got error: %s", result.Error) } data := result.Data.(map[string]interface{}) gotContent := data["content"].(string) if gotContent != content { t.Errorf("expected content %q, got %q", content, gotContent) } } func TestReadFileToolMissingPath(t *testing.T) { readT := NewReadFileTool() ctx := context.Background() result, err := readT.Execute(ctx, map[string]interface{}{}) if err != nil { t.Fatalf("Execute should not error for invalid args: %v", err) } if result.Success { t.Error("expected failure for missing path") } } func TestReadFileToolNonexistent(t *testing.T) { readT := NewReadFileTool() ctx := context.Background() result, err := readT.Execute(ctx, map[string]interface{}{ "path": "/nonexistent/path/that/does/not/exist.txt", }) if err != nil { t.Fatalf("Execute should not error for missing file: %v", err) } if result.Success { t.Error("expected failure for nonexistent file") } } func TestWriteFileTool(t *testing.T) { writeT := NewWriteFileTool() if writeT.Name() != "write_file" { t.Errorf("expected name 'write_file', got %q", writeT.Name()) } } func TestWriteFileToolExecute(t *testing.T) { tmpDir, err := os.MkdirTemp("", "orca-write-test-*") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(tmpDir) testPath := filepath.Join(tmpDir, "nested", "test.txt") content := "hello world" writeT := NewWriteFileTool() ctx := context.Background() result, err := writeT.Execute(ctx, map[string]interface{}{ "path": testPath, "content": content, }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success, got error: %s", result.Error) } // Verify the file was written data, err := os.ReadFile(testPath) if err != nil { t.Fatalf("failed to read written file: %v", err) } if string(data) != content { t.Errorf("expected content %q, got %q", content, string(data)) } } func TestWriteFileToolMissingArgs(t *testing.T) { writeT := NewWriteFileTool() ctx := context.Background() // Missing path result, err := writeT.Execute(ctx, map[string]interface{}{ "content": "test", }) if err != nil { t.Fatalf("Execute should not error for invalid args: %v", err) } if result.Success { t.Error("expected failure for missing path") } // Missing content result, err = writeT.Execute(ctx, map[string]interface{}{ "path": "/tmp/test.txt", }) if err != nil { t.Fatalf("Execute should not error for invalid args: %v", err) } if result.Success { t.Error("expected failure for missing content") } } func TestListDirTool(t *testing.T) { listT := NewListDirTool() if listT.Name() != "list_dir" { t.Errorf("expected name 'list_dir', got %q", listT.Name()) } } func TestListDirToolExecute(t *testing.T) { tmpDir, err := os.MkdirTemp("", "orca-list-test-*") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(tmpDir) // Create some test files os.WriteFile(filepath.Join(tmpDir, "a.txt"), []byte("a"), 0644) os.WriteFile(filepath.Join(tmpDir, "b.txt"), []byte("bb"), 0644) os.Mkdir(filepath.Join(tmpDir, "subdir"), 0755) listT := NewListDirTool() ctx := context.Background() result, err := listT.Execute(ctx, map[string]interface{}{ "path": tmpDir, }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success, got error: %s", result.Error) } data := result.Data.(map[string]interface{}) count := data["count"].(int) if count != 3 { t.Errorf("expected 3 entries, got %d", count) } } func TestListDirToolRecursive(t *testing.T) { tmpDir, err := os.MkdirTemp("", "orca-list-rec-*") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(tmpDir) os.MkdirAll(filepath.Join(tmpDir, "a", "b"), 0755) os.WriteFile(filepath.Join(tmpDir, "a", "b", "c.txt"), []byte("c"), 0644) listT := NewListDirTool() ctx := context.Background() result, err := listT.Execute(ctx, map[string]interface{}{ "path": tmpDir, "recursive": true, }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success, got error: %s", result.Error) } data := result.Data.(map[string]interface{}) entries := data["entries"].([]map[string]interface{}) if len(entries) < 2 { t.Errorf("expected at least 2 recursive entries, got %d", len(entries)) } } func TestListDirToolNonexistent(t *testing.T) { listT := NewListDirTool() ctx := context.Background() result, err := listT.Execute(ctx, map[string]interface{}{ "path": "/nonexistent/path", }) if err != nil { t.Fatalf("Execute should not error for missing path: %v", err) } if result.Success { t.Error("expected failure for nonexistent path") } } func TestSearchFilesTool(t *testing.T) { searchT := NewSearchFilesTool() if searchT.Name() != "search_files" { t.Errorf("expected name 'search_files', got %q", searchT.Name()) } } func TestSearchFilesToolExecute(t *testing.T) { tmpDir, err := os.MkdirTemp("", "orca-search-test-*") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(tmpDir) // Create test files os.WriteFile(filepath.Join(tmpDir, "findme.go"), []byte("package main\nfunc hello() {\n}\n"), 0644) os.WriteFile(filepath.Join(tmpDir, "other.py"), []byte("def world():\n pass\n"), 0644) searchT := NewSearchFilesTool() ctx := context.Background() result, err := searchT.Execute(ctx, map[string]interface{}{ "pattern": "hello", "path": tmpDir, }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success, got error: %s", result.Error) } data := result.Data.(map[string]interface{}) count := data["count"].(int) if count != 1 { t.Errorf("expected 1 match for 'hello', got %d", count) } } func TestSearchFilesToolNoMatch(t *testing.T) { tmpDir, err := os.MkdirTemp("", "orca-search-nomatch-*") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(tmpDir) os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("nothing here"), 0644) searchT := NewSearchFilesTool() ctx := context.Background() result, err := searchT.Execute(ctx, map[string]interface{}{ "pattern": "nonexistent-pattern-xyz", "path": tmpDir, }) if err != nil { t.Fatalf("Execute failed: %v", err) } if !result.Success { t.Errorf("expected success even with no matches, got error: %s", result.Error) } data := result.Data.(map[string]interface{}) count := data["count"].(int) if count != 0 { t.Errorf("expected 0 matches, got %d", count) } } func TestSearchFilesToolMissingPattern(t *testing.T) { searchT := NewSearchFilesTool() ctx := context.Background() result, err := searchT.Execute(ctx, map[string]interface{}{}) if err != nil { t.Fatalf("Execute should not error for invalid args: %v", err) } if result.Success { t.Error("expected failure for missing pattern") } } func TestToolInterfaceSatisfied(t *testing.T) { sb := sandbox.NewProcessSandbox() tools := []Tool{ NewExecTool(sb), NewReadFileTool(), NewWriteFileTool(), NewListDirTool(), NewSearchFilesTool(), } names := []string{"exec", "read_file", "write_file", "list_dir", "search_files"} for i, tool := range tools { if tool.Name() != names[i] { t.Errorf("expected name %q, got %q", names[i], tool.Name()) } if tool.Description() == "" { t.Errorf("tool %q has empty description", names[i]) } if tool.Parameters() == nil { t.Errorf("tool %q has nil parameters", names[i]) } } }