*
* Copyright (c) 2025 Bocloud Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* <http://license.coscl.org.cn/MulanPSL2>
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*
*/
package cmd
import (
"testing"
"github.com/agiledragon/gomonkey/v2"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"gopkg.openfuyao.cn/bkeadm/pkg/cluster"
"gopkg.openfuyao.cn/bkeadm/pkg/root"
)
const (
clusterSubcommandCount = 6
)
func TestClusterCmdInitialization(t *testing.T) {
tests := []struct {
name string
cmd *cobra.Command
expectedUse string
expectedShort string
hasSubcommands bool
}{
{
name: "cluster command properties",
cmd: clusterCmd,
expectedUse: "cluster",
expectedShort: "Operating an existing cluster.",
hasSubcommands: true,
},
{
name: "list command properties",
cmd: listDep,
expectedUse: "list",
expectedShort: "Cluster list",
hasSubcommands: false,
},
{
name: "remove command properties",
cmd: removeDep,
expectedUse: "remove",
expectedShort: "Delete a Specified Cluster",
hasSubcommands: false,
},
{
name: "create command properties",
cmd: createDep,
expectedUse: "create",
expectedShort: "Deploying a Cluster",
hasSubcommands: false,
},
{
name: "scale command properties",
cmd: scaleDep,
expectedUse: "scale",
expectedShort: "shard cluster",
hasSubcommands: false,
},
{
name: "logs command properties",
cmd: logsDep,
expectedUse: "logs",
expectedShort: "Obtain cluster deployment events",
hasSubcommands: false,
},
{
name: "exist command properties",
cmd: existDep,
expectedUse: "exist",
expectedShort: "Manage existing clusters",
hasSubcommands: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expectedUse, tt.cmd.Use)
assert.Equal(t, tt.expectedShort, tt.cmd.Short)
})
}
}
func TestRegisterClusterCommand(t *testing.T) {
var foundClusterCmd bool
for _, cmd := range rootCmd.Commands() {
if cmd.Use == "cluster" {
foundClusterCmd = true
assert.Len(t, cmd.Commands(), clusterSubcommandCount)
var foundSubcommands []string
for _, subCmd := range cmd.Commands() {
foundSubcommands = append(foundSubcommands, subCmd.Use)
}
expectedSubcommands := []string{"list", "remove", "create", "scale", "logs", "exist"}
for _, expected := range expectedSubcommands {
assert.Contains(t, foundSubcommands, expected)
}
break
}
}
assert.True(t, foundClusterCmd, "cluster command should be registered in root command")
}
func TestListCmdPreRunEAndRun(t *testing.T) {
originalArgs := listOption.Args
originalOptions := listOption.Options
defer func() {
listOption.Args = originalArgs
listOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*cluster.Options).List, func(o *cluster.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := listDep.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, listOption.Args)
assert.Equal(t, options, listOption.Options)
listDep.Run(cmd, args)
assert.Equal(t, args, listOption.Args)
assert.Equal(t, options, listOption.Options)
}
func TestRemoveCmdArgs(t *testing.T) {
tests := []struct {
name string
args []string
expectError bool
errorContains string
}{
{
name: "valid input with ns/name format",
args: []string{"namespace/name"},
expectError: false,
},
{
name: "missing args should return error",
args: []string{},
expectError: true,
errorContains: "Required parameters are missing",
},
{
name: "invalid format should return error",
args: []string{"invalid-format"},
expectError: true,
errorContains: "The parameter format is invalid, The parameter format is ns/name",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := removeDep.Args(nil, tt.args)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
}
})
}
}
func TestRemoveCmdPreRunEAndRun(t *testing.T) {
originalArgs := removeOption.Args
originalOptions := removeOption.Options
defer func() {
removeOption.Args = originalArgs
removeOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*cluster.Options).Remove, func(o *cluster.Options) {
})
cmd := &cobra.Command{}
args := []string{"namespace/name"}
err := removeDep.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, removeOption.Args)
assert.Equal(t, options, removeOption.Options)
removeDep.Run(cmd, args)
assert.Equal(t, args, removeOption.Args)
assert.Equal(t, options, removeOption.Options)
}
func TestCreateCmdArgs(t *testing.T) {
tests := []struct {
name string
fileValue string
nodesFileValue string
expectError bool
errorContains string
}{
{
name: "valid input with file",
fileValue: "/path/to/file.yaml",
nodesFileValue: "/path/to/nodes.yaml",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
expectError: true,
errorContains: "The `file` parameter is required",
},
{
name: "missing nodes file should return error",
fileValue: "/path/to/file.yaml",
nodesFileValue: "",
expectError: true,
errorContains: "The `nodes` parameter is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := createOption.File
originalNodesFile := createOption.NodesFile
defer func() {
createOption.File = originalFile
createOption.NodesFile = originalNodesFile
}()
createOption.File = tt.fileValue
createOption.NodesFile = tt.nodesFileValue
err := createDep.Args(nil, nil)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
}
})
}
}
func TestCreateCmdPreRunEAndRun(t *testing.T) {
originalArgs := createOption.Args
originalOptions := createOption.Options
originalFile := createOption.File
defer func() {
createOption.Args = originalArgs
createOption.Options = originalOptions
createOption.File = originalFile
}()
createOption.File = "/path/to/file.yaml"
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*cluster.Options).Cluster, func(o *cluster.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := createDep.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, createOption.Args)
assert.Equal(t, options, createOption.Options)
createDep.Run(cmd, args)
assert.Equal(t, args, createOption.Args)
assert.Equal(t, options, createOption.Options)
}
func TestScaleCmdArgs(t *testing.T) {
tests := []struct {
name string
fileValue string
nodesFileValue string
expectError bool
errorContains string
}{
{
name: "valid input with file",
fileValue: "/path/to/file.yaml",
nodesFileValue: "/path/to/nodes.yaml",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
expectError: true,
errorContains: "The `file` parameter is required",
},
{
name: "missing nodes file should return error",
fileValue: "/path/to/file.yaml",
nodesFileValue: "",
expectError: true,
errorContains: "The `nodes` parameter is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := scaleOption.File
originalNodesFile := scaleOption.NodesFile
defer func() {
scaleOption.File = originalFile
scaleOption.NodesFile = originalNodesFile
}()
scaleOption.File = tt.fileValue
scaleOption.NodesFile = tt.nodesFileValue
err := scaleDep.Args(nil, nil)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
}
})
}
}
func TestScaleCmdPreRunEAndRun(t *testing.T) {
originalArgs := scaleOption.Args
originalOptions := scaleOption.Options
originalFile := scaleOption.File
defer func() {
scaleOption.Args = originalArgs
scaleOption.Options = originalOptions
scaleOption.File = originalFile
}()
scaleOption.File = "/path/to/file.yaml"
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*cluster.Options).Scale, func(o *cluster.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := scaleDep.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, scaleOption.Args)
assert.Equal(t, options, scaleOption.Options)
scaleDep.Run(cmd, args)
assert.Equal(t, args, scaleOption.Args)
assert.Equal(t, options, scaleOption.Options)
}
func TestLogsCmdArgs(t *testing.T) {
tests := []struct {
name string
args []string
expectError bool
errorContains string
}{
{
name: "valid input with ns/name format",
args: []string{"namespace/name"},
expectError: false,
},
{
name: "missing args should return error",
args: []string{},
expectError: true,
errorContains: "Required parameters are missing",
},
{
name: "invalid format should return error",
args: []string{"invalid-format"},
expectError: true,
errorContains: "The parameter format is invalid, The parameter format is ns/name",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := logsDep.Args(nil, tt.args)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
}
})
}
}
func TestLogsCmdPreRunEAndRun(t *testing.T) {
originalArgs := logsOption.Args
originalOptions := logsOption.Options
defer func() {
logsOption.Args = originalArgs
logsOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*cluster.Options).Log, func(o *cluster.Options) {
})
cmd := &cobra.Command{}
args := []string{"namespace/name"}
err := logsDep.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, logsOption.Args)
assert.Equal(t, options, logsOption.Options)
logsDep.Run(cmd, args)
assert.Equal(t, args, logsOption.Args)
assert.Equal(t, options, logsOption.Options)
}
func TestExistCmdArgs(t *testing.T) {
tests := []struct {
name string
fileValue string
confValue string
expectError bool
errorContains string
}{
{
name: "valid input with both file and conf",
fileValue: "/path/to/file.yaml",
confValue: "/path/to/conf.yaml",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
confValue: "/path/to/conf.yaml",
expectError: true,
errorContains: "The `file` parameter is required",
},
{
name: "missing conf should return error",
fileValue: "/path/to/file.yaml",
confValue: "",
expectError: true,
errorContains: "The `conf` parameter is required",
},
{
name: "missing both should return error",
fileValue: "",
confValue: "",
expectError: true,
errorContains: "The `file` parameter is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := existOption.File
originalConf := existOption.Conf
defer func() {
existOption.File = originalFile
existOption.Conf = originalConf
}()
existOption.File = tt.fileValue
existOption.Conf = tt.confValue
err := existDep.Args(nil, nil)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
}
})
}
}
func TestExistCmdPreRunEAndRun(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*cluster.Options).ExistsCluster, func(o *cluster.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := existDep.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, existOption.Args)
assert.Equal(t, options, existOption.Options)
existDep.Run(cmd, args)
assert.Equal(t, args, existOption.Args)
assert.Equal(t, options, existOption.Options)
}