*
* 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 (
"errors"
"net"
"testing"
"github.com/agiledragon/gomonkey/v2"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"gopkg.openfuyao.cn/bkeadm/pkg/agent"
"gopkg.openfuyao.cn/bkeadm/pkg/root"
)
const (
testIPv4SegmentA = 192
testIPv4SegmentB = 168
testIPv4SegmentC = 1
testIPv4SegmentD = 1
subcommandCount = 5
generatedNameLength = 12
)
var testIP = net.IPv4(
testIPv4SegmentA,
testIPv4SegmentB,
testIPv4SegmentC,
testIPv4SegmentD,
)
func TestCommandCmdInitialization(t *testing.T) {
tests := []struct {
name string
cmd *cobra.Command
expectedUse string
expectedShort string
hasSubcommands bool
}{
{
name: "command command properties",
cmd: commandCmd,
expectedUse: "command",
expectedShort: "The machine executes remote instructions",
hasSubcommands: true,
},
{
name: "exec command properties",
cmd: execCmd,
expectedUse: "exec",
expectedShort: "Execute specified command",
hasSubcommands: false,
},
{
name: "list command properties",
cmd: liCmd,
expectedUse: "list",
expectedShort: "List all the commands",
hasSubcommands: false,
},
{
name: "info command properties",
cmd: infoCmd,
expectedUse: "info",
expectedShort: "Observe a command out",
hasSubcommands: false,
},
{
name: "remove command properties",
cmd: rmCmd,
expectedUse: "remove",
expectedShort: "Delete instruction",
hasSubcommands: false,
},
{
name: "syncTime command properties",
cmd: syncTimeCmd,
expectedUse: "syncTime",
expectedShort: "Synchronization time",
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 TestRegisterCommandCommand(t *testing.T) {
var foundCommandCmd bool
for _, cmd := range rootCmd.Commands() {
if cmd.Use == "command" {
foundCommandCmd = true
assert.Len(t, cmd.Commands(), subcommandCount)
var foundSubcommands []string
for _, subCmd := range cmd.Commands() {
foundSubcommands = append(foundSubcommands, subCmd.Use)
}
expectedSubcommands := []string{"exec", "list", "info", "remove", "syncTime"}
for _, expected := range expectedSubcommands {
assert.Contains(t, foundSubcommands, expected)
}
break
}
}
assert.True(t, foundCommandCmd, "command command should be registered in root command")
}
func TestExecCmdArgs(t *testing.T) {
tests := []struct {
name string
nodesValue string
fileValue string
commandValue string
initialName string
expectError bool
errorContains string
}{
{
name: "valid inputs with nodes and command",
nodesValue: testIP.String(),
fileValue: "",
commandValue: "echo hello",
initialName: "",
expectError: false,
},
{
name: "valid inputs with nodes and file",
nodesValue: testIP.String(),
fileValue: "/path/to/script.sh",
commandValue: "",
initialName: "",
expectError: false,
},
{
name: "missing nodes should return error",
nodesValue: "",
fileValue: "",
commandValue: "echo hello",
initialName: "",
expectError: true,
errorContains: "The `nodes` parameter is required",
},
{
name: "missing both file and command should return error",
nodesValue: testIP.String(),
fileValue: "",
commandValue: "",
initialName: "",
expectError: true,
errorContains: "One of the parameters `file` and `command` must exist",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalNodes := execOption.Nodes
originalFile := execOption.File
originalCommand := execOption.Command
originalName := execOption.Name
defer func() {
execOption.Nodes = originalNodes
execOption.File = originalFile
execOption.Command = originalCommand
execOption.Name = originalName
}()
execOption.Nodes = tt.nodesValue
execOption.File = tt.fileValue
execOption.Command = tt.commandValue
execOption.Name = tt.initialName
err := execCmd.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 TestExecCmdArgsGeneratesName(t *testing.T) {
originalNodes := execOption.Nodes
originalFile := execOption.File
originalCommand := execOption.Command
originalName := execOption.Name
defer func() {
execOption.Nodes = originalNodes
execOption.File = originalFile
execOption.Command = originalCommand
execOption.Name = originalName
}()
execOption.Nodes = testIP.String()
execOption.File = ""
execOption.Command = "echo hello"
execOption.Name = ""
err := execCmd.Args(nil, nil)
assert.NoError(t, err)
assert.NotEmpty(t, execOption.Name)
assert.Len(t, execOption.Name, generatedNameLength)
}
func TestExecCmdPreRunE(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return errors.New("The kube config configuration file does not exist. ")
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := execCmd.PreRunE(cmd, args)
assert.Error(t, err)
}
func TestExecCmdRun(t *testing.T) {
originalArgs := execOption.Args
originalOptions := execOption.Options
defer func() {
execOption.Args = originalArgs
execOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*agent.Options).Exec, func(o *agent.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
execCmd.Run(cmd, args)
assert.Equal(t, args, execOption.Args)
assert.Equal(t, options, execOption.Options)
}
func TestLiCmdPreRunEAndRun(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*agent.Options).List, func(o *agent.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := liCmd.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, liOption.Args)
assert.Equal(t, options, liOption.Options)
liCmd.Run(cmd, args)
assert.Equal(t, args, liOption.Args)
assert.Equal(t, options, liOption.Options)
}
func TestInfoCmdPreRunEAndRun(t *testing.T) {
originalArgs := infoOptions.Args
originalOptions := infoOptions.Options
defer func() {
infoOptions.Args = originalArgs
infoOptions.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*agent.Options).Info, func(o *agent.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := infoCmd.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, infoOptions.Args)
assert.Equal(t, options, infoOptions.Options)
infoCmd.Run(cmd, args)
assert.Equal(t, args, infoOptions.Args)
assert.Equal(t, options, infoOptions.Options)
}
func TestRmCmdPreRunEAndRun(t *testing.T) {
originalArgs := rmOptions.Args
originalOptions := rmOptions.Options
defer func() {
rmOptions.Args = originalArgs
rmOptions.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*root.Options).ClusterPre, func(o *root.Options) error {
return nil
})
patches.ApplyFunc((*agent.Options).Remove, func(o *agent.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := rmCmd.PreRunE(cmd, args)
assert.NoError(t, err)
assert.Equal(t, args, rmOptions.Args)
assert.Equal(t, options, rmOptions.Options)
rmCmd.Run(cmd, args)
assert.Equal(t, args, rmOptions.Args)
assert.Equal(t, options, rmOptions.Options)
}
func TestSyncTimeCmdPreRunEAndRun(t *testing.T) {
originalArgs := syncTimeOptions.Args
originalOptions := syncTimeOptions.Options
defer func() {
syncTimeOptions.Args = originalArgs
syncTimeOptions.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*agent.Options).SyncTime, func(o *agent.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
err := syncTimeCmd.PreRunE(cmd, args)
assert.NoError(t, err)
syncTimeCmd.Run(cmd, args)
assert.Equal(t, args, syncTimeOptions.Args)
assert.Equal(t, options, syncTimeOptions.Options)
}