@@ -0,0 +1,483 @@
9+ "github.com/charmbracelet/crush/internal/home"
10+ "github.com/stretchr/testify/require"
13+func TestLookupClosest(t *testing.T) {
14+ tempDir := t.TempDir()
16+ // Change to temp directory
17+ oldWd, _ := os.Getwd()
18+ err := os.Chdir(tempDir)
19+ require.NoError(t, err)
25+ t.Run("target found in starting directory", func(t *testing.T) {
26+ testDir := t.TempDir()
28+ // Create target file in current directory
29+ targetFile := filepath.Join(testDir, "target.txt")
30+ err := os.WriteFile(targetFile, []byte("test"), 0o644)
31+ require.NoError(t, err)
33+ foundPath, found := LookupClosest(testDir, "target.txt")
34+ require.True(t, found)
35+ require.Equal(t, targetFile, foundPath)
38+ t.Run("target found in parent directory", func(t *testing.T) {
39+ testDir := t.TempDir()
41+ // Create subdirectory
42+ subDir := filepath.Join(testDir, "subdir")
43+ err := os.Mkdir(subDir, 0o755)
44+ require.NoError(t, err)
46+ // Create target file in parent directory
47+ targetFile := filepath.Join(testDir, "target.txt")
48+ err = os.WriteFile(targetFile, []byte("test"), 0o644)
49+ require.NoError(t, err)
51+ foundPath, found := LookupClosest(subDir, "target.txt")
52+ require.True(t, found)
53+ require.Equal(t, targetFile, foundPath)
56+ t.Run("target found in grandparent directory", func(t *testing.T) {
57+ testDir := t.TempDir()
59+ // Create nested subdirectories
60+ subDir := filepath.Join(testDir, "subdir")
61+ err := os.Mkdir(subDir, 0o755)
62+ require.NoError(t, err)
64+ subSubDir := filepath.Join(subDir, "subsubdir")
65+ err = os.Mkdir(subSubDir, 0o755)
66+ require.NoError(t, err)
68+ // Create target file in grandparent directory
69+ targetFile := filepath.Join(testDir, "target.txt")
70+ err = os.WriteFile(targetFile, []byte("test"), 0o644)
71+ require.NoError(t, err)
73+ foundPath, found := LookupClosest(subSubDir, "target.txt")
74+ require.True(t, found)
75+ require.Equal(t, targetFile, foundPath)
78+ t.Run("target not found", func(t *testing.T) {
79+ testDir := t.TempDir()
81+ foundPath, found := LookupClosest(testDir, "nonexistent.txt")
82+ require.False(t, found)
83+ require.Empty(t, foundPath)
86+ t.Run("target directory found", func(t *testing.T) {
87+ testDir := t.TempDir()
89+ // Create target directory in current directory
90+ targetDir := filepath.Join(testDir, "targetdir")
91+ err := os.Mkdir(targetDir, 0o755)
92+ require.NoError(t, err)
94+ foundPath, found := LookupClosest(testDir, "targetdir")
95+ require.True(t, found)
96+ require.Equal(t, targetDir, foundPath)
99+ t.Run("stops at home directory", func(t *testing.T) {
100+ // This test is limited as we can't easily create files above home directory
101+ // but we can test the behavior by searching from home directory itself
102+ homeDir := home.Dir()
104+ // Search for a file that doesn't exist from home directory
105+ foundPath, found := LookupClosest(homeDir, "nonexistent_file_12345.txt")
106+ require.False(t, found)
107+ require.Empty(t, foundPath)
110+ t.Run("invalid starting directory", func(t *testing.T) {
111+ foundPath, found := LookupClosest("/invalid/path/that/does/not/exist", "target.txt")
112+ require.False(t, found)
113+ require.Empty(t, foundPath)
116+ t.Run("relative path handling", func(t *testing.T) {
117+ testDir := t.TempDir()
119+ // Change to test directory
120+ oldWd, _ := os.Getwd()
121+ err := os.Chdir(testDir)
122+ require.NoError(t, err)
123+ defer os.Chdir(oldWd)
125+ // Create target file in current directory
126+ err = os.WriteFile("target.txt", []byte("test"), 0o644)
127+ require.NoError(t, err)
129+ // Search using relative path
130+ foundPath, found := LookupClosest(".", "target.txt")
131+ require.True(t, found)
133+ // Resolve symlinks to handle macOS /private/var vs /var discrepancy
134+ expectedPath, err := filepath.EvalSymlinks(filepath.Join(testDir, "target.txt"))
135+ require.NoError(t, err)
136+ actualPath, err := filepath.EvalSymlinks(foundPath)
137+ require.NoError(t, err)
138+ require.Equal(t, expectedPath, actualPath)
142+func TestLookupClosestWithOwnership(t *testing.T) {
143+ // Note: Testing ownership boundaries is difficult in a cross-platform way
144+ // without creating complex directory structures with different owners.
145+ // This test focuses on the basic functionality when ownership checks pass.
147+ tempDir := t.TempDir()
149+ // Change to temp directory
150+ oldWd, _ := os.Getwd()
151+ err := os.Chdir(tempDir)
152+ require.NoError(t, err)
158+ t.Run("search respects same ownership", func(t *testing.T) {
159+ testDir := t.TempDir()
161+ // Create subdirectory structure
162+ subDir := filepath.Join(testDir, "subdir")
163+ err := os.Mkdir(subDir, 0o755)
164+ require.NoError(t, err)
166+ // Create target file in parent directory
167+ targetFile := filepath.Join(testDir, "target.txt")
168+ err = os.WriteFile(targetFile, []byte("test"), 0o644)
169+ require.NoError(t, err)
171+ // Search should find the target assuming same ownership
172+ foundPath, found := LookupClosest(subDir, "target.txt")
173+ require.True(t, found)
174+ require.Equal(t, targetFile, foundPath)
178+func TestLookup(t *testing.T) {
179+ tempDir := t.TempDir()
181+ // Change to temp directory
182+ oldWd, _ := os.Getwd()
183+ err := os.Chdir(tempDir)
184+ require.NoError(t, err)
190+ t.Run("no targets returns empty slice", func(t *testing.T) {
191+ testDir := t.TempDir()
193+ found, err := Lookup(testDir)
194+ require.NoError(t, err)
195+ require.Empty(t, found)
198+ t.Run("single target found in starting directory", func(t *testing.T) {
199+ testDir := t.TempDir()
201+ // Create target file in current directory
202+ targetFile := filepath.Join(testDir, "target.txt")
203+ err := os.WriteFile(targetFile, []byte("test"), 0o644)
204+ require.NoError(t, err)
206+ found, err := Lookup(testDir, "target.txt")
207+ require.NoError(t, err)
208+ require.Len(t, found, 1)
209+ require.Equal(t, targetFile, found[0])
212+ t.Run("multiple targets found in starting directory", func(t *testing.T) {
213+ testDir := t.TempDir()
215+ // Create multiple target files in current directory
216+ targetFile1 := filepath.Join(testDir, "target1.txt")
217+ targetFile2 := filepath.Join(testDir, "target2.txt")
218+ targetFile3 := filepath.Join(testDir, "target3.txt")
220+ err := os.WriteFile(targetFile1, []byte("test1"), 0o644)
221+ require.NoError(t, err)
222+ err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
223+ require.NoError(t, err)
224+ err = os.WriteFile(targetFile3, []byte("test3"), 0o644)
225+ require.NoError(t, err)
227+ found, err := Lookup(testDir, "target1.txt", "target2.txt", "target3.txt")
228+ require.NoError(t, err)
229+ require.Len(t, found, 3)
230+ require.Contains(t, found, targetFile1)
231+ require.Contains(t, found, targetFile2)
232+ require.Contains(t, found, targetFile3)
235+ t.Run("targets found in parent directories", func(t *testing.T) {
236+ testDir := t.TempDir()
238+ // Create subdirectory
239+ subDir := filepath.Join(testDir, "subdir")
240+ err := os.Mkdir(subDir, 0o755)
241+ require.NoError(t, err)
243+ // Create target files in parent directory
244+ targetFile1 := filepath.Join(testDir, "target1.txt")
245+ targetFile2 := filepath.Join(testDir, "target2.txt")
246+ err = os.WriteFile(targetFile1, []byte("test1"), 0o644)
247+ require.NoError(t, err)
248+ err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
249+ require.NoError(t, err)
251+ found, err := Lookup(subDir, "target1.txt", "target2.txt")
252+ require.NoError(t, err)
253+ require.Len(t, found, 2)
254+ require.Contains(t, found, targetFile1)
255+ require.Contains(t, found, targetFile2)
258+ t.Run("targets found across multiple directory levels", func(t *testing.T) {
259+ testDir := t.TempDir()
261+ // Create nested subdirectories
262+ subDir := filepath.Join(testDir, "subdir")
263+ err := os.Mkdir(subDir, 0o755)
264+ require.NoError(t, err)
266+ subSubDir := filepath.Join(subDir, "subsubdir")
267+ err = os.Mkdir(subSubDir, 0o755)
268+ require.NoError(t, err)
270+ // Create target files at different levels
271+ targetFile1 := filepath.Join(testDir, "target1.txt")
272+ targetFile2 := filepath.Join(subDir, "target2.txt")
273+ targetFile3 := filepath.Join(subSubDir, "target3.txt")
275+ err = os.WriteFile(targetFile1, []byte("test1"), 0o644)
276+ require.NoError(t, err)
277+ err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
278+ require.NoError(t, err)
279+ err = os.WriteFile(targetFile3, []byte("test3"), 0o644)
280+ require.NoError(t, err)
282+ found, err := Lookup(subSubDir, "target1.txt", "target2.txt", "target3.txt")
283+ require.NoError(t, err)
284+ require.Len(t, found, 3)
285+ require.Contains(t, found, targetFile1)
286+ require.Contains(t, found, targetFile2)
287+ require.Contains(t, found, targetFile3)
290+ t.Run("some targets not found", func(t *testing.T) {
291+ testDir := t.TempDir()
293+ // Create only some target files
294+ targetFile1 := filepath.Join(testDir, "target1.txt")
295+ targetFile2 := filepath.Join(testDir, "target2.txt")
297+ err := os.WriteFile(targetFile1, []byte("test1"), 0o644)
298+ require.NoError(t, err)
299+ err = os.WriteFile(targetFile2, []byte("test2"), 0o644)
300+ require.NoError(t, err)
302+ // Search for existing and non-existing targets
303+ found, err := Lookup(testDir, "target1.txt", "nonexistent.txt", "target2.txt", "another_nonexistent.txt")
304+ require.NoError(t, err)
305+ require.Len(t, found, 2)
306+ require.Contains(t, found, targetFile1)
307+ require.Contains(t, found, targetFile2)
310+ t.Run("no targets found", func(t *testing.T) {
311+ testDir := t.TempDir()
313+ found, err := Lookup(testDir, "nonexistent1.txt", "nonexistent2.txt", "nonexistent3.txt")
314+ require.NoError(t, err)
315+ require.Empty(t, found)
318+ t.Run("target directories found", func(t *testing.T) {
319+ testDir := t.TempDir()
321+ // Create target directories
322+ targetDir1 := filepath.Join(testDir, "targetdir1")
323+ targetDir2 := filepath.Join(testDir, "targetdir2")
324+ err := os.Mkdir(targetDir1, 0o755)
325+ require.NoError(t, err)
326+ err = os.Mkdir(targetDir2, 0o755)
327+ require.NoError(t, err)
329+ found, err := Lookup(testDir, "targetdir1", "targetdir2")
330+ require.NoError(t, err)
331+ require.Len(t, found, 2)
332+ require.Contains(t, found, targetDir1)
333+ require.Contains(t, found, targetDir2)
336+ t.Run("mixed files and directories", func(t *testing.T) {
337+ testDir := t.TempDir()
339+ // Create target files and directories
340+ targetFile := filepath.Join(testDir, "target.txt")
341+ targetDir := filepath.Join(testDir, "targetdir")
342+ err := os.WriteFile(targetFile, []byte("test"), 0o644)
343+ require.NoError(t, err)
344+ err = os.Mkdir(targetDir, 0o755)
345+ require.NoError(t, err)
347+ found, err := Lookup(testDir, "target.txt", "targetdir")
348+ require.NoError(t, err)
349+ require.Len(t, found, 2)
350+ require.Contains(t, found, targetFile)
351+ require.Contains(t, found, targetDir)
354+ t.Run("invalid starting directory", func(t *testing.T) {
355+ found, err := Lookup("/invalid/path/that/does/not/exist", "target.txt")
356+ require.Error(t, err)
357+ require.Empty(t, found)
360+ t.Run("relative path handling", func(t *testing.T) {
361+ testDir := t.TempDir()
363+ // Change to test directory
364+ oldWd, _ := os.Getwd()
365+ err := os.Chdir(testDir)
366+ require.NoError(t, err)
372+ // Create target files in current directory
373+ err = os.WriteFile("target1.txt", []byte("test1"), 0o644)
374+ require.NoError(t, err)
375+ err = os.WriteFile("target2.txt", []byte("test2"), 0o644)
376+ require.NoError(t, err)
378+ // Search using relative path
379+ found, err := Lookup(".", "target1.txt", "target2.txt")
380+ require.NoError(t, err)
381+ require.Len(t, found, 2)
383+ // Resolve symlinks to handle macOS /private/var vs /var discrepancy
384+ expectedPath1, err := filepath.EvalSymlinks(filepath.Join(testDir, "target1.txt"))
385+ require.NoError(t, err)
386+ expectedPath2, err := filepath.EvalSymlinks(filepath.Join(testDir, "target2.txt"))
387+ require.NoError(t, err)
389+ // Check that found paths match expected paths (order may vary)
390+ foundEvalSymlinks := make([]string, len(found))
391+ for i, path := range found {
392+ evalPath, err := filepath.EvalSymlinks(path)
393+ require.NoError(t, err)
394+ foundEvalSymlinks[i] = evalPath
397+ require.Contains(t, foundEvalSymlinks, expectedPath1)
398+ require.Contains(t, foundEvalSymlinks, expectedPath2)
402+func TestProbeEnt(t *testing.T) {
403+ t.Run("existing file with correct owner", func(t *testing.T) {
404+ tempDir := t.TempDir()
407+ testFile := filepath.Join(tempDir, "test.txt")
408+ err := os.WriteFile(testFile, []byte("test"), 0o644)
409+ require.NoError(t, err)
411+ // Get owner of temp directory
412+ owner, err := Owner(tempDir)
413+ require.NoError(t, err)
415+ // Test probeEnt with correct owner
416+ err = probeEnt(testFile, owner)
417+ require.NoError(t, err)
420+ t.Run("existing directory with correct owner", func(t *testing.T) {
421+ tempDir := t.TempDir()
423+ // Create test directory
424+ testDir := filepath.Join(tempDir, "testdir")
425+ err := os.Mkdir(testDir, 0o755)
426+ require.NoError(t, err)
428+ // Get owner of temp directory
429+ owner, err := Owner(tempDir)
430+ require.NoError(t, err)
432+ // Test probeEnt with correct owner
433+ err = probeEnt(testDir, owner)
434+ require.NoError(t, err)
437+ t.Run("nonexistent file", func(t *testing.T) {
438+ tempDir := t.TempDir()
440+ nonexistentFile := filepath.Join(tempDir, "nonexistent.txt")
441+ owner, err := Owner(tempDir)
442+ require.NoError(t, err)
444+ err = probeEnt(nonexistentFile, owner)
445+ require.Error(t, err)
446+ require.True(t, errors.Is(err, os.ErrNotExist))
449+ t.Run("nonexistent file in nonexistent directory", func(t *testing.T) {
450+ nonexistentFile := "/this/directory/does/not/exists/nonexistent.txt"
452+ err := probeEnt(nonexistentFile, -1)
453+ require.Error(t, err)
454+ require.True(t, errors.Is(err, os.ErrNotExist))
457+ t.Run("ownership bypass with -1", func(t *testing.T) {
458+ tempDir := t.TempDir()
461+ testFile := filepath.Join(tempDir, "test.txt")
462+ err := os.WriteFile(testFile, []byte("test"), 0o644)
463+ require.NoError(t, err)
465+ // Test probeEnt with -1 (bypass ownership check)
466+ err = probeEnt(testFile, -1)
467+ require.NoError(t, err)
470+ t.Run("ownership mismatch returns permission error", func(t *testing.T) {
471+ tempDir := t.TempDir()
474+ testFile := filepath.Join(tempDir, "test.txt")
475+ err := os.WriteFile(testFile, []byte("test"), 0o644)
476+ require.NoError(t, err)
478+ // Test probeEnt with different owner (use 9999 which is unlikely to be the actual owner)
479+ err = probeEnt(testFile, 9999)
480+ require.Error(t, err)
481+ require.True(t, errors.Is(err, os.ErrPermission))