Files
Memoh/internal/command/parser_test.go
Acbox 0549f5cafc feat(command): improve slash command UX
Make slash commands easier to navigate in chat by splitting help into levels, compacting list output, and surfacing current selections for model, search, memory, and browser settings. Also route /status to the active conversation session and add an access inspector so users can understand their current command and ACL context.
2026-04-12 17:25:10 +08:00

110 lines
2.8 KiB
Go

package command
import (
"testing"
)
func TestParse_Basic(t *testing.T) {
t.Parallel()
tests := []struct {
input string
resource string
action string
args []string
}{
{"/help", "help", "", nil},
{"/help model", "help", "model", nil},
{"/help model set", "help", "model", []string{"set"}},
{"/subagent list", "subagent", "list", nil},
{"/subagent get mybot", "subagent", "get", []string{"mybot"}},
{"/schedule create daily \"0 9 * * *\" Send report", "schedule", "create", []string{"daily", "0 9 * * *", "Send", "report"}},
{" /settings ", "settings", "", nil},
{"/HELP", "help", "", nil},
{"/Schedule List", "schedule", "list", nil},
{"/help@MemohBot", "help", "", nil},
{"/schedule@BotName list", "schedule", "list", nil},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
t.Parallel()
parsed, err := Parse(tt.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if parsed.Resource != tt.resource {
t.Errorf("resource: got %q, want %q", parsed.Resource, tt.resource)
}
if parsed.Action != tt.action {
t.Errorf("action: got %q, want %q", parsed.Action, tt.action)
}
if len(parsed.Args) != len(tt.args) {
t.Fatalf("args length: got %d, want %d", len(parsed.Args), len(tt.args))
}
for i, arg := range tt.args {
if parsed.Args[i] != arg {
t.Errorf("arg[%d]: got %q, want %q", i, parsed.Args[i], arg)
}
}
})
}
}
func TestExtractCommandText(t *testing.T) {
t.Parallel()
tests := []struct {
input string
want string
}{
{"/help", "/help"},
{" /subagent list", "/subagent list"},
{"@BotName /help", "/help"},
{"@_user_1 /schedule list arg1", "/schedule list arg1"},
{"<@123456> /mcp list", "/mcp list"},
{"@bot hello", ""},
{"hello world", ""},
{"", ""},
{"some text with no slash", ""},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
t.Parallel()
got := ExtractCommandText(tt.input)
if got != tt.want {
t.Errorf("ExtractCommandText(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
func TestParse_Errors(t *testing.T) {
t.Parallel()
tests := []string{
"",
"hello",
"no slash",
}
for _, input := range tests {
t.Run(input, func(t *testing.T) {
t.Parallel()
_, err := Parse(input)
if err == nil {
t.Error("expected error, got nil")
}
})
}
}
func TestTokenize_Quotes(t *testing.T) {
t.Parallel()
tokens := tokenize(`create daily "0 9 * * *" 'Send report now'`)
expected := []string{"create", "daily", "0 9 * * *", "Send report now"}
if len(tokens) != len(expected) {
t.Fatalf("tokens length: got %d, want %d (%v)", len(tokens), len(expected), tokens)
}
for i, tok := range expected {
if tokens[i] != tok {
t.Errorf("token[%d]: got %q, want %q", i, tokens[i], tok)
}
}
}