fix(commands): harden executor matching and edge-case tests
This commit is contained in:
parent
0844cbbce4
commit
bb61147f7e
2 changed files with 126 additions and 5 deletions
|
|
@ -28,7 +28,7 @@ func NewExecutor(reg *Registry) *Executor {
|
|||
return &Executor{reg: reg}
|
||||
}
|
||||
|
||||
func (e *Executor) Execute(ctx context.Context, req Request, _ any) ExecuteResult {
|
||||
func (e *Executor) Execute(ctx context.Context, req Request) ExecuteResult {
|
||||
cmdName, ok := parseCommandName(req.Text)
|
||||
if !ok {
|
||||
return ExecuteResult{Outcome: OutcomePassthrough}
|
||||
|
|
@ -38,16 +38,26 @@ func (e *Executor) Execute(ctx context.Context, req Request, _ any) ExecuteResul
|
|||
return ExecuteResult{Outcome: OutcomePassthrough, Command: cmdName}
|
||||
}
|
||||
|
||||
matchedSupported := false
|
||||
passthroughCommand := ""
|
||||
|
||||
for _, def := range e.reg.ForChannel(req.Channel) {
|
||||
if !matchesCommand(def, cmdName) {
|
||||
continue
|
||||
}
|
||||
matchedSupported = true
|
||||
if passthroughCommand == "" {
|
||||
passthroughCommand = def.Name
|
||||
}
|
||||
if def.Handler == nil {
|
||||
return ExecuteResult{Outcome: OutcomePassthrough, Command: def.Name}
|
||||
continue
|
||||
}
|
||||
err := def.Handler(ctx, req)
|
||||
return ExecuteResult{Outcome: OutcomeHandled, Command: def.Name, Err: err}
|
||||
}
|
||||
if matchedSupported {
|
||||
return ExecuteResult{Outcome: OutcomePassthrough, Command: passthroughCommand}
|
||||
}
|
||||
|
||||
for _, def := range e.reg.defs {
|
||||
if !matchesCommand(def, cmdName) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package commands
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ func TestExecutor_RegisteredButUnsupported_ReturnsRejected(t *testing.T) {
|
|||
defs := []Definition{{Name: "show", Channels: []string{"telegram"}}}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "whatsapp", Text: "/show"}, nil)
|
||||
res := ex.Execute(context.Background(), Request{Channel: "whatsapp", Text: "/show"})
|
||||
if res.Outcome != OutcomeRejected {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeRejected)
|
||||
}
|
||||
|
|
@ -19,7 +20,7 @@ func TestExecutor_UnknownSlashCommand_ReturnsPassthrough(t *testing.T) {
|
|||
defs := []Definition{{Name: "show", Channels: []string{"telegram"}}}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/unknown"}, nil)
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/unknown"})
|
||||
if res.Outcome != OutcomePassthrough {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomePassthrough)
|
||||
}
|
||||
|
|
@ -39,7 +40,7 @@ func TestExecutor_SupportedCommandWithHandler_ReturnsHandled(t *testing.T) {
|
|||
}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/help@my_bot"}, nil)
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/help@my_bot"})
|
||||
if res.Outcome != OutcomeHandled {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeHandled)
|
||||
}
|
||||
|
|
@ -47,3 +48,113 @@ func TestExecutor_SupportedCommandWithHandler_ReturnsHandled(t *testing.T) {
|
|||
t.Fatalf("expected handler to be called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutor_AliasUnsupportedChannel_ReturnsRejected(t *testing.T) {
|
||||
defs := []Definition{
|
||||
{
|
||||
Name: "show",
|
||||
Aliases: []string{"display"},
|
||||
Channels: []string{"telegram"},
|
||||
},
|
||||
}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "whatsapp", Text: "/display"})
|
||||
if res.Outcome != OutcomeRejected {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeRejected)
|
||||
}
|
||||
if res.Command != "show" {
|
||||
t.Fatalf("command=%q, want=%q", res.Command, "show")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutor_AliasWithHandler_ReturnsHandled(t *testing.T) {
|
||||
called := false
|
||||
defs := []Definition{
|
||||
{
|
||||
Name: "new",
|
||||
Aliases: []string{"reset"},
|
||||
Channels: []string{"telegram"},
|
||||
Handler: func(context.Context, Request) error {
|
||||
called = true
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/reset"})
|
||||
if res.Outcome != OutcomeHandled {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeHandled)
|
||||
}
|
||||
if res.Command != "new" {
|
||||
t.Fatalf("command=%q, want=%q", res.Command, "new")
|
||||
}
|
||||
if !called {
|
||||
t.Fatalf("expected handler to be called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutor_SupportedCommandWithNilHandler_ReturnsPassthrough(t *testing.T) {
|
||||
defs := []Definition{
|
||||
{Name: "session", Channels: []string{"telegram"}},
|
||||
}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/session list"})
|
||||
if res.Outcome != OutcomePassthrough {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomePassthrough)
|
||||
}
|
||||
if res.Command != "session" {
|
||||
t.Fatalf("command=%q, want=%q", res.Command, "session")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutor_NilHandlerDoesNotMaskLaterHandler(t *testing.T) {
|
||||
called := false
|
||||
defs := []Definition{
|
||||
{Name: "session", Channels: []string{"telegram"}},
|
||||
{
|
||||
Name: "session",
|
||||
Channels: []string{"telegram"},
|
||||
Handler: func(context.Context, Request) error {
|
||||
called = true
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/session"})
|
||||
if res.Outcome != OutcomeHandled {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeHandled)
|
||||
}
|
||||
if res.Command != "session" {
|
||||
t.Fatalf("command=%q, want=%q", res.Command, "session")
|
||||
}
|
||||
if !called {
|
||||
t.Fatalf("expected later handler to be called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutor_HandlerErrorIsPropagated(t *testing.T) {
|
||||
wantErr := errors.New("handler failed")
|
||||
defs := []Definition{
|
||||
{
|
||||
Name: "help",
|
||||
Channels: []string{"telegram"},
|
||||
Handler: func(context.Context, Request) error {
|
||||
return wantErr
|
||||
},
|
||||
},
|
||||
}
|
||||
ex := NewExecutor(NewRegistry(defs))
|
||||
|
||||
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "/help"})
|
||||
if res.Outcome != OutcomeHandled {
|
||||
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeHandled)
|
||||
}
|
||||
if !errors.Is(res.Err, wantErr) {
|
||||
t.Fatalf("err=%v, want=%v", res.Err, wantErr)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue