返回 DeepSeek-Reasonix
search_test.go
根目录 / internal / fileref / search_test.go
1 package fileref
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7 )
8
9 // writeFile is a tiny helper that creates a regular file with placeholder
10 // content. Tests use it to scaffold the workspace layout before each Search
11 // call.
12 func writeFile(t *testing.T, path string) {
13 t.Helper()
14 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
15 t.Fatalf("mkdir: %v", err)
16 }
17 if err := os.WriteFile(path, []byte("ok"), 0o644); err != nil {
18 t.Fatalf("write: %v", err)
19 }
20 }
21
22 // resultPaths extracts just the Path fields from a slice of SearchResult for
23 // easy assertion against expected path lists.
24 func resultPaths(results []SearchResult) []string {
25 paths := make([]string, len(results))
26 for i, r := range results {
27 paths[i] = r.Path
28 }
29 return paths
30 }
31
32 // containsPath reports whether want appears in the results by Path field.
33 func containsPath(got []SearchResult, want string) bool {
34 for _, r := range got {
35 if r.Path == want {
36 return true
37 }
38 }
39 return false
40 }
41
42 // containsDirHit reports whether a result with the given Path and IsDir=true
43 // exists in the results.
44 func containsDirHit(got []SearchResult, wantPath string) bool {
45 for _, r := range got {
46 if r.Path == wantPath && r.IsDir {
47 return true
48 }
49 }
50 return false
51 }
52
53 // TestSearchMatchesPathSegment verifies the fix for issue #3769: a query
54 // matching an intermediate directory segment (here "planind") should now
55 // surface files under that directory, even when the basename does not
56 // contain the query.
57 func TestSearchMatchesPathSegment(t *testing.T) {
58 root := t.TempDir()
59 writeFile(t, filepath.Join(root, "src", "planind", "index.tsx"))
60
61 got := Search(root, "planind", 50)
62 if !containsPath(got, "src/planind/index.tsx") {
63 t.Fatalf("Search(%q) should return %q (path-segment match), got %v", "planind", "src/planind/index.tsx", resultPaths(got))
64 }
65 }
66
67 // TestSearchMatchesDirectories verifies that a query matching a directory
68 // name returns the directory itself with IsDir=true, not just its contents.
69 func TestSearchMatchesDirectories(t *testing.T) {
70 root := t.TempDir()
71 writeFile(t, filepath.Join(root, "docs", "assets", "readme.md"))
72 writeFile(t, filepath.Join(root, "docs", "assets", "image.png"))
73
74 got := Search(root, "assets", 50)
75 if !containsDirHit(got, "docs/assets") {
76 t.Fatalf("Search(%q) should return directory %q with IsDir=true, got %v", "assets", "docs/assets", resultPaths(got))
77 }
78 // Directory hits come after basename and segment hits.
79 if !containsPath(got, "docs/assets/readme.md") {
80 t.Fatalf("Search(%q) should still return files under the directory, got %v", "assets", resultPaths(got))
81 }
82 }
83
84 // TestSearchKeepsBasenameMatch guards the legacy behavior: when the query
85 // matches the file's basename, the file must still appear in the results,
86 // and basename hits must sort strictly before path-segment and directory
87 // hits so the most relevant matches surface at the top of the completion
88 // menu.
89 func TestSearchKeepsBasenameMatch(t *testing.T) {
90 root := t.TempDir()
91 writeFile(t, filepath.Join(root, "planind.go"))
92 writeFile(t, filepath.Join(root, "src", "planind", "index.tsx")) // also a segment hit
93
94 got := Search(root, "planind", 50)
95 gotPaths := resultPaths(got)
96 want := []string{"src/planind", "planind.go", "src/planind/index.tsx"}
97 if !equalSlices(gotPaths, want) {
98 t.Fatalf("Search(%q) order mismatch:\n want %v\n got %v", "planind", want, gotPaths)
99 }
100 // Verify the directory hit has IsDir=true.
101 if !containsDirHit(got, "src/planind") {
102 t.Fatalf("Search(%q) should return %q with IsDir=true", "planind", "src/planind")
103 }
104 }
105
106 // equalSlices reports whether two []string are element-wise equal.
107 func equalSlices(a, b []string) bool {
108 if len(a) != len(b) {
109 return false
110 }
111 for i := range a {
112 if a[i] != b[i] {
113 return false
114 }
115 }
116 return true
117 }
118
119 // TestSearchHandlesBasenamePathQuery verifies that searching for the basename
120 // part of a nested file still surfaces the file (regression guard).
121 func TestSearchHandlesBasenamePathQuery(t *testing.T) {
122 root := t.TempDir()
123 writeFile(t, filepath.Join(root, "src", "planind", "index.tsx"))
124
125 got := Search(root, "index", 50)
126 if !containsPath(got, "src/planind/index.tsx") {
127 t.Fatalf("Search(%q) should return %q (basename of nested file), got %v", "index", "src/planind/index.tsx", resultPaths(got))
128 }
129 }
130
131 // TestSearchSkipsNoiseStillWorks ensures the noise-directory skip list still
132 // applies to path-segment matches. Files under node_modules must not surface
133 // even when an intermediate segment matches the query.
134 func TestSearchSkipsNoiseStillWorks(t *testing.T) {
135 root := t.TempDir()
136 writeFile(t, filepath.Join(root, "src", "planind", "index.tsx")) // legitimate hit
137 writeFile(t, filepath.Join(root, "node_modules", "planind", "index.tsx")) // must be skipped
138 writeFile(t, filepath.Join(root, "build", "planind", "index.tsx")) // skipDirNames
139 writeFile(t, filepath.Join(root, "dist", "planind", "index.tsx")) // skipDirNames
140
141 got := Search(root, "planind", 50)
142 if containsPath(got, "node_modules/planind/index.tsx") {
143 t.Fatalf("Search should skip node_modules, got %v", resultPaths(got))
144 }
145 if containsPath(got, "build/planind/index.tsx") {
146 t.Fatalf("Search should skip build/, got %v", resultPaths(got))
147 }
148 if containsPath(got, "dist/planind/index.tsx") {
149 t.Fatalf("Search should skip dist/, got %v", resultPaths(got))
150 }
151 if !containsPath(got, "src/planind/index.tsx") {
152 t.Fatalf("Search should still return legitimate hit, got %v", resultPaths(got))
153 }
154 }
155
155 lines GO