refactor: better project structure (#35)
This project is starting to grow, and I've seen flaws in the initial project structure design I think I'll restructure the entire project directory for better scalability and also cleaner
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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 := 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 := 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 := 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 := 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 := 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/versenilvis/iris/integration/shell"
|
||||
"github.com/versenilvis/iris/logger"
|
||||
"github.com/versenilvis/iris/internal/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"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 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.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range iterations {
|
||||
_ = Lookup("gca")
|
||||
_ = Lookup("git ")
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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 := 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 := 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