package builtin import ( "context" "encoding/json" "os" "path/filepath" "testing" ) func TestGitignoreTool_Add_NewFile(t *testing.T) { dir := t.TempDir() tool := NewGitignoreTool(dir) input, _ := json.Marshal(map[string]any{ "action": "add", "patterns": []string{"*.log", "dist/"}, }) res, err := tool.Execute(context.Background(), input, nil) if err != nil { t.Fatal(err) } if res.IsError { t.Fatalf("unexpected error: %s", res.Output) } content, _ := os.ReadFile(filepath.Join(dir, ".gitignore")) if !containsLine(string(content), "*.log") || !containsLine(string(content), "dist/") { t.Errorf("expected patterns in .gitignore, got: %s", content) } } func TestGitignoreTool_Add_Idempotent(t *testing.T) { dir := t.TempDir() tool := NewGitignoreTool(dir) input, _ := json.Marshal(map[string]any{ "action": "add", "patterns": []string{"*.log"}, }) // 添加两次 tool.Execute(context.Background(), input, nil) tool.Execute(context.Background(), input, nil) content, _ := os.ReadFile(filepath.Join(dir, ".gitignore")) count := countLines(string(content), "*.log") if count != 1 { t.Errorf("expected 1 occurrence of *.log, got %d", count) } } func TestGitignoreTool_Add_Append(t *testing.T) { dir := t.TempDir() os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("node_modules/\n"), 0644) tool := NewGitignoreTool(dir) input, _ := json.Marshal(map[string]any{ "action": "add", "patterns": []string{"*.tmp"}, }) tool.Execute(context.Background(), input, nil) content, _ := os.ReadFile(filepath.Join(dir, ".gitignore")) if !containsLine(string(content), "node_modules/") || !containsLine(string(content), "*.tmp") { t.Errorf("expected both patterns, got: %s", content) } } func TestGitignoreTool_Check_Ignored(t *testing.T) { dir := t.TempDir() os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.log\n"), 0644) tool := NewGitignoreTool(dir) input, _ := json.Marshal(map[string]any{ "action": "check", "path": "app.log", }) res, err := tool.Execute(context.Background(), input, nil) if err != nil || res.IsError { t.Fatalf("unexpected error: %v %s", err, res.Output) } dataMap, ok := res.Data.(map[string]any) if !ok { t.Fatalf("expected Data to be map, got %T", res.Data) } if ignored, _ := dataMap["ignored"].(bool); !ignored { t.Errorf("expected ignored=true, got output: %s", res.Output) } } func TestGitignoreTool_Check_NotIgnored(t *testing.T) { dir := t.TempDir() os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.log\n"), 0644) tool := NewGitignoreTool(dir) input, _ := json.Marshal(map[string]any{ "action": "check", "path": "main.go", }) res, _ := tool.Execute(context.Background(), input, nil) dataMap, ok := res.Data.(map[string]any) if !ok { t.Fatalf("expected Data to be map, got %T", res.Data) } if ignored, _ := dataMap["ignored"].(bool); ignored { t.Errorf("expected ignored=false, got output: %s", res.Output) } } func TestGitignoreTool_InvalidAction(t *testing.T) { tool := NewGitignoreTool(t.TempDir()) input, _ := json.Marshal(map[string]any{"action": "delete"}) res, _ := tool.Execute(context.Background(), input, nil) if !res.IsError { t.Error("expected error for unknown action") } } // containsLine checks if s contains line as a complete line. func containsLine(s, line string) bool { for _, l := range splitLines(s) { if l == line { return true } } return false } func countLines(s, line string) int { n := 0 for _, l := range splitLines(s) { if l == line { n++ } } return n } func splitLines(s string) []string { var lines []string for _, l := range filepath.SplitList(s) { _ = l } // 用 strings.Split 实现 cur := "" for _, ch := range s { if ch == '\n' { lines = append(lines, cur) cur = "" } else { cur += string(ch) } } if cur != "" { lines = append(lines, cur) } return lines }