*
* 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 server
import (
"os"
"testing"
"github.com/agiledragon/gomonkey/v2"
"github.com/shirou/gopsutil/v3/process"
"github.com/stretchr/testify/assert"
"gopkg.openfuyao.cn/cluster-api-provider-bke/common/ntp"
"gopkg.openfuyao.cn/bkeadm/pkg/global"
"gopkg.openfuyao.cn/bkeadm/utils"
)
const (
testNTPServerName = "ntpserver"
testNTPServerAddress = "pool.ntp.org"
testLogFilePath = "/tmp/ntpserver.log"
testDaemonMaxCount = 5
)
func TestTryConnectNTPServerSuccess(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(ntp.Date, func(server string) error {
return nil
})
result := TryConnectNTPServer(testNTPServerAddress)
assert.True(t, result)
}
func TestTryConnectNTPServerFailure(t *testing.T) {
callCount := 0
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(ntp.Date, func(server string) error {
callCount++
return assert.AnError
})
result := TryConnectNTPServer(testNTPServerAddress)
assert.False(t, result)
assert.Equal(t, testDaemonMaxCount, callCount)
}
func TestRemoveNTPServerLogFileRemoval(t *testing.T) {
removeCalled := false
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(utils.Exists, func(path string) bool {
return false
})
patches.ApplyFunc(process.Pids, func() ([]int32, error) {
return []int32{}, nil
})
patches.ApplyFunc(os.Remove, func(path string) error {
if path == testLogFilePath {
removeCalled = true
}
return nil
})
err := RemoveNTPServer()
assert.NoError(t, err)
assert.True(t, removeCalled)
}
func TestRemoveNTPServerProcessCleanup(t *testing.T) {
tests := []struct {
name string
mockPids func() ([]int32, error)
mockNewProcess func(int32) (*process.Process, error)
mockCmdline func(*process.Process) (string, error)
expectCleanup bool
}{
{
name: "no matching process",
mockPids: func() ([]int32, error) {
return []int32{9999}, nil
},
mockNewProcess: func(pid int32) (*process.Process, error) {
return &process.Process{Pid: pid}, nil
},
mockCmdline: func(p *process.Process) (string, error) {
return "some other process", nil
},
expectCleanup: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(utils.Exists, func(path string) bool {
return false
})
patches.ApplyFunc(process.Pids, tt.mockPids)
if tt.mockNewProcess != nil {
patches.ApplyFunc(process.NewProcess, tt.mockNewProcess)
}
if tt.mockCmdline != nil {
patches.ApplyFunc((*process.Process).Cmdline, tt.mockCmdline)
}
err := RemoveNTPServer()
assert.NoError(t, err)
})
}
}
func TestSystemdNTPServer(t *testing.T) {
tests := []struct {
name string
mockGetenv func(string) string
mockExecPath func() (string, error)
mockWriteFile func(string, []byte, os.FileMode) error
mockExecuteCommand func(string, ...string) error
expectError bool
}{
{
name: "successful systemd setup",
mockGetenv: func(key string) string {
if key == "bke" {
return "/usr/local/bin/bke"
}
return ""
},
mockExecPath: func() (string, error) {
return "/usr/local/bin/bke", nil
},
mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
return nil
},
mockExecuteCommand: func(command string, args ...string) error {
return nil
},
expectError: false,
},
{
name: "getenv fails falls back to exec path",
mockGetenv: func(key string) string {
return ""
},
mockExecPath: func() (string, error) {
return "/usr/local/bin/bke", nil
},
mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
return nil
},
mockExecuteCommand: func(command string, args ...string) error {
return nil
},
expectError: false,
},
{
name: "exec path fails",
mockGetenv: func(key string) string {
return ""
},
mockExecPath: func() (string, error) {
return "", assert.AnError
},
expectError: true,
},
{
name: "write file fails",
mockGetenv: func(key string) string {
return ""
},
mockExecPath: func() (string, error) {
return "/usr/local/bin/bke", nil
},
mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
return assert.AnError
},
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.mockGetenv != nil {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(os.Getenv, tt.mockGetenv)
}
if tt.mockExecPath != nil {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(utils.ExecPath, tt.mockExecPath)
}
if tt.mockWriteFile != nil {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(os.WriteFile, tt.mockWriteFile)
}
if tt.mockExecuteCommand != nil {
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(global.Command.ExecuteCommand, tt.mockExecuteCommand)
}
SystemdNTPServer()
})
}
}
func TestSystemdNTPServerCommandExecution(t *testing.T) {
enableCalled := false
daemonReloadCalled := false
startCalled := false
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(os.Getenv, func(key string) string {
return ""
})
patches.ApplyFunc(utils.ExecPath, func() (string, error) {
return "/usr/local/bin/bke", nil
})
patches.ApplyFunc(os.WriteFile, func(name string, data []byte, perm os.FileMode) error {
return nil
})
patches.ApplyFunc(global.Command.ExecuteCommand, func(command string, args ...string) error {
if command == "sh" && len(args) > 0 {
if args[0] == "systemctl enable ntpserver.service" {
enableCalled = true
}
if args[0] == "systemctl daemon-reload" {
daemonReloadCalled = true
}
if args[0] == "systemctl start ntpserver.service" {
startCalled = true
}
}
return nil
})
SystemdNTPServer()
assert.False(t, enableCalled)
assert.False(t, daemonReloadCalled)
assert.False(t, startCalled)
}