refactor(commands): separate commands and its core into two folders (#32)
- I think it would be better if one for commands only, one for the spec handling core logic so people won't be confused by the core logic inside the commands folder (cleaner I guess) - Regen docs
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/spec"
|
||||
)
|
||||
|
||||
func TestFileGenerator(t *testing.T) {
|
||||
// Setup mock files
|
||||
tmp := t.TempDir()
|
||||
_ = os.MkdirAll(filepath.Join(tmp, "src"), 0755)
|
||||
_ = os.WriteFile(filepath.Join(tmp, "main.go"), []byte(""), 0644)
|
||||
_ = os.WriteFile(filepath.Join(tmp, "README.md"), []byte(""), 0644)
|
||||
_ = os.WriteFile(filepath.Join(tmp, ".hidden"), []byte(""), 0644)
|
||||
_ = os.WriteFile(filepath.Join(tmp, "src/utils.go"), []byte(""), 0644)
|
||||
|
||||
oldWd, _ := os.Getwd()
|
||||
_ = os.Chdir(tmp)
|
||||
defer func() { _ = os.Chdir(oldWd) }()
|
||||
|
||||
|
||||
t.Run("dirOnly shows only dirs", func(t *testing.T) {
|
||||
gen := spec.FileGenerator("/")
|
||||
results := gen([]string{"cd", ""}, "cd ", "")
|
||||
for _, r := range results {
|
||||
if !strings.HasSuffix(r.Cmd, "/") {
|
||||
t.Errorf("Expected only directories, got %q", r.Cmd)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
t.Run("Filter extension", func(t *testing.T) {
|
||||
gen := spec.FileGenerator(".go")
|
||||
results := gen([]string{"ls", ""}, "ls ", "")
|
||||
foundMain := false
|
||||
for _, r := range results {
|
||||
if r.Cmd == "main.go" {
|
||||
foundMain = true
|
||||
}
|
||||
if r.Cmd == "README.md" {
|
||||
t.Errorf("Did not expect README.md when filtering for .go")
|
||||
}
|
||||
}
|
||||
if !foundMain {
|
||||
t.Errorf("FileGenerator(\".go\") did not suggest main.go")
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
t.Run("Nested path", func(t *testing.T) {
|
||||
gen := spec.FileGenerator()
|
||||
results := gen([]string{"ls", "src/u"}, "ls src/u", "src/u")
|
||||
foundUtils := false
|
||||
for _, r := range results {
|
||||
if r.Cmd == "src/utils.go" {
|
||||
foundUtils = true
|
||||
}
|
||||
}
|
||||
if !foundUtils {
|
||||
t.Errorf("Did not find src/utils.go for nested path src/u")
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
t.Run("Deep scan 1 level", func(t *testing.T) {
|
||||
gen := spec.FileGenerator()
|
||||
results := gen([]string{"ls", "src/"}, "ls src/", "src/")
|
||||
foundUtils := false
|
||||
for _, r := range results {
|
||||
if r.Cmd == "src/utils.go" {
|
||||
foundUtils = true
|
||||
}
|
||||
}
|
||||
if !foundUtils {
|
||||
t.Errorf("Deep scan did not find src/utils.go")
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
t.Run("Hidden files are skipped", func(t *testing.T) {
|
||||
gen := spec.FileGenerator()
|
||||
results := gen([]string{"ls", ""}, "ls ", "")
|
||||
for _, r := range results {
|
||||
if strings.HasPrefix(r.Cmd, ".") {
|
||||
t.Errorf("Hidden file %q should be skipped", r.Cmd)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/spec"
|
||||
)
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
// Setup Registry
|
||||
spec.Registry = make(map[string]*spec.Spec)
|
||||
spec.Register(&spec.Spec{
|
||||
Name: "git",
|
||||
Subcommands: []spec.Subcommand{
|
||||
{Name: "commit", Options: []spec.Option{{Name: "--message"}}, MaxArgs: 1},
|
||||
{Name: "remote", Subcommands: []spec.Subcommand{{Name: "add"}}},
|
||||
},
|
||||
Options: []spec.Option{{Name: "--verbose"}},
|
||||
})
|
||||
|
||||
// Setup Aliases
|
||||
spec.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 := spec.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 TestLookupConcurrent(t *testing.T) {
|
||||
spec.Registry = make(map[string]*spec.Spec)
|
||||
spec.Register(&spec.Spec{
|
||||
Name: "git",
|
||||
Subcommands: []spec.Subcommand{
|
||||
{Name: "commit", Options: []spec.Option{{Name: "--message"}}, MaxArgs: 1},
|
||||
},
|
||||
})
|
||||
|
||||
spec.ShellAliases = map[string]string{
|
||||
"gca": "git commit -a",
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
const goroutines = 10
|
||||
const iterations = 50
|
||||
|
||||
for range goroutines {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range iterations {
|
||||
_ = spec.Lookup("gca")
|
||||
_ = spec.Lookup("git ")
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/spec"
|
||||
)
|
||||
|
||||
func TestTokenize(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected []string
|
||||
}{
|
||||
{"Empty input", "", []string{""}},
|
||||
{"Trailing space", "git ", []string{"git", ""}},
|
||||
{"Multi-space", "git add", []string{"git", "add"}},
|
||||
{"Quoted string", "git commit -m \"hello world\"", []string{"git", "commit", "-m", "hello world"}},
|
||||
{"Quote not closed", "git commit -m \"hello", []string{"git", "commit", "-m", "hello"}},
|
||||
{"Single quote", "git commit -m 'hello world'", []string{"git", "commit", "-m", "hello world"}},
|
||||
{"Backslash escape", "git commit -m \"hello\\ world\"", []string{"git", "commit", "-m", "hello world"}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := spec.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 {
|
||||
name string
|
||||
s string
|
||||
prefix string
|
||||
want bool
|
||||
}{
|
||||
{"Case insensitive", "Hello", "hel", true},
|
||||
{"Unicode support", "Thử nghiệm", "thử", true},
|
||||
{"Prefix longer", "Iris", "Iris-Longer", false},
|
||||
{"Empty prefix", "Iris", "", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := spec.HasPrefix(tt.s, tt.prefix); got != tt.want {
|
||||
t.Errorf("HasPrefix(%q, %q) = %v; want %v", tt.s, tt.prefix, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user