177 lines
5.1 KiB
Go
177 lines
5.1 KiB
Go
package fs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/versenilvis/iris/spec"
|
|
)
|
|
|
|
func TestZoxideSourceCachesDirectoriesAndErrors(t *testing.T) {
|
|
now := time.Date(2026, time.August, 14, 12, 0, 0, 0, time.UTC)
|
|
calls := 0
|
|
source := newZoxideSource(func(context.Context) ([]byte, error) {
|
|
calls++
|
|
return []byte("/tmp/Project One \n/tmp/Проект\r\n"), nil
|
|
})
|
|
source.now = func() time.Time { return now }
|
|
|
|
want := []string{"/tmp/Project One ", "/tmp/Проект"}
|
|
for range 2 {
|
|
got, err := source.directories()
|
|
if err != nil {
|
|
t.Fatalf("directories returned an error: %v", err)
|
|
}
|
|
if !slices.Equal(got, want) {
|
|
t.Fatalf("directories = %q, want %q", got, want)
|
|
}
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("query calls = %d, want 1", calls)
|
|
}
|
|
|
|
now = now.Add(zoxideCacheTTL)
|
|
if _, err := source.directories(); err != nil {
|
|
t.Fatalf("directories after cache expiry returned an error: %v", err)
|
|
}
|
|
if calls != 2 {
|
|
t.Fatalf("query calls after cache expiry = %d, want 2", calls)
|
|
}
|
|
|
|
wantErr := errors.New("zoxide unavailable")
|
|
errorCalls := 0
|
|
errorSource := newZoxideSource(func(context.Context) ([]byte, error) {
|
|
errorCalls++
|
|
return nil, wantErr
|
|
})
|
|
if _, err := errorSource.directories(); !errors.Is(err, wantErr) {
|
|
t.Fatalf("directories error = %v, want %v", err, wantErr)
|
|
}
|
|
if _, err := errorSource.directories(); !errors.Is(err, wantErr) {
|
|
t.Fatalf("cached directories error = %v, want %v", err, wantErr)
|
|
}
|
|
if errorCalls != 1 {
|
|
t.Fatalf("failed query calls = %d, want 1", errorCalls)
|
|
}
|
|
}
|
|
|
|
func TestZoxideGeneratorMergesValidDirectories(t *testing.T) {
|
|
cwd := t.TempDir()
|
|
local := filepath.Join(cwd, "project")
|
|
external := filepath.Join(t.TempDir(), "My Проект")
|
|
missing := filepath.Join(t.TempDir(), "deleted")
|
|
mustMkdirAll(t, local)
|
|
mustMkdirAll(t, external)
|
|
|
|
previousCWD := spec.GetCWD()
|
|
spec.SetCWD(cwd)
|
|
t.Cleanup(func() { spec.SetCWD(previousCWD) })
|
|
|
|
gen := newZoxideGenerator(func() ([]string, error) {
|
|
return []string{local, external, external, missing}, nil
|
|
})
|
|
results := gen([]string{"z", ""}, "z ", "")
|
|
|
|
if len(results) != 2 {
|
|
t.Fatalf("suggestion count = %d, want 2: %#v", len(results), results)
|
|
}
|
|
if results[0].Cmd != "project/" {
|
|
t.Fatalf("first suggestion = %q, want local project/", results[0].Cmd)
|
|
}
|
|
if results[1].Cmd != external {
|
|
t.Fatalf("second suggestion = %q, want %q", results[1].Cmd, external)
|
|
}
|
|
if results[1].Priority != 40 {
|
|
t.Fatalf("external priority = %d, want 40", results[1].Priority)
|
|
}
|
|
}
|
|
|
|
func TestZoxideGeneratorFiltersAndFallsBack(t *testing.T) {
|
|
cwd := t.TempDir()
|
|
nested := filepath.Join(cwd, "nested", "child")
|
|
mustMkdirAll(t, nested)
|
|
|
|
previousCWD := spec.GetCWD()
|
|
spec.SetCWD(cwd)
|
|
t.Cleanup(func() { spec.SetCWD(previousCWD) })
|
|
|
|
calls := 0
|
|
gen := newZoxideGenerator(func() ([]string, error) {
|
|
calls++
|
|
return nil, errors.New("zoxide unavailable")
|
|
})
|
|
|
|
results := gen([]string{"z", "nested/ch"}, "z ", "nested/ch")
|
|
if calls != 0 {
|
|
t.Fatalf("source calls for explicit path = %d, want 0", calls)
|
|
}
|
|
if len(results) != 1 || results[0].Cmd != "nested/child/" {
|
|
t.Fatalf("explicit path suggestions = %#v", results)
|
|
}
|
|
|
|
results = gen([]string{"z", "n"}, "z ", "n")
|
|
if calls != 1 {
|
|
t.Fatalf("source calls after name query = %d, want 1", calls)
|
|
}
|
|
if len(results) != 1 || results[0].Cmd != "nested/" {
|
|
t.Fatalf("fallback suggestions = %#v", results)
|
|
}
|
|
|
|
empty := newZoxideGenerator(func() ([]string, error) { return nil, nil })
|
|
if results := empty([]string{"z", "missing"}, "z ", "missing"); len(results) != 0 {
|
|
t.Fatalf("empty database suggestions = %#v, want none", results)
|
|
}
|
|
}
|
|
|
|
func TestZoxideGeneratorFuzzyMatchesUnicodeAndLookupQuotesSpaces(t *testing.T) {
|
|
cwd := t.TempDir()
|
|
target := filepath.Join(t.TempDir(), "My Awesome $Проект")
|
|
mustMkdirAll(t, target)
|
|
|
|
previousCWD := spec.GetCWD()
|
|
spec.SetCWD(cwd)
|
|
t.Cleanup(func() { spec.SetCWD(previousCWD) })
|
|
|
|
const command = "zoxide-test-command"
|
|
spec.Register(&spec.Spec{
|
|
Name: command,
|
|
MaxArgs: 0,
|
|
Generator: newZoxideGenerator(func() ([]string, error) { return []string{target}, nil }),
|
|
})
|
|
t.Cleanup(func() { delete(spec.Registry, command) })
|
|
|
|
results := spec.Lookup(command + " My Awe")
|
|
want := command + ` '` + target + `'`
|
|
if len(results) != 1 || results[0].Cmd != want {
|
|
t.Fatalf("lookup suggestions = %#v, want command %q", results, want)
|
|
}
|
|
}
|
|
|
|
func TestDisplayDirectoryOnlyShortensHomePrefix(t *testing.T) {
|
|
home := filepath.Join(string(os.PathSeparator), "Users", "example")
|
|
inside := filepath.Join(home, "Developer", "Проект")
|
|
outside := filepath.Join(string(os.PathSeparator), "tmp", "Users", "example", "data")
|
|
|
|
if got := displayDirectory(home, home); got != "~" {
|
|
t.Fatalf("displayDirectory(home) = %q, want ~", got)
|
|
}
|
|
if got := displayDirectory(inside, home); got != filepath.Join("~", "Developer", "Проект") {
|
|
t.Fatalf("displayDirectory(inside) = %q", got)
|
|
}
|
|
if got := displayDirectory(outside, home); got != outside {
|
|
t.Fatalf("displayDirectory(outside) = %q, want %q", got, outside)
|
|
}
|
|
}
|
|
|
|
func mustMkdirAll(t *testing.T, path string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
t.Fatalf("create directory %q: %v", path, err)
|
|
}
|
|
}
|