Skip to content

Commit 77c9e15

Browse files
committed
fix: check duplicated sub command name and alias
1 parent b686f4f commit 77c9e15

File tree

4 files changed

+116
-0
lines changed

4 files changed

+116
-0
lines changed

app.go

+3
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
329329
a.rootCommand = a.newRootCommand()
330330
cCtx.Command = a.rootCommand
331331

332+
if err := checkDuplicatedCmds(a.rootCommand); err != nil {
333+
return err
334+
}
332335
return a.rootCommand.Run(cCtx, arguments...)
333336
}
334337

app_test.go

+89
Original file line numberDiff line numberDiff line change
@@ -3087,3 +3087,92 @@ func TestFlagAction(t *testing.T) {
30873087
})
30883088
}
30893089
}
3090+
3091+
func TestDuplicateSubcommand(t *testing.T) {
3092+
var testdata = []struct {
3093+
app *App
3094+
expectNoError bool
3095+
}{
3096+
{&App{
3097+
Name: "p1",
3098+
}, true},
3099+
{&App{
3100+
Name: "p2",
3101+
Commands: []*Command{},
3102+
}, true},
3103+
{&App{
3104+
Name: "p3",
3105+
Commands: []*Command{{Name: "sub1"}},
3106+
}, true},
3107+
{&App{
3108+
Name: "p4",
3109+
Commands: []*Command{{Name: "sub1"}, {Name: "sub1"}},
3110+
}, false},
3111+
{&App{
3112+
Name: "p5",
3113+
Commands: []*Command{{Name: "sub1"}, {Name: "sub2", Aliases: []string{"sub1"}}},
3114+
}, false},
3115+
{&App{
3116+
Name: "p6",
3117+
Commands: []*Command{{Name: "sub1"}, {Name: "sub2"}},
3118+
}, true},
3119+
}
3120+
for _, tt := range testdata {
3121+
err := tt.app.Run([]string{})
3122+
if tt.expectNoError {
3123+
expect(t, err, nil)
3124+
} else {
3125+
expectNotEqual(t, err, nil)
3126+
}
3127+
3128+
err = checkDuplicatedCmds(tt.app.rootCommand)
3129+
if tt.expectNoError {
3130+
expect(t, err, nil)
3131+
} else {
3132+
expectNotEqual(t, err, nil)
3133+
}
3134+
}
3135+
3136+
var testNested = []struct {
3137+
app *App
3138+
subcommandTocheck string
3139+
expectNoError bool
3140+
}{
3141+
{
3142+
&App{
3143+
Name: "nested-0",
3144+
Commands: []*Command{
3145+
{Name: "sub1",
3146+
Subcommands: []*Command{
3147+
{Name: "sub1_a"},
3148+
{Name: "sub1_b"},
3149+
},
3150+
},
3151+
{Name: "sub2"}},
3152+
},
3153+
"sub1",
3154+
true},
3155+
{&App{
3156+
Name: "nested-1",
3157+
Commands: []*Command{
3158+
{Name: "sub1",
3159+
Subcommands: []*Command{
3160+
{Name: "sub1_a"},
3161+
{Name: "sub1_a"},
3162+
},
3163+
},
3164+
{Name: "sub2"}},
3165+
},
3166+
"sub1",
3167+
false},
3168+
}
3169+
3170+
for _, tt := range testNested {
3171+
err := tt.app.Run([]string{tt.app.Name, tt.subcommandTocheck})
3172+
if tt.expectNoError {
3173+
expect(t, err, nil)
3174+
} else {
3175+
expectNotEqual(t, err, nil)
3176+
}
3177+
}
3178+
}

command.go

+16
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ func (c *Command) Run(cCtx *Context, arguments ...string) (err error) {
149149

150150
if !c.isRoot {
151151
c.setup(cCtx)
152+
if err := checkDuplicatedCmds(c); err != nil {
153+
return err
154+
}
152155
}
153156

154157
a := args(arguments)
@@ -404,3 +407,16 @@ func hasCommand(commands []*Command, command *Command) bool {
404407

405408
return false
406409
}
410+
411+
func checkDuplicatedCmds(parent *Command) error {
412+
seen := make(map[string]struct{})
413+
for _, c := range parent.Subcommands {
414+
for _, name := range c.Names() {
415+
if _, exists := seen[name]; exists {
416+
return fmt.Errorf("parent command [%s] has duplicated subcommand name or alias: %s", parent.Name, name)
417+
}
418+
seen[name] = struct{}{}
419+
}
420+
}
421+
return nil
422+
}

helpers_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@ func expect(t *testing.T, a interface{}, b interface{}) {
1717
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
1818
}
1919
}
20+
21+
func expectNotEqual(t *testing.T, a interface{}, b interface{}) {
22+
t.Helper()
23+
24+
if reflect.DeepEqual(a, b) {
25+
t.Errorf("Expected not equal, but got: %v (type %v), %v (type %v) ", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
26+
}
27+
}

0 commit comments

Comments
 (0)