*
* 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 (
"runtime"
"testing"
"github.com/agiledragon/gomonkey/v2"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"gopkg.openfuyao.cn/bkeadm/pkg/build"
"gopkg.openfuyao.cn/bkeadm/utils"
)
const (
buildSubcommandCount = 5
)
func TestBuildCmdInitialization(t *testing.T) {
tests := []struct {
name string
cmd *cobra.Command
expectedUse string
expectedShort string
hasSubcommands bool
}{
{
name: "build command properties",
cmd: buildCmd,
expectedUse: "build",
expectedShort: "Build the BKE installation package.",
hasSubcommands: true,
},
{
name: "build config command properties",
cmd: buildConfigCmd,
expectedUse: "config",
expectedShort: "The default BKE configuration is exported.",
hasSubcommands: false,
},
{
name: "patch command properties",
cmd: patchCmd,
expectedUse: "patch",
expectedShort: "Build the bke patch pack.",
hasSubcommands: false,
},
{
name: "online image command properties",
cmd: onlineCmd,
expectedUse: "online-image",
expectedShort: "Compile an image installed online",
hasSubcommands: false,
},
{
name: "rpm command properties",
cmd: rpmCmd,
expectedUse: "rpm",
expectedShort: "Build an offline rpm package",
hasSubcommands: false,
},
{
name: "check command properties",
cmd: preCheckCmd,
expectedUse: "check",
expectedShort: "Check the mirror in the configuration file",
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 TestRegisterBuildCommand(t *testing.T) {
var foundBuildCmd bool
for _, cmd := range rootCmd.Commands() {
if cmd.Use == "build" {
foundBuildCmd = true
assert.Len(t, cmd.Commands(), buildSubcommandCount)
var foundSubcommands []string
for _, subCmd := range cmd.Commands() {
foundSubcommands = append(foundSubcommands, subCmd.Use)
}
expectedSubcommands := []string{"config", "patch", "online-image", "rpm", "check"}
for _, expected := range expectedSubcommands {
assert.Contains(t, foundSubcommands, expected)
}
break
}
}
assert.True(t, foundBuildCmd, "build command should be registered in root command")
}
func TestBuildCmdPreRunE(t *testing.T) {
tests := []struct {
name string
fileValue string
expectError bool
errorContains string
}{
{
name: "valid input with file",
fileValue: "/path/to/file.yaml",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
expectError: true,
errorContains: "The parameter `file` is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := buildOption.File
defer func() {
buildOption.File = originalFile
}()
buildOption.File = tt.fileValue
err := buildCmd.PreRunE(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 TestBuildCmdRun(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*build.Options).Build, func(o *build.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
buildCmd.Run(cmd, args)
assert.NotEqual(t, args, buildOption.Args)
assert.Equal(t, options, buildOption.Options)
}
func TestBuildConfigCmdRun(t *testing.T) {
originalArgs := buildOption.Args
originalOptions := buildOption.Options
defer func() {
buildOption.Args = originalArgs
buildOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*build.Options).Config, func(o *build.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
buildConfigCmd.Run(cmd, args)
assert.NotEqual(t, args, buildOption.Args)
assert.Equal(t, options, buildOption.Options)
}
func TestPatchCmdPreRunE(t *testing.T) {
tests := []struct {
name string
fileValue string
strategyValue string
expectError bool
errorContains string
}{
{
name: "valid input with file and valid strategy",
fileValue: "/path/to/file.yaml",
strategyValue: "registry",
expectError: false,
},
{
name: "valid input with file and invalid strategy (gets default)",
fileValue: "/path/to/file.yaml",
strategyValue: "invalid",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
strategyValue: "registry",
expectError: true,
errorContains: "The parameter `file` is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := patchOption.File
originalStrategy := patchOption.Strategy
defer func() {
patchOption.File = originalFile
patchOption.Strategy = originalStrategy
}()
patchOption.File = tt.fileValue
patchOption.Strategy = tt.strategyValue
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(utils.ContainsString, func(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
})
err := patchCmd.PreRunE(nil, nil)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
if tt.strategyValue == "invalid" {
assert.Equal(t, "registry", patchOption.Strategy)
}
}
})
}
}
func TestPatchCmdRun(t *testing.T) {
originalArgs := patchOption.Args
originalOptions := patchOption.Options
defer func() {
patchOption.Args = originalArgs
patchOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*build.Options).Patch, func(o *build.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
patchCmd.Run(cmd, args)
assert.NotEqual(t, args, patchOption.Args)
assert.Equal(t, options, patchOption.Options)
}
func TestOnlineCmdPreRunE(t *testing.T) {
tests := []struct {
name string
fileValue string
targetValue string
strategyValue string
archValue string
expectError bool
errorContains string
}{
{
name: "valid input with all required params",
fileValue: "/path/to/file.yaml",
targetValue: "cr.registry.com/repo:tag",
strategyValue: "registry",
archValue: "amd64",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
targetValue: "cr.registry.com/repo:tag",
strategyValue: "registry",
archValue: "amd64",
expectError: true,
errorContains: "The parameter `file` is required",
},
{
name: "missing target should return error",
fileValue: "/path/to/file.yaml",
targetValue: "",
strategyValue: "registry",
archValue: "amd64",
expectError: true,
errorContains: "The parameter `target` is required",
},
{
name: "missing arch gets default",
fileValue: "/path/to/file.yaml",
targetValue: "cr.registry.com/repo:tag",
strategyValue: "invalid",
archValue: "",
expectError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := onlineOption.File
originalTarget := onlineOption.Target
originalStrategy := onlineOption.Strategy
originalArch := onlineOption.Arch
defer func() {
onlineOption.File = originalFile
onlineOption.Target = originalTarget
onlineOption.Strategy = originalStrategy
onlineOption.Arch = originalArch
}()
onlineOption.File = tt.fileValue
onlineOption.Target = tt.targetValue
onlineOption.Strategy = tt.strategyValue
onlineOption.Arch = tt.archValue
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(utils.ContainsString, func(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
})
err := onlineCmd.PreRunE(nil, nil)
if tt.expectError {
assert.Error(t, err)
if tt.errorContains != "" {
assert.Contains(t, err.Error(), tt.errorContains)
}
} else {
assert.NoError(t, err)
if tt.strategyValue == "invalid" {
assert.Equal(t, "registry", onlineOption.Strategy)
}
if tt.archValue == "" {
assert.Equal(t, runtime.GOARCH, onlineOption.Arch)
}
}
})
}
}
func TestOnlineCmdRun(t *testing.T) {
originalArgs := onlineOption.Args
originalOptions := onlineOption.Options
defer func() {
onlineOption.Args = originalArgs
onlineOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*build.Options).BuildOnlineImage, func(o *build.Options) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
onlineCmd.Run(cmd, args)
assert.NotEqual(t, args, onlineOption.Args)
assert.Equal(t, options, onlineOption.Options)
}
func TestRpmCmdPreRunE(t *testing.T) {
tests := []struct {
name string
addValue string
packageValue string
expectError bool
errorContains string
}{
{
name: "both add and package provided",
addValue: "centos/8/amd64",
packageValue: "docker-ce",
expectError: false,
},
{
name: "neither add nor package provided",
addValue: "",
packageValue: "",
expectError: false,
},
{
name: "only add provided should return error",
addValue: "centos/8/amd64",
packageValue: "",
expectError: true,
errorContains: "The parameter `add` or `package` is required",
},
{
name: "only package provided should return error",
addValue: "",
packageValue: "docker-ce",
expectError: true,
errorContains: "The parameter `add` or `package` is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalAdd := rpmOption.Add
originalPackage := rpmOption.Package
defer func() {
rpmOption.Add = originalAdd
rpmOption.Package = originalPackage
}()
rpmOption.Add = tt.addValue
rpmOption.Package = tt.packageValue
err := rpmCmd.PreRunE(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 TestRpmCmdRun(t *testing.T) {
originalArgs := rpmOption.Args
originalOptions := rpmOption.Options
defer func() {
rpmOption.Args = originalArgs
rpmOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*build.RpmOptions).Build, func(o *build.RpmOptions) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
rpmCmd.Run(cmd, args)
assert.NotEqual(t, args, rpmOption.Args)
assert.Equal(t, options, rpmOption.Options)
}
func TestPreCheckCmdPreRunE(t *testing.T) {
tests := []struct {
name string
fileValue string
expectError bool
errorContains string
}{
{
name: "valid input with file",
fileValue: "/path/to/file.yaml",
expectError: false,
},
{
name: "missing file should return error",
fileValue: "",
expectError: true,
errorContains: "The parameter `file` is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalFile := preCheckOption.File
defer func() {
preCheckOption.File = originalFile
}()
preCheckOption.File = tt.fileValue
err := preCheckCmd.PreRunE(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 TestPreCheckCmdRun(t *testing.T) {
originalArgs := preCheckOption.Args
originalOptions := preCheckOption.Options
defer func() {
preCheckOption.Args = originalArgs
preCheckOption.Options = originalOptions
}()
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc((*build.PreCheckOptions).PreCheck, func(o *build.PreCheckOptions) {
})
cmd := &cobra.Command{}
args := []string{"arg1", "arg2"}
preCheckCmd.Run(cmd, args)
assert.NotEqual(t, args, preCheckOption.Args)
assert.Equal(t, options, preCheckOption.Options)
}