Files
iris-context/spec/lookup_test.go
T
2026-07-26 20:43:05 +07:00

234 lines
5.6 KiB
Go

package spec
import (
"os"
"path/filepath"
"strings"
"sync"
"testing"
)
func TestLookup(t *testing.T) {
// Setup Registry
Registry = make(map[string]*Spec)
Register(&Spec{
Name: "git",
Subcommands: []Subcommand{
{Name: "commit", Options: []Option{{Name: "--message"}}, MaxArgs: 1},
{Name: "remote", Subcommands: []Subcommand{{Name: "add"}}},
},
Options: []Option{{Name: "--verbose"}},
})
// Setup Aliases
ShellAliases = map[string]string{
"gca": "git commit -a",
"ta": "tmux a -t",
}
tests := []struct {
name string
input string
minResults int
mustContain string
}{
{"Top-level", "gi", 1, "git"},
{"Subcommand", "git ", 1, "git commit"},
{"Alias expansion", "gca", 1, "git commit -a"},
{"Alias with space", "ta", 1, "tmux a -t"},
{"Deep subcommand", "git remote ", 1, "git remote add"},
{"Option dedup", "git --verbose -", 0, ""},
{"Flag with value ignore", "git --output=json ", 2, "git --output=json commit"},
{"Unknown root command", "unknowncmd ", 0, ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
results := Lookup(tt.input)
if len(results) < tt.minResults {
t.Errorf("Lookup(%q) got %d results; want at least %d", tt.input, len(results), tt.minResults)
}
if tt.mustContain != "" {
found := false
for _, r := range results {
if strings.Contains(r.Cmd, tt.mustContain) || strings.Contains(r.Desc, tt.mustContain) {
found = true
break
}
}
if !found {
t.Errorf("Lookup(%q) results did not contain %q", tt.input, tt.mustContain)
}
}
})
}
}
func TestLookup_NoFlagGateAndPriority(t *testing.T) {
Registry["demo"] = &Spec{
Name: "demo",
Subcommands: []Subcommand{
{Name: "sub1", Priority: 85},
},
Options: []Option{
{Name: "--verbose", Priority: 70},
},
}
results := Lookup("demo ")
foundSub, foundOpt := false, false
for _, r := range results {
if strings.Contains(r.Cmd, "sub1") {
foundSub = true
if r.Priority != 85 {
t.Errorf("expected subcommand Priority 85, got %d", r.Priority)
}
}
if strings.Contains(r.Cmd, "--verbose") {
foundOpt = true
if r.Priority != 70 {
t.Errorf("expected option Priority 70, got %d", r.Priority)
}
}
}
if !foundSub {
t.Error("expected sub1 in lookup results")
}
if !foundOpt {
t.Error("expected --verbose in lookup results even without typed dash")
}
partialResults := Lookup("demo ver")
foundPartialOpt := false
for _, r := range partialResults {
if strings.Contains(r.Cmd, "--verbose") {
foundPartialOpt = true
break
}
}
if !foundPartialOpt {
t.Error("expected --verbose when partial query is 'ver' (trimmed dash match)")
}
}
func TestLookupConcurrent(t *testing.T) {
Registry = make(map[string]*Spec)
Register(&Spec{
Name: "git",
Subcommands: []Subcommand{
{Name: "commit", Options: []Option{{Name: "--message"}}, MaxArgs: 1},
},
})
ShellAliases = map[string]string{
"gca": "git commit -a",
}
var wg sync.WaitGroup
const goroutines = 10
const iterations = 50
for range goroutines {
wg.Go(func() {
for range iterations {
_ = Lookup("gca")
_ = Lookup("git ")
}
})
}
wg.Wait()
}
func TestLookup_AliasFileGenerator(t *testing.T) {
ResetRegistry()
Register(&Spec{
Name: "nvim",
Generator: FileGenerator(),
})
// alias "nv" -> "nvim", single token with trailing space
ShellAliases = map[string]string{"nv": "nvim"}
results := Lookup("nv ")
if len(results) == 0 {
t.Errorf("expected file suggestions for alias 'nv ' -> 'nvim', got none")
}
for _, r := range results {
if !strings.HasPrefix(r.Cmd, "nvim ") {
t.Errorf("expected suggestion to start with 'nvim ', got %q", r.Cmd)
}
}
}
func TestLookup_NvimFileGenerator(t *testing.T) {
ResetRegistry()
Register(&Spec{
Name: "nvim",
Generator: FileGenerator(),
})
results := Lookup("nvim ")
if len(results) == 0 {
t.Errorf("expected file suggestions for 'nvim ', got none")
}
}
func TestLookup_OptionAndFilePriority(t *testing.T) {
ResetRegistry()
Register(&Spec{
Name: "nvim",
Generator: FileGenerator(),
Options: []Option{
{Name: "-c", Description: "Execute cmd"},
{Name: "--cmd", Description: "Execute cmd before config"},
},
})
// When query is 'nvim ', files should be prioritized over flags
resultsEmpty := Lookup("nvim ")
if len(resultsEmpty) == 0 {
t.Fatalf("expected results for 'nvim ', got 0")
}
// first result should be a file or dir (Priority 50), not option (Priority 10)
if strings.HasPrefix(resultsEmpty[0].Cmd, "nvim -") {
t.Errorf("expected file/dir as top result for 'nvim ', got %q", resultsEmpty[0].Cmd)
}
// When query is 'nvim -', flags should be prioritized (Priority 80)
resultsDash := Lookup("nvim -")
if len(resultsDash) == 0 {
t.Fatalf("expected results for 'nvim -', got 0")
}
if !strings.HasPrefix(resultsDash[0].Cmd, "nvim -") {
t.Errorf("expected option as top result for 'nvim -', got %q", resultsDash[0].Cmd)
}
}
func TestLookup_NestedDirectoryTrailingSpace(t *testing.T) {
ResetRegistry()
Register(&Spec{
Name: "cat",
Generator: FileGenerator(),
})
tmpDir := t.TempDir()
subDir := filepath.Join(tmpDir, "subdir")
_ = os.Mkdir(subDir, 0755)
testFile := filepath.Join(subDir, "hello.txt")
_ = os.WriteFile(testFile, []byte("hi"), 0644)
query := "cat " + subDir + "/ "
results := Lookup(query)
if len(results) == 0 {
t.Fatalf("expected results for %q, got 0", query)
}
found := false
for _, r := range results {
if strings.Contains(r.Cmd, "hello.txt") {
found = true
break
}
}
if !found {
t.Errorf("expected hello.txt in results, got %v", results)
}
}