495 lines
12 KiB
Go
495 lines
12 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cobracmd
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func TestChildByName(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
parent *cobra.Command
|
|
child string
|
|
wantNil bool
|
|
wantName string
|
|
}{
|
|
{
|
|
name: "nil parent returns nil",
|
|
parent: nil,
|
|
child: "anything",
|
|
wantNil: true,
|
|
},
|
|
{
|
|
name: "no matching child returns nil",
|
|
parent: func() *cobra.Command {
|
|
p := &cobra.Command{Use: "root"}
|
|
p.AddCommand(&cobra.Command{Use: "alpha"})
|
|
return p
|
|
}(),
|
|
child: "beta",
|
|
wantNil: true,
|
|
},
|
|
{
|
|
name: "matching child is returned",
|
|
parent: func() *cobra.Command {
|
|
p := &cobra.Command{Use: "root"}
|
|
p.AddCommand(&cobra.Command{Use: "alpha"})
|
|
p.AddCommand(&cobra.Command{Use: "beta"})
|
|
return p
|
|
}(),
|
|
child: "beta",
|
|
wantNil: false,
|
|
wantName: "beta",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := ChildByName(tc.parent, tc.child)
|
|
if tc.wantNil {
|
|
if got != nil {
|
|
t.Fatalf("expected nil, got %v", got.Name())
|
|
}
|
|
return
|
|
}
|
|
if got == nil {
|
|
t.Fatal("expected non-nil command")
|
|
}
|
|
if got.Name() != tc.wantName {
|
|
t.Fatalf("expected name %q, got %q", tc.wantName, got.Name())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFlagChanged(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
setup func() *cobra.Command
|
|
flagName string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "flag exists and changed",
|
|
setup: func() *cobra.Command {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().String("output", "", "output format")
|
|
_ = cmd.Flags().Set("output", "json")
|
|
return cmd
|
|
},
|
|
flagName: "output",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "flag exists but not changed",
|
|
setup: func() *cobra.Command {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().String("output", "table", "output format")
|
|
return cmd
|
|
},
|
|
flagName: "output",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "flag does not exist",
|
|
setup: func() *cobra.Command {
|
|
return &cobra.Command{Use: "test"}
|
|
},
|
|
flagName: "nonexistent",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
cmd := tc.setup()
|
|
got := FlagChanged(cmd, tc.flagName)
|
|
if got != tc.want {
|
|
t.Fatalf("FlagChanged() = %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewGroupCommand(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cmd := NewGroupCommand("mygroup", "my group description")
|
|
|
|
if cmd.Use != "mygroup" {
|
|
t.Fatalf("Use = %q, want %q", cmd.Use, "mygroup")
|
|
}
|
|
if cmd.Short != "my group description" {
|
|
t.Fatalf("Short = %q, want %q", cmd.Short, "my group description")
|
|
}
|
|
if cmd.Args == nil {
|
|
t.Fatal("Args should be set (cobra.NoArgs)")
|
|
}
|
|
// Verify Args rejects arguments.
|
|
if err := cmd.Args(cmd, []string{"extra"}); err == nil {
|
|
t.Fatal("expected Args to reject extra arguments")
|
|
}
|
|
// Verify RunE is set and returns help (no error for valid invocation).
|
|
if cmd.RunE == nil {
|
|
t.Fatal("RunE should not be nil")
|
|
}
|
|
// RunE calls cmd.Help() which should not error.
|
|
if err := cmd.RunE(cmd, nil); err != nil {
|
|
t.Fatalf("RunE returned unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNewHiddenGroupCommand(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cmd := NewHiddenGroupCommand("secret", "hidden group")
|
|
|
|
if !cmd.Hidden {
|
|
t.Fatal("expected Hidden to be true")
|
|
}
|
|
if cmd.Use != "secret" {
|
|
t.Fatalf("Use = %q, want %q", cmd.Use, "secret")
|
|
}
|
|
if cmd.Short != "hidden group" {
|
|
t.Fatalf("Short = %q, want %q", cmd.Short, "hidden group")
|
|
}
|
|
}
|
|
|
|
func TestNewPlaceholderParent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
child1 := &cobra.Command{Use: "child1"}
|
|
child2 := &cobra.Command{Use: "child2"}
|
|
|
|
cmd := NewPlaceholderParent("parent", "parent desc", child1, child2)
|
|
|
|
if cmd.Use != "parent" {
|
|
t.Fatalf("Use = %q, want %q", cmd.Use, "parent")
|
|
}
|
|
if len(cmd.Commands()) != 2 {
|
|
t.Fatalf("expected 2 children, got %d", len(cmd.Commands()))
|
|
}
|
|
if ChildByName(cmd, "child1") == nil {
|
|
t.Fatal("child1 not found")
|
|
}
|
|
if ChildByName(cmd, "child2") == nil {
|
|
t.Fatal("child2 not found")
|
|
}
|
|
}
|
|
|
|
func TestIsGenericOverlayShort(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "Generated compatibility overlay prefix",
|
|
input: "Generated compatibility overlay for foo",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Generated raw tool overlay prefix",
|
|
input: "Generated raw tool overlay for bar",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Fallback-only prefix",
|
|
input: "Fallback-only command",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "non-matching string",
|
|
input: "A real description of a command",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty string",
|
|
input: "",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "partial match not at prefix",
|
|
input: "This is a Generated compatibility overlay",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := IsGenericOverlayShort(tc.input)
|
|
if got != tc.want {
|
|
t.Fatalf("IsGenericOverlayShort(%q) = %v, want %v", tc.input, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMergeCommandTree(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil inputs are safe", func(t *testing.T) {
|
|
t.Parallel()
|
|
MergeCommandTree(nil, nil)
|
|
MergeCommandTree(&cobra.Command{Use: "a"}, nil)
|
|
MergeCommandTree(nil, &cobra.Command{Use: "b"})
|
|
})
|
|
|
|
t.Run("short override from generic to real", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Short: "Generated compatibility overlay for root"}
|
|
src := &cobra.Command{Use: "root", Short: "Real description"}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Short != "Real description" {
|
|
t.Fatalf("Short = %q, want %q", dst.Short, "Real description")
|
|
}
|
|
})
|
|
|
|
t.Run("short not overridden when dst is real", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Short: "Real description"}
|
|
src := &cobra.Command{Use: "root", Short: "Another description"}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Short != "Real description" {
|
|
t.Fatalf("Short = %q, want %q", dst.Short, "Real description")
|
|
}
|
|
})
|
|
|
|
t.Run("short override from empty", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Short: ""}
|
|
src := &cobra.Command{Use: "root", Short: "New description"}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Short != "New description" {
|
|
t.Fatalf("Short = %q, want %q", dst.Short, "New description")
|
|
}
|
|
})
|
|
|
|
t.Run("long override from empty", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Long: ""}
|
|
src := &cobra.Command{Use: "root", Long: "Detailed description"}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Long != "Detailed description" {
|
|
t.Fatalf("Long = %q, want %q", dst.Long, "Detailed description")
|
|
}
|
|
})
|
|
|
|
t.Run("long not overridden when dst is set", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Long: "Already set"}
|
|
src := &cobra.Command{Use: "root", Long: "Other long"}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Long != "Already set" {
|
|
t.Fatalf("Long = %q, want %q", dst.Long, "Already set")
|
|
}
|
|
})
|
|
|
|
t.Run("hidden override from true to false", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Hidden: true}
|
|
src := &cobra.Command{Use: "root", Hidden: false}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Hidden {
|
|
t.Fatal("expected Hidden to become false")
|
|
}
|
|
})
|
|
|
|
t.Run("hidden stays false when both false", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root", Hidden: false}
|
|
src := &cobra.Command{Use: "root", Hidden: true}
|
|
MergeCommandTree(dst, src)
|
|
if dst.Hidden {
|
|
t.Fatal("expected Hidden to remain false")
|
|
}
|
|
})
|
|
|
|
t.Run("child merge recursive", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root"}
|
|
dstChild := &cobra.Command{Use: "sub", Short: ""}
|
|
dst.AddCommand(dstChild)
|
|
|
|
src := &cobra.Command{Use: "root"}
|
|
srcChild := &cobra.Command{Use: "sub", Short: "Merged short"}
|
|
src.AddCommand(srcChild)
|
|
|
|
MergeCommandTree(dst, src)
|
|
found := ChildByName(dst, "sub")
|
|
if found == nil {
|
|
t.Fatal("expected child 'sub' to exist")
|
|
}
|
|
if found.Short != "Merged short" {
|
|
t.Fatalf("child Short = %q, want %q", found.Short, "Merged short")
|
|
}
|
|
})
|
|
|
|
t.Run("leaf replacement by higher priority", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root"}
|
|
dstLeaf := &cobra.Command{Use: "leaf", Short: "old"}
|
|
SetOverridePriority(dstLeaf, 1)
|
|
dst.AddCommand(dstLeaf)
|
|
|
|
src := &cobra.Command{Use: "root"}
|
|
srcLeaf := &cobra.Command{Use: "leaf", Short: "new"}
|
|
SetOverridePriority(srcLeaf, 5)
|
|
src.AddCommand(srcLeaf)
|
|
|
|
MergeCommandTree(dst, src)
|
|
found := ChildByName(dst, "leaf")
|
|
if found == nil {
|
|
t.Fatal("expected child 'leaf' to exist")
|
|
}
|
|
if found.Short != "new" {
|
|
t.Fatalf("leaf Short = %q, want %q", found.Short, "new")
|
|
}
|
|
})
|
|
|
|
t.Run("new child addition", func(t *testing.T) {
|
|
t.Parallel()
|
|
dst := &cobra.Command{Use: "root"}
|
|
dst.AddCommand(&cobra.Command{Use: "existing"})
|
|
|
|
src := &cobra.Command{Use: "root"}
|
|
src.AddCommand(&cobra.Command{Use: "brand-new", Short: "added"})
|
|
|
|
MergeCommandTree(dst, src)
|
|
found := ChildByName(dst, "brand-new")
|
|
if found == nil {
|
|
t.Fatal("expected new child 'brand-new' to be added")
|
|
}
|
|
if found.Short != "added" {
|
|
t.Fatalf("Short = %q, want %q", found.Short, "added")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestReplaceChild(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil inputs are safe", func(t *testing.T) {
|
|
t.Parallel()
|
|
ReplaceChild(nil, nil, nil)
|
|
ReplaceChild(&cobra.Command{Use: "p"}, nil, &cobra.Command{Use: "n"})
|
|
ReplaceChild(&cobra.Command{Use: "p"}, &cobra.Command{Use: "o"}, nil)
|
|
})
|
|
|
|
t.Run("normal replacement", func(t *testing.T) {
|
|
t.Parallel()
|
|
parent := &cobra.Command{Use: "root"}
|
|
old := &cobra.Command{Use: "child", Short: "old"}
|
|
parent.AddCommand(old)
|
|
|
|
replacement := &cobra.Command{Use: "child", Short: "new"}
|
|
ReplaceChild(parent, old, replacement)
|
|
|
|
found := ChildByName(parent, "child")
|
|
if found == nil {
|
|
t.Fatal("expected child to exist")
|
|
}
|
|
if found.Short != "new" {
|
|
t.Fatalf("Short = %q, want %q", found.Short, "new")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLocalFlagCount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("nil cmd returns 0", func(t *testing.T) {
|
|
t.Parallel()
|
|
if got := LocalFlagCount(nil); got != 0 {
|
|
t.Fatalf("LocalFlagCount(nil) = %d, want 0", got)
|
|
}
|
|
})
|
|
|
|
t.Run("hidden flags are excluded", func(t *testing.T) {
|
|
t.Parallel()
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().String("visible", "", "visible flag")
|
|
cmd.Flags().String("secret", "", "hidden flag")
|
|
_ = cmd.Flags().MarkHidden("secret")
|
|
|
|
if got := LocalFlagCount(cmd); got != 1 {
|
|
t.Fatalf("LocalFlagCount() = %d, want 1", got)
|
|
}
|
|
})
|
|
|
|
t.Run("visible flags are counted", func(t *testing.T) {
|
|
t.Parallel()
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().String("a", "", "flag a")
|
|
cmd.Flags().String("b", "", "flag b")
|
|
cmd.Flags().Int("c", 0, "flag c")
|
|
|
|
if got := LocalFlagCount(cmd); got != 3 {
|
|
t.Fatalf("LocalFlagCount() = %d, want 3", got)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyCommandPath(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
cmd func() *cobra.Command
|
|
want string
|
|
}{
|
|
{
|
|
name: "normal path strips dws prefix",
|
|
cmd: func() *cobra.Command {
|
|
root := &cobra.Command{Use: "dws"}
|
|
sub := &cobra.Command{Use: "product"}
|
|
leaf := &cobra.Command{Use: "action"}
|
|
sub.AddCommand(leaf)
|
|
root.AddCommand(sub)
|
|
return leaf
|
|
},
|
|
want: "product action",
|
|
},
|
|
{
|
|
name: "root only returns dws unchanged",
|
|
cmd: func() *cobra.Command {
|
|
return &cobra.Command{Use: "dws"}
|
|
},
|
|
want: "dws",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := LegacyCommandPath(tc.cmd())
|
|
if got != tc.want {
|
|
t.Fatalf("LegacyCommandPath() = %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|