on 94de6bc help sign in
crush/internal/fsext/lookup_test.go
437 lines 13 kB history raw
1package fsext
3import (
4 "errors"
5 "os"
6 "path/filepath"
7 "testing"
9 "github.com/charmbracelet/crush/internal/home"
10 "github.com/stretchr/testify/require"
13func TestLookupClosest(t *testing.T) {
14 tempDir := t.TempDir()
15 t.Chdir(tempDir)
17 t.Run("target found in starting directory", func(t *testing.T) {
18 testDir := t.TempDir()
20 // Create target file in current directory
21 targetFile := filepath.Join(testDir, "target.txt")
22 err := os.WriteFile(targetFile, []byte("test"), 0o644)
23 require.NoError(t, err)
25 foundPath, found := LookupClosest(testDir, "target.txt")
26 require.True(t, found)
27 require.Equal(t, targetFile, foundPath)
28 })
30 t.Run("target found in parent directory", func(t *testing.T) {
31 testDir := t.TempDir()
33 // Create subdirectory
34 subDir := filepath.Join(testDir, "subdir")
35 err := os.Mkdir(subDir, 0o755)
36 require.NoError(t, err)
38 // Create target file in parent directory
39 targetFile := filepath.Join(testDir, "target.txt")
40 err = os.WriteFile(targetFile, []byte("test"), 0o644)
41 require.NoError(t, err)
43 foundPath, found := LookupClosest(subDir, "target.txt")
44 require.True(t, found)
45 require.Equal(t, targetFile, foundPath)
46 })
48 t.Run("target found in grandparent directory", func(t *testing.T) {
49 testDir := t.TempDir()
51 // Create nested subdirectories
52 subDir := filepath.Join(testDir, "subdir")
53 err := os.Mkdir(subDir, 0o755)
54 require.NoError(t, err)
56 subSubDir := filepath.Join(subDir, "subsubdir")
57 err = os.Mkdir(subSubDir, 0o755)
58 require.NoError(t, err)
60 // Create target file in grandparent directory
61 targetFile := filepath.Join(testDir, "target.txt")
62 err = os.WriteFile(targetFile, []byte("test"), 0o644)
63 require.NoError(t, err)
65 foundPath, found := LookupClosest(subSubDir, "target.txt")
66 require.True(t, found)
67 require.Equal(t, targetFile, foundPath)
68 })
70 t.Run("target not found", func(t *testing.T) {
71 testDir := t.TempDir()
73 foundPath, found := LookupClosest(testDir, "nonexistent.txt")
74 require.False(t, found)
75 require.Empty(t, foundPath)
76 })
78 t.Run("target directory found", func(t *testing.T) {
79 testDir := t.TempDir()
81 // Create target directory in current directory
82 targetDir := filepath.Join(testDir, "targetdir")
83 err := os.Mkdir(targetDir, 0o755)
84 require.NoError(t, err)
86 foundPath, found := LookupClosest(testDir, "targetdir")
87 require.True(t, found)
88 require.Equal(t, targetDir, foundPath)
89 })
91 t.Run("stops at home directory", func(t *testing.T) {
92 // This test is limited as we can't easily create files above home directory
93 // but we can test the behavior by searching from home directory itself
94 homeDir := home.Dir()
96 // Search for a file that doesn't exist from home directory
97 foundPath, found := LookupClosest(homeDir, "nonexistent_file_12345.txt")
98 require.False(t, found)
99 require.Empty(t, foundPath)
100 })
102 t.Run("invalid starting directory", func(t *testing.T) {
103 foundPath, found := LookupClosest("/invalid/path/that/does/not/exist", "target.txt")
104 require.False(t, found)
105 require.Empty(t, foundPath)
106 })
108 t.Run("relative path handling", func(t *testing.T) {
109 // Create target file in current directory
110 require.NoError(t, os.WriteFile("target.txt", []byte("test"), 0o644))
112 // Search using relative path
113 foundPath, found := LookupClosest(".", "target.txt")
114 require.True(t, found)
116 // Resolve symlinks to handle macOS /private/var vs /var discrepancy
117 expectedPath, err := filepath.EvalSymlinks(filepath.Join(tempDir, "target.txt"))
118 require.NoError(t, err)
119 actualPath, err := filepath.EvalSymlinks(foundPath)
120 require.NoError(t, err)
121 require.Equal(t, expectedPath, actualPath)
122 })
125func TestLookupClosestWithOwnership(t *testing.T) {
126 // Note: Testing ownership boundaries is difficult in a cross-platform way
127 // without creating complex directory structures with different owners.
128 // This test focuses on the basic functionality when ownership checks pass.
130 tempDir := t.TempDir()
131 t.Chdir(tempDir)
133 t.Run("search respects same ownership", func(t *testing.T) {
134 testDir := t.TempDir()
136 // Create subdirectory structure
137 subDir := filepath.Join(testDir, "subdir")
138 err := os.Mkdir(subDir, 0o755)
139 require.NoError(t, err)
141 // Create target file in parent directory
142 targetFile := filepath.Join(testDir, "target.txt")
143 err = os.WriteFile(targetFile, []byte("test"), 0o644)
144 require.NoError(t, err)
146 // Search should find the target assuming same ownership
147 foundPath, found := LookupClosest(subDir, "target.txt")
148 require.True(t, found)
149 require.Equal(t, targetFile, foundPath)
150 })
153func TestLookup(t *testing.T) {
154 tempDir := t.TempDir()
155 t.Chdir(tempDir)
157 t.Run("no targets returns empty slice", func(t *testing.T) {
158 testDir := t.TempDir()
160 found, err := Lookup(testDir)
161 require.NoError(t, err)
162 require.Empty(t, found)
163 })
165 t.Run("single target found in starting directory", func(t *testing.T) {
166 testDir := t.TempDir()
168 // Create target file in current directory
169 targetFile := filepath.Join(testDir, "target.txt")
170 err := os.WriteFile(targetFile, []byte("test"), 0o644)
171 require.NoError(t, err)
173 found, err := Lookup(testDir, "target.txt")
174 require.NoError(t, err)
175 require.Len(t, found, 1)
176 require.Equal(t, targetFile, found[0])
177 })
179 t.Run("multiple targets found in starting directory", func(t *testing.T) {
180 testDir := t.TempDir()
182 // Create multiple target files in current directory
183 targetFile1 := filepath.Join(testDir, "target1.txt")
184 targetFile2 := filepath.Join(testDir, "target2.txt")
185 targetFile3 := filepath.Join(testDir, "target3.txt")
187 err := os.WriteFile(targetFile1, []byte("test1"), 0o644)
188 require.NoError(t, err)
189 err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
190 require.NoError(t, err)
191 err = os.WriteFile(targetFile3, []byte("test3"), 0o644)
192 require.NoError(t, err)
194 found, err := Lookup(testDir, "target1.txt", "target2.txt", "target3.txt")
195 require.NoError(t, err)
196 require.Len(t, found, 3)
197 require.Contains(t, found, targetFile1)
198 require.Contains(t, found, targetFile2)
199 require.Contains(t, found, targetFile3)
200 })
202 t.Run("targets found in parent directories", func(t *testing.T) {
203 testDir := t.TempDir()
205 // Create subdirectory
206 subDir := filepath.Join(testDir, "subdir")
207 err := os.Mkdir(subDir, 0o755)
208 require.NoError(t, err)
210 // Create target files in parent directory
211 targetFile1 := filepath.Join(testDir, "target1.txt")
212 targetFile2 := filepath.Join(testDir, "target2.txt")
213 err = os.WriteFile(targetFile1, []byte("test1"), 0o644)
214 require.NoError(t, err)
215 err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
216 require.NoError(t, err)
218 found, err := Lookup(subDir, "target1.txt", "target2.txt")
219 require.NoError(t, err)
220 require.Len(t, found, 2)
221 require.Contains(t, found, targetFile1)
222 require.Contains(t, found, targetFile2)
223 })
225 t.Run("targets found across multiple directory levels", func(t *testing.T) {
226 testDir := t.TempDir()
228 // Create nested subdirectories
229 subDir := filepath.Join(testDir, "subdir")
230 err := os.Mkdir(subDir, 0o755)
231 require.NoError(t, err)
233 subSubDir := filepath.Join(subDir, "subsubdir")
234 err = os.Mkdir(subSubDir, 0o755)
235 require.NoError(t, err)
237 // Create target files at different levels
238 targetFile1 := filepath.Join(testDir, "target1.txt")
239 targetFile2 := filepath.Join(subDir, "target2.txt")
240 targetFile3 := filepath.Join(subSubDir, "target3.txt")
242 err = os.WriteFile(targetFile1, []byte("test1"), 0o644)
243 require.NoError(t, err)
244 err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
245 require.NoError(t, err)
246 err = os.WriteFile(targetFile3, []byte("test3"), 0o644)
247 require.NoError(t, err)
249 found, err := Lookup(subSubDir, "target1.txt", "target2.txt", "target3.txt")
250 require.NoError(t, err)
251 require.Len(t, found, 3)
252 require.Contains(t, found, targetFile1)
253 require.Contains(t, found, targetFile2)
254 require.Contains(t, found, targetFile3)
255 })
257 t.Run("some targets not found", func(t *testing.T) {
258 testDir := t.TempDir()
260 // Create only some target files
261 targetFile1 := filepath.Join(testDir, "target1.txt")
262 targetFile2 := filepath.Join(testDir, "target2.txt")
264 err := os.WriteFile(targetFile1, []byte("test1"), 0o644)
265 require.NoError(t, err)
266 err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
267 require.NoError(t, err)
269 // Search for existing and non-existing targets
270 found, err := Lookup(testDir, "target1.txt", "nonexistent.txt", "target2.txt", "another_nonexistent.txt")
271 require.NoError(t, err)
272 require.Len(t, found, 2)
273 require.Contains(t, found, targetFile1)
274 require.Contains(t, found, targetFile2)
275 })
277 t.Run("no targets found", func(t *testing.T) {
278 testDir := t.TempDir()
280 found, err := Lookup(testDir, "nonexistent1.txt", "nonexistent2.txt", "nonexistent3.txt")
281 require.NoError(t, err)
282 require.Empty(t, found)
283 })
285 t.Run("target directories found", func(t *testing.T) {
286 testDir := t.TempDir()
288 // Create target directories
289 targetDir1 := filepath.Join(testDir, "targetdir1")
290 targetDir2 := filepath.Join(testDir, "targetdir2")
291 err := os.Mkdir(targetDir1, 0o755)
292 require.NoError(t, err)
293 err = os.Mkdir(targetDir2, 0o755)
294 require.NoError(t, err)
296 found, err := Lookup(testDir, "targetdir1", "targetdir2")
297 require.NoError(t, err)
298 require.Len(t, found, 2)
299 require.Contains(t, found, targetDir1)
300 require.Contains(t, found, targetDir2)
301 })
303 t.Run("mixed files and directories", func(t *testing.T) {
304 testDir := t.TempDir()
306 // Create target files and directories
307 targetFile := filepath.Join(testDir, "target.txt")
308 targetDir := filepath.Join(testDir, "targetdir")
309 err := os.WriteFile(targetFile, []byte("test"), 0o644)
310 require.NoError(t, err)
311 err = os.Mkdir(targetDir, 0o755)
312 require.NoError(t, err)
314 found, err := Lookup(testDir, "target.txt", "targetdir")
315 require.NoError(t, err)
316 require.Len(t, found, 2)
317 require.Contains(t, found, targetFile)
318 require.Contains(t, found, targetDir)
319 })
321 t.Run("invalid starting directory", func(t *testing.T) {
322 found, err := Lookup("/invalid/path/that/does/not/exist", "target.txt")
323 require.Error(t, err)
324 require.Empty(t, found)
325 })
327 t.Run("relative path handling", func(t *testing.T) {
328 // Create target files in current directory
329 require.NoError(t, os.WriteFile("target1.txt", []byte("test1"), 0o644))
330 require.NoError(t, os.WriteFile("target2.txt", []byte("test2"), 0o644))
332 // Search using relative path
333 found, err := Lookup(".", "target1.txt", "target2.txt")
334 require.NoError(t, err)
335 require.Len(t, found, 2)
337 // Resolve symlinks to handle macOS /private/var vs /var discrepancy
338 expectedPath1, err := filepath.EvalSymlinks(filepath.Join(tempDir, "target1.txt"))
339 require.NoError(t, err)
340 expectedPath2, err := filepath.EvalSymlinks(filepath.Join(tempDir, "target2.txt"))
341 require.NoError(t, err)
343 // Check that found paths match expected paths (order may vary)
344 foundEvalSymlinks := make([]string, len(found))
345 for i, path := range found {
346 evalPath, err := filepath.EvalSymlinks(path)
347 require.NoError(t, err)
348 foundEvalSymlinks[i] = evalPath
349 }
351 require.Contains(t, foundEvalSymlinks, expectedPath1)
352 require.Contains(t, foundEvalSymlinks, expectedPath2)
353 })
356func TestProbeEnt(t *testing.T) {
357 t.Run("existing file with correct owner", func(t *testing.T) {
358 tempDir := t.TempDir()
360 // Create test file
361 testFile := filepath.Join(tempDir, "test.txt")
362 err := os.WriteFile(testFile, []byte("test"), 0o644)
363 require.NoError(t, err)
365 // Get owner of temp directory
366 owner, err := Owner(tempDir)
367 require.NoError(t, err)
369 // Test probeEnt with correct owner
370 err = probeEnt(testFile, owner)
371 require.NoError(t, err)
372 })
374 t.Run("existing directory with correct owner", func(t *testing.T) {
375 tempDir := t.TempDir()
377 // Create test directory
378 testDir := filepath.Join(tempDir, "testdir")
379 err := os.Mkdir(testDir, 0o755)
380 require.NoError(t, err)
382 // Get owner of temp directory
383 owner, err := Owner(tempDir)
384 require.NoError(t, err)
386 // Test probeEnt with correct owner
387 err = probeEnt(testDir, owner)
388 require.NoError(t, err)
389 })
391 t.Run("nonexistent file", func(t *testing.T) {
392 tempDir := t.TempDir()
394 nonexistentFile := filepath.Join(tempDir, "nonexistent.txt")
395 owner, err := Owner(tempDir)
396 require.NoError(t, err)
398 err = probeEnt(nonexistentFile, owner)
399 require.Error(t, err)
400 require.True(t, errors.Is(err, os.ErrNotExist))
401 })
403 t.Run("nonexistent file in nonexistent directory", func(t *testing.T) {
404 nonexistentFile := "/this/directory/does/not/exists/nonexistent.txt"
406 err := probeEnt(nonexistentFile, -1)
407 require.Error(t, err)
408 require.True(t, errors.Is(err, os.ErrNotExist))
409 })
411 t.Run("ownership bypass with -1", func(t *testing.T) {
412 tempDir := t.TempDir()
414 // Create test file
415 testFile := filepath.Join(tempDir, "test.txt")
416 err := os.WriteFile(testFile, []byte("test"), 0o644)
417 require.NoError(t, err)
419 // Test probeEnt with -1 (bypass ownership check)
420 err = probeEnt(testFile, -1)
421 require.NoError(t, err)
422 })
424 t.Run("ownership mismatch returns permission error", func(t *testing.T) {
425 tempDir := t.TempDir()
427 // Create test file
428 testFile := filepath.Join(tempDir, "test.txt")
429 err := os.WriteFile(testFile, []byte("test"), 0o644)
430 require.NoError(t, err)
432 // Test probeEnt with different owner (use 9999 which is unlikely to be the actual owner)
433 err = probeEnt(testFile, 9999)
434 require.Error(t, err)
435 require.True(t, errors.Is(err, os.ErrPermission))
436 })