feat: all tests
This commit is contained in:
@@ -87,7 +87,7 @@ func FileGenerator(filters ...string) GeneratorFunc {
|
||||
continue
|
||||
}
|
||||
|
||||
if filePrefix != "" && !hasPrefix(name, filePrefix) {
|
||||
if filePrefix != "" && !HasPrefix(name, filePrefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
+20
-20
@@ -6,11 +6,11 @@ import (
|
||||
"github.com/versenilvis/iris/integration/shell"
|
||||
)
|
||||
|
||||
var shellAliases = make(map[string]string)
|
||||
var ShellAliases = map[string]string{}
|
||||
|
||||
func GetAlias(name string) (string, bool) {
|
||||
target, ok := shellAliases[name]
|
||||
return target, ok
|
||||
val, ok := ShellAliases[name]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Lookup finds matching suggestions for your input by looking at how many words you typed
|
||||
@@ -19,16 +19,16 @@ func GetAlias(name string) (string, bool) {
|
||||
// e.g. Lookup("git checkout ") -> suggests branch names via generator
|
||||
func Lookup(input string) []Suggestion {
|
||||
if shell.Current != nil {
|
||||
shellAliases = shell.Current.ScanAliases()
|
||||
ShellAliases = shell.Current.ScanAliases()
|
||||
} else {
|
||||
shellAliases = make(map[string]string)
|
||||
ShellAliases = make(map[string]string)
|
||||
}
|
||||
|
||||
if input == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
tokens := tokenize(input)
|
||||
tokens := Tokenize(input)
|
||||
|
||||
if len(tokens) == 1 && tokens[0] == "" {
|
||||
return nil
|
||||
@@ -43,8 +43,8 @@ func Lookup(input string) []Suggestion {
|
||||
// if you have an alias in your shell config like: alias gca="git commit -a"
|
||||
// if the first word match it, IRIS will suggest "git commit -a"
|
||||
if len(tokens) > 1 {
|
||||
if target, ok := shellAliases[tokens[0]]; ok {
|
||||
aliasTokens := tokenize(target)
|
||||
if target, ok := ShellAliases[tokens[0]]; ok {
|
||||
aliasTokens := Tokenize(target)
|
||||
if len(aliasTokens) > 0 && aliasTokens[len(aliasTokens)-1] == "" {
|
||||
aliasTokens = aliasTokens[:len(aliasTokens)-1]
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func Lookup(input string) []Suggestion {
|
||||
query := tokens[0]
|
||||
results := topLevelSuggestions(query)
|
||||
|
||||
if spec, exists := registry[query]; exists {
|
||||
if spec, exists := Registry[query]; exists {
|
||||
hasTrailingSpace := query != "" && query[len(query)-1] == ' '
|
||||
|
||||
if hasTrailingSpace {
|
||||
@@ -82,7 +82,7 @@ func Lookup(input string) []Suggestion {
|
||||
}
|
||||
|
||||
rootCmdName := tokens[0]
|
||||
spec, exists := registry[rootCmdName]
|
||||
spec, exists := Registry[rootCmdName]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func Lookup(input string) []Suggestion {
|
||||
genResults := currentGen(tokens, prefix, partial)
|
||||
|
||||
for _, g := range genResults {
|
||||
if partial != "" && !hasPrefix(g.Cmd, partial) && !strings.Contains(g.Cmd, partial) {
|
||||
if partial != "" && !HasPrefix(g.Cmd, partial) && !strings.Contains(g.Cmd, partial) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ func Lookup(input string) []Suggestion {
|
||||
finalCmd = strings.TrimSpace(linePrefix) + " " + suggested
|
||||
}
|
||||
|
||||
newTokens := tokenize(finalCmd)
|
||||
newTokens := Tokenize(finalCmd)
|
||||
if len(newTokens) > 0 {
|
||||
lastToken := newTokens[len(newTokens)-1]
|
||||
isDuplicate := false
|
||||
@@ -218,7 +218,7 @@ func Lookup(input string) []Suggestion {
|
||||
|
||||
if allowMoreArgs {
|
||||
for _, sub := range currentSubs {
|
||||
if partial == "" || hasPrefix(sub.Name, partial) {
|
||||
if partial == "" || HasPrefix(sub.Name, partial) {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: prefix + " " + sub.Name, Desc: sub.Description, Icon: rootCmdName,
|
||||
})
|
||||
@@ -234,7 +234,7 @@ func Lookup(input string) []Suggestion {
|
||||
}
|
||||
}
|
||||
for _, opt := range currentOpts {
|
||||
if !usedOpts[opt.Name] && (partial == "" || hasPrefix(opt.Name, partial)) {
|
||||
if !usedOpts[opt.Name] && (partial == "" || HasPrefix(opt.Name, partial)) {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: prefix + " " + opt.Name, Desc: opt.Description, Icon: rootCmdName,
|
||||
})
|
||||
@@ -248,8 +248,8 @@ func Lookup(input string) []Suggestion {
|
||||
func topLevelSuggestions(query string) []Suggestion {
|
||||
results, seen := []Suggestion{}, make(map[string]bool)
|
||||
|
||||
for name, target := range shellAliases {
|
||||
if !seen[name] && (query == "" || hasPrefix(name, query)) {
|
||||
for name, target := range ShellAliases {
|
||||
if !seen[name] && (query == "" || HasPrefix(name, query)) {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: target, Desc: "alias: " + name, Icon: "root",
|
||||
})
|
||||
@@ -257,16 +257,16 @@ func topLevelSuggestions(query string) []Suggestion {
|
||||
}
|
||||
}
|
||||
|
||||
for name, spec := range registry {
|
||||
for name, spec := range Registry {
|
||||
if seen[name] {
|
||||
continue
|
||||
}
|
||||
match := false
|
||||
if query == "" || hasPrefix(name, query) {
|
||||
if query == "" || HasPrefix(name, query) {
|
||||
match = true
|
||||
} else {
|
||||
for _, a := range spec.Aliases {
|
||||
if hasPrefix(a, query) {
|
||||
if HasPrefix(a, query) {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
@@ -279,7 +279,7 @@ func topLevelSuggestions(query string) []Suggestion {
|
||||
}
|
||||
|
||||
for name := range pathCmds {
|
||||
if !seen[name] && (query == "" || hasPrefix(name, query)) {
|
||||
if !seen[name] && (query == "" || HasPrefix(name, query)) {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: name, Desc: "system command", Icon: "root",
|
||||
})
|
||||
|
||||
@@ -37,10 +37,10 @@ type Suggestion struct {
|
||||
Icon string
|
||||
}
|
||||
|
||||
var registry = map[string]*Spec{}
|
||||
var Registry = map[string]*Spec{}
|
||||
|
||||
// Register adds a new spec to the global registry
|
||||
// Register adds a new spec to the global Registry
|
||||
// example: Register(&Spec{Name: "git"})
|
||||
func Register(s *Spec) {
|
||||
registry[s.Name] = s
|
||||
Registry[s.Name] = s
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ func debugLog(format string, a ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// tokenize parses the input string into shell-like tokens handling quotes
|
||||
// example: tokenize("git commit -m \"hello world\"")
|
||||
func tokenize(s string) []string {
|
||||
// SplitAliasTokens parses the input string into shell-like tokens handling quotes
|
||||
// example: SplitAliasTokens("git commit -m \"hello world\"")
|
||||
func Tokenize(s string) []string {
|
||||
tokens := []string{}
|
||||
var current strings.Builder
|
||||
inQuote := false
|
||||
@@ -43,8 +43,8 @@ func tokenize(s string) []string {
|
||||
return tokens
|
||||
}
|
||||
|
||||
// hasPrefix checks if s starts with prefix using case-insensitive matching
|
||||
func hasPrefix(s, prefix string) bool {
|
||||
// HasPrefix checks if s starts with prefix using case-insensitive matching
|
||||
func HasPrefix(s, prefix string) bool {
|
||||
if len(prefix) > len(s) {
|
||||
return false
|
||||
}
|
||||
@@ -65,5 +65,5 @@ func hasPrefix(s, prefix string) bool {
|
||||
|
||||
// CI = case insensitive
|
||||
func HasPrefixCI(s, prefix string) bool {
|
||||
return hasPrefix(s, prefix)
|
||||
return HasPrefix(s, prefix)
|
||||
}
|
||||
|
||||
@@ -19,17 +19,17 @@ func init() {
|
||||
Name: "z",
|
||||
Description: "jump to directory",
|
||||
MaxArgs: 0,
|
||||
Generator: zoxideGenerator(),
|
||||
Generator: ZoxideGenerator(),
|
||||
})
|
||||
core.Register(&core.Spec{
|
||||
Name: "zi",
|
||||
Description: "jump to directory interactively",
|
||||
MaxArgs: 0,
|
||||
Generator: zoxideGenerator(),
|
||||
Generator: ZoxideGenerator(),
|
||||
})
|
||||
}
|
||||
|
||||
func zoxideGenerator() core.GeneratorFunc {
|
||||
func ZoxideGenerator() core.GeneratorFunc {
|
||||
return func(tokens []string, prefix string, partial string) []core.Suggestion {
|
||||
fullQuery := strings.Join(tokens[1:], " ")
|
||||
localSuggestions := core.FileGenerator("/")(tokens, prefix, fullQuery)
|
||||
|
||||
@@ -43,7 +43,7 @@ func (b *BashAdapter) PrepareSelectSequence(selected string) []byte {
|
||||
return append([]byte{0x15}, []byte(selected)...)
|
||||
}
|
||||
func (b *BashAdapter) ScanAliases() map[string]string {
|
||||
return scanPosixAliases([]string{".bashrc", ".bash_profile", ".bash_aliases"})
|
||||
return ScanPosixAliases([]string{".bashrc", ".bash_profile", ".bash_aliases"})
|
||||
}
|
||||
|
||||
// ZshAdapter implementation
|
||||
@@ -58,7 +58,7 @@ func (z *ZshAdapter) PrepareSelectSequence(selected string) []byte {
|
||||
return append([]byte{0x15}, []byte(selected)...)
|
||||
}
|
||||
func (z *ZshAdapter) ScanAliases() map[string]string {
|
||||
return scanPosixAliases([]string{".zshrc", ".zshenv", ".zprofile"})
|
||||
return ScanPosixAliases([]string{".zshrc", ".zshenv", ".zprofile"})
|
||||
}
|
||||
|
||||
// FishAdapter implementation
|
||||
@@ -74,10 +74,10 @@ func (f *FishAdapter) PrepareSelectSequence(selected string) []byte {
|
||||
}
|
||||
func (f *FishAdapter) ScanAliases() map[string]string {
|
||||
// fish uses 'alias' command in config.fish or separate function files
|
||||
return scanPosixAliases([]string{filepath.Join(".config", "fish", "config.fish")})
|
||||
return ScanPosixAliases([]string{filepath.Join(".config", "fish", "config.fish")})
|
||||
}
|
||||
|
||||
func scanPosixAliases(files []string) map[string]string {
|
||||
func ScanPosixAliases(files []string) map[string]string {
|
||||
aliases := make(map[string]string)
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
@@ -105,7 +105,7 @@ func scanPosixAliases(files []string) map[string]string {
|
||||
continue
|
||||
}
|
||||
|
||||
pairs := splitAliasTokens(body)
|
||||
pairs := SplitAliasTokens(body)
|
||||
for _, pair := range pairs {
|
||||
eqIdx := strings.IndexByte(pair, '=')
|
||||
if eqIdx < 0 {
|
||||
@@ -122,7 +122,7 @@ func scanPosixAliases(files []string) map[string]string {
|
||||
return aliases
|
||||
}
|
||||
|
||||
func splitAliasTokens(s string) []string {
|
||||
func SplitAliasTokens(s string) []string {
|
||||
var tokens []string
|
||||
var cur strings.Builder
|
||||
inQuote := false
|
||||
|
||||
@@ -33,3 +33,7 @@ debug:
|
||||
copy:
|
||||
@rm ~/.local/bin/iris
|
||||
@cp ./iris ~/.local/bin/iris
|
||||
# run all tests
|
||||
[group('dev')]
|
||||
test:
|
||||
@go test ./... -v
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// mergeResults collects and dedupes suggestions for a query and mode
|
||||
// example: mergeResults("git ", "spec")
|
||||
func mergeResults(query string, mode string) []core.Suggestion {
|
||||
func MergeResults(query string, mode string) []core.Suggestion {
|
||||
if query == "" && mode != "history" {
|
||||
debugLog("[Merge] Query empty, returning nil")
|
||||
return nil
|
||||
|
||||
+3
-3
@@ -207,7 +207,7 @@ func runWrapper() {
|
||||
continue
|
||||
}
|
||||
|
||||
results := mergeResults(query, "spec")
|
||||
results := MergeResults(query, "spec")
|
||||
if len(results) == 0 {
|
||||
os.Stdout.Write([]byte(overlay.ClearAndDisable()))
|
||||
continue
|
||||
@@ -263,7 +263,7 @@ func runWrapper() {
|
||||
var b strings.Builder
|
||||
if !navCopy {
|
||||
debugLog("[Render] query: '%s', mode: %s", bufCopy, modeCopy)
|
||||
results := mergeResults(bufCopy, modeCopy)
|
||||
results := MergeResults(bufCopy, modeCopy)
|
||||
debugLog("[Render] results found: %d", len(results))
|
||||
|
||||
if len(results) == 0 {
|
||||
@@ -378,7 +378,7 @@ func runWrapper() {
|
||||
mode = "history"
|
||||
saveMode(mode)
|
||||
|
||||
results := mergeResults("", "history")
|
||||
results := MergeResults("", "history")
|
||||
if len(results) > 0 {
|
||||
limit := 100
|
||||
if len(results) < limit {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
)
|
||||
|
||||
func TestFileGenerator(t *testing.T) {
|
||||
// Create a temp directory structure
|
||||
tmp := t.TempDir()
|
||||
|
||||
os.Mkdir(filepath.Join(tmp, ".git"), 0755)
|
||||
os.Mkdir(filepath.Join(tmp, "src"), 0755)
|
||||
os.Mkdir(filepath.Join(tmp, "docs"), 0755)
|
||||
os.WriteFile(filepath.Join(tmp, "src", "main.go"), []byte("package main"), 0644)
|
||||
os.WriteFile(filepath.Join(tmp, "src", "utils.go"), []byte("package core"), 0644)
|
||||
os.WriteFile(filepath.Join(tmp, "README.md"), []byte("# Readme"), 0644)
|
||||
|
||||
// Helper to change CWD for test
|
||||
oldWd, _ := os.Getwd()
|
||||
os.Chdir(tmp)
|
||||
defer os.Chdir(oldWd)
|
||||
|
||||
t.Run("dirOnly shows only dirs", func(t *testing.T) {
|
||||
gen := core.FileGenerator("/")
|
||||
results := gen([]string{"cd", ""}, "cd ", "")
|
||||
for _, r := range results {
|
||||
if r.Desc != "directory" {
|
||||
t.Errorf("FileGenerator(\"/\") suggested a non-directory: %s", r.Cmd)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Filter extension", func(t *testing.T) {
|
||||
gen := core.FileGenerator(".go")
|
||||
results := gen([]string{"go", "src/"}, "go src/", "src/")
|
||||
foundMain := false
|
||||
for _, r := range results {
|
||||
if r.Cmd == "src/main.go" {
|
||||
foundMain = true
|
||||
}
|
||||
}
|
||||
if !foundMain {
|
||||
t.Errorf("FileGenerator(\".go\") did not suggest src/main.go")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Hidden files are skipped", func(t *testing.T) {
|
||||
gen := core.FileGenerator()
|
||||
results := gen([]string{"ls", ""}, "ls ", "")
|
||||
for _, r := range results {
|
||||
if strings.HasPrefix(filepath.Base(r.Cmd), ".") {
|
||||
t.Errorf("FileGenerator suggested hidden file: %s", r.Cmd)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
"github.com/versenilvis/iris/integration/shell"
|
||||
)
|
||||
|
||||
type mockAdapter struct {
|
||||
shell.BashAdapter
|
||||
}
|
||||
|
||||
func (m *mockAdapter) ScanAliases() map[string]string {
|
||||
return map[string]string{
|
||||
"gca": "git commit -a",
|
||||
"ta": "tmux a -t",
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
// Use mock adapter
|
||||
shell.Current = &mockAdapter{}
|
||||
|
||||
core.Register(&core.Spec{
|
||||
Name: "git",
|
||||
Description: "git command",
|
||||
Subcommands: []core.Subcommand{
|
||||
{Name: "commit", Description: "commit changes"},
|
||||
{Name: "remote", Description: "manage remotes", Subcommands: []core.Subcommand{
|
||||
{Name: "add", Description: "add remote"},
|
||||
}},
|
||||
},
|
||||
Options: []core.Option{
|
||||
{Name: "--verbose", Description: "verbose output"},
|
||||
},
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
minCount int
|
||||
checkCmd string
|
||||
}{
|
||||
{"Top-level suggestions", "gi", 1, "git"},
|
||||
{"Subcommand suggestions", "git ", 2, "git commit"},
|
||||
{"Alias expansion", "gca", 1, "git commit -a"},
|
||||
{"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"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
results := core.Lookup(tt.input)
|
||||
if len(results) < tt.minCount {
|
||||
t.Errorf("Lookup(%q) returned %d results; want at least %d", tt.input, len(results), tt.minCount)
|
||||
}
|
||||
if tt.checkCmd != "" {
|
||||
found := false
|
||||
for _, r := range results {
|
||||
if r.Cmd == tt.checkCmd {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Lookup(%q) did not suggest %q", tt.input, tt.checkCmd)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
)
|
||||
|
||||
func TestTokenize(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected []string
|
||||
}{
|
||||
{"", []string{""}},
|
||||
{"git ", []string{"git", ""}},
|
||||
{"git add", []string{"git", "add"}},
|
||||
{"git commit -m \"hello world\"", []string{"git", "commit", "-m", "hello world"}},
|
||||
{"git commit -m \"hello", []string{"git", "commit", "-m", "hello"}},
|
||||
{"git commit -m 'hello world'", []string{"git", "commit", "-m", "hello world"}},
|
||||
{"git commit -m 'hello", []string{"git", "commit", "-m", "hello"}},
|
||||
{"ls -l \"file name\"", []string{"ls", "-l", "file name"}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
got := core.Tokenize(tt.input)
|
||||
if !reflect.DeepEqual(got, tt.expected) {
|
||||
t.Errorf("Tokenize(%q) = %v; want %v", tt.input, got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
s string
|
||||
prefix string
|
||||
want bool
|
||||
}{
|
||||
{"Hello", "hel", true},
|
||||
{"Thử nghiệm", "thử", true},
|
||||
{"Iris", "Iris-Longer", false},
|
||||
{"Iris", "", true},
|
||||
{"", "a", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.s+"_"+tt.prefix, func(t *testing.T) {
|
||||
if got := core.HasPrefix(tt.s, tt.prefix); got != tt.want {
|
||||
t.Errorf("HasPrefix(%q, %q) = %v; want %v", tt.s, tt.prefix, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package fs_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/commands/fs"
|
||||
)
|
||||
|
||||
func TestZoxideGenerator(t *testing.T) {
|
||||
// Create a mock zoxide binary
|
||||
tmp := t.TempDir()
|
||||
mockZoxide := filepath.Join(tmp, "zoxide")
|
||||
|
||||
// Script that prints mock directories
|
||||
script := "#!/bin/sh\necho \"/home/verse/project1\n/home/verse/docs\n/home/verse/dev/iris\""
|
||||
os.WriteFile(mockZoxide, []byte(script), 0755)
|
||||
|
||||
// Add tmp to PATH
|
||||
oldPath := os.Getenv("PATH")
|
||||
os.Setenv("PATH", tmp+string(os.PathListSeparator)+oldPath)
|
||||
defer os.Setenv("PATH", oldPath)
|
||||
|
||||
gen := fs.ZoxideGenerator()
|
||||
|
||||
t.Run("Query returns correct result when partial is empty", func(t *testing.T) {
|
||||
results := gen([]string{"z", ""}, "z ", "")
|
||||
if len(results) == 0 {
|
||||
t.Errorf("Expected results from zoxide history, got 0")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Path replaces home dir with ~", func(t *testing.T) {
|
||||
home, _ := os.UserHomeDir()
|
||||
results := gen([]string{"z", ""}, "z ", "")
|
||||
foundHome := false
|
||||
for _, r := range results {
|
||||
if strings.HasPrefix(r.Desc, "~") {
|
||||
foundHome = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundHome && home != "" {
|
||||
t.Logf("Warning: Did not find ~ in descriptions, home is %s", home)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Sort by descending score", func(t *testing.T) {
|
||||
results := gen([]string{"z", "i"}, "z ", "i")
|
||||
if len(results) >= 1 {
|
||||
if results[0].Cmd == "" {
|
||||
t.Errorf("Empty result command")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
_ "github.com/versenilvis/iris/commands/fs" // Register z command
|
||||
)
|
||||
|
||||
func TestIntegration_ZoxideMultiWord(t *testing.T) {
|
||||
// Create mock directory with spaces
|
||||
tmp := t.TempDir()
|
||||
targetDir := filepath.Join(tmp, "My Awesome Project")
|
||||
os.MkdirAll(targetDir, 0755)
|
||||
|
||||
// Mock shell environment
|
||||
oldWd, _ := os.Getwd()
|
||||
os.Chdir(tmp)
|
||||
defer os.Chdir(oldWd)
|
||||
|
||||
// Mock zoxide binary
|
||||
mockBinDir := t.TempDir()
|
||||
mockZoxide := filepath.Join(mockBinDir, "zoxide")
|
||||
script := "#!/bin/sh\necho \"" + targetDir + "\""
|
||||
os.WriteFile(mockZoxide, []byte(script), 0755)
|
||||
|
||||
oldPath := os.Getenv("PATH")
|
||||
os.Setenv("PATH", mockBinDir+string(os.PathListSeparator)+oldPath)
|
||||
defer os.Setenv("PATH", oldPath)
|
||||
|
||||
t.Run("z matches multi-word folder without quotes", func(t *testing.T) {
|
||||
// Simulating user typing "z My Awe"
|
||||
input := "z My Awe"
|
||||
results := core.Lookup(input)
|
||||
|
||||
found := false
|
||||
for _, r := range results {
|
||||
// Expected result should be "z My Awesome Project/"
|
||||
if strings.Contains(r.Cmd, "My Awesome Project") {
|
||||
found = true
|
||||
// Check for word duplication bug: "z My My Awesome..."
|
||||
if strings.Contains(r.Cmd, "My My") {
|
||||
t.Errorf("Word duplication detected in suggestion: %s", r.Cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Could not find 'My Awesome Project' in suggestions for %q", input)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package root_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
"github.com/versenilvis/iris/root"
|
||||
)
|
||||
|
||||
func TestMergeResults(t *testing.T) {
|
||||
// Setup mock Registry
|
||||
core.Registry = make(map[string]*core.Spec)
|
||||
core.Register(&core.Spec{
|
||||
Name: "ls",
|
||||
Description: "list files",
|
||||
})
|
||||
|
||||
// Setup mock history file
|
||||
tmp := t.TempDir()
|
||||
histFile := filepath.Join(tmp, ".bash_history")
|
||||
os.WriteFile(histFile, []byte("ls -l\ncd /tmp\ngit status\n"), 0644)
|
||||
|
||||
oldHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tmp)
|
||||
defer os.Setenv("HOME", oldHome)
|
||||
|
||||
t.Run("History mode returns history items", func(t *testing.T) {
|
||||
results := root.MergeResults("", "history")
|
||||
if len(results) == 0 {
|
||||
t.Errorf("MergeResults history mode returned 0 items")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Spec mode returns command results and dedups", func(t *testing.T) {
|
||||
core.Register(&core.Spec{Name: "git", Description: "git"})
|
||||
|
||||
results := root.MergeResults("gi", "spec")
|
||||
foundGit := false
|
||||
for _, r := range results {
|
||||
if r.Cmd == "git" {
|
||||
foundGit = true
|
||||
}
|
||||
}
|
||||
if !foundGit {
|
||||
t.Errorf("MergeResults spec mode did not find 'git'")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package shell_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/integration/shell"
|
||||
)
|
||||
|
||||
func TestSplitAliasTokens(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected []string
|
||||
}{
|
||||
{"g='git commit'", []string{"g='git commit'"}},
|
||||
{"a=b c=d", []string{"a=b", "c=d"}},
|
||||
{"ta='tmux a -t' l='ls -l'", []string{"ta='tmux a -t'", "l='ls -l'"}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
got := shell.SplitAliasTokens(tt.input)
|
||||
if !reflect.DeepEqual(got, tt.expected) {
|
||||
t.Errorf("SplitAliasTokens(%q) = %v; want %v", tt.input, got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanPosixAliases(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
|
||||
aliasFile := filepath.Join(tmp, ".bashrc")
|
||||
content := `
|
||||
# some comments
|
||||
alias g='git'
|
||||
alias gca='git commit -a'
|
||||
alias multi="a" b="c"
|
||||
`
|
||||
os.WriteFile(aliasFile, []byte(content), 0644)
|
||||
|
||||
oldHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tmp)
|
||||
defer os.Setenv("HOME", oldHome)
|
||||
|
||||
aliases := shell.ScanPosixAliases([]string{".bashrc"})
|
||||
|
||||
expected := map[string]string{
|
||||
"g": "git",
|
||||
"gca": "git commit -a",
|
||||
"multi": "a",
|
||||
"b": "c",
|
||||
}
|
||||
|
||||
for k, v := range expected {
|
||||
if aliases[k] != v {
|
||||
t.Errorf("Expected alias %s=%s, got %s", k, v, aliases[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user