Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(virtbmc): add unit tests for virtbmc #29

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/onsi/ginkgo/v2 v2.11.0
github.com/onsi/gomega v1.27.10
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
github.com/urfave/cli/v2 v2.25.7
github.com/vmware/goipmi v0.0.0-20181114221114-2333cd82d702
k8s.io/api v0.28.3
Expand Down Expand Up @@ -50,12 +51,14 @@ require (
github.com/openshift/custom-resource-status v1.1.2 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand Down
323 changes: 323 additions & 0 deletions pkg/virtbmc/k8s_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
package virtbmc

import (
"context"
"testing"

"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
kubevirtv1 "kubevirt.io/api/core/v1"
kubevirttypev1 "kubevirt.io/kubevirtbmc/pkg/generated/clientset/versioned/typed/core/v1"
)

type MockKubevirtClient struct {
mock.Mock
}

func (m *MockKubevirtClient) VirtualMachines(namespace string) kubevirttypev1.VirtualMachineInterface {
args := m.Called(namespace)
return args.Get(0).(kubevirttypev1.VirtualMachineInterface)
}

type MockVirtualMachineInterface struct {
mock.Mock
}

func (m *MockVirtualMachineInterface) Get(
ctx context.Context, name string, options v1.GetOptions,
) (*kubevirtv1.VirtualMachine, error) {
args := m.Called(ctx, name, options)
return args.Get(0).(*kubevirtv1.VirtualMachine), args.Error(1)
}

func (m *MockVirtualMachineInterface) Create(
ctx context.Context, vm *kubevirtv1.VirtualMachine, options v1.CreateOptions,
) (*kubevirtv1.VirtualMachine, error) {
panic("implement me")
}

func (m *MockVirtualMachineInterface) Update(
ctx context.Context, vm *kubevirtv1.VirtualMachine, options v1.UpdateOptions,
) (*kubevirtv1.VirtualMachine, error) {
args := m.Called(ctx, vm, options)
return args.Get(0).(*kubevirtv1.VirtualMachine), args.Error(1)
}

func (m *MockVirtualMachineInterface) Delete(ctx context.Context, name string, options v1.DeleteOptions) error {
panic("implement me")
}

func (m *MockVirtualMachineInterface) DeleteCollection(
ctx context.Context, options v1.DeleteOptions, listOptions v1.ListOptions,
) error {
panic("implement me")
}

func (m *MockVirtualMachineInterface) List(
ctx context.Context, options v1.ListOptions,
) (*kubevirtv1.VirtualMachineList, error) {
panic("implement me")
}

func (m *MockVirtualMachineInterface) Patch(
ctx context.Context,
name string,
pt types.PatchType,
data []byte,
options v1.PatchOptions,
subresources ...string,
) (*kubevirtv1.VirtualMachine, error) {
panic("implement me")
}

func (m *MockVirtualMachineInterface) UpdateStatus(
ctx context.Context, vm *kubevirtv1.VirtualMachine, options v1.UpdateOptions,
) (*kubevirtv1.VirtualMachine, error) {
panic("implement me")
}

func (m *MockVirtualMachineInterface) Watch(ctx context.Context, options v1.ListOptions) (watch.Interface, error) {
panic("implement me")
}

func (m *MockKubevirtClient) VirtualMachineInstances(namespace string) kubevirttypev1.VirtualMachineInstanceInterface {
args := m.Called(namespace)
return args.Get(0).(kubevirttypev1.VirtualMachineInstanceInterface)
}

type MockVirtualMachineInstanceInterface struct {
mock.Mock
}

func (m *MockVirtualMachineInstanceInterface) Get(
ctx context.Context, name string, options v1.GetOptions,
) (*kubevirtv1.VirtualMachineInstance, error) {
panic("implement me")
}

func (m *MockVirtualMachineInstanceInterface) Create(
ctx context.Context, vmi *kubevirtv1.VirtualMachineInstance, options v1.CreateOptions,
) (*kubevirtv1.VirtualMachineInstance, error) {
panic("implement me")
}

func (m *MockVirtualMachineInstanceInterface) Update(
ctx context.Context, vmi *kubevirtv1.VirtualMachineInstance, options v1.UpdateOptions,
) (*kubevirtv1.VirtualMachineInstance, error) {
args := m.Called(ctx, vmi, options)
return args.Get(0).(*kubevirtv1.VirtualMachineInstance), args.Error(1)
}

func (m *MockVirtualMachineInstanceInterface) Delete(ctx context.Context, name string, options v1.DeleteOptions) error {
args := m.Called(ctx, name, options)
return args.Error(0)
}

func (m *MockVirtualMachineInstanceInterface) DeleteCollection(
ctx context.Context, options v1.DeleteOptions, listOptions v1.ListOptions,
) error {
panic("implement me")
}

func (m *MockVirtualMachineInstanceInterface) List(
ctx context.Context, options v1.ListOptions,
) (*kubevirtv1.VirtualMachineInstanceList, error) {
panic("implement me")
}

func (m *MockVirtualMachineInstanceInterface) Patch(
ctx context.Context,
name string,
pt types.PatchType,
data []byte,
options v1.PatchOptions,
subresources ...string,
) (*kubevirtv1.VirtualMachineInstance, error) {
panic("implement me")
}

func (m *MockVirtualMachineInstanceInterface) UpdateStatus(
ctx context.Context, vm *kubevirtv1.VirtualMachineInstance, options v1.UpdateOptions,
) (*kubevirtv1.VirtualMachineInstance, error) {
panic("implement me")
}

func (m *MockVirtualMachineInstanceInterface) Watch(
ctx context.Context, options v1.ListOptions,
) (watch.Interface, error) {
panic("implement me")
}

func TestGetVirtualMachinePowerStatus(t *testing.T) {
mockClient := new(MockKubevirtClient)
mockVMInterface := new(MockVirtualMachineInterface)
mockClient.On("VirtualMachines", "default").Return(mockVMInterface)

mockVM := &kubevirtv1.VirtualMachine{
Status: kubevirtv1.VirtualMachineStatus{
Ready: true,
},
}
mockVMInterface.On("Get", mock.Anything, "test-vm", mock.Anything).Return(mockVM, nil)

bmc := &VirtBMC{
context: context.TODO(),
address: "127.0.0.1",
port: 623,
vmNamespace: "default",
vmName: "test-vm",
kvClient: mockClient,
}

// Test getVirtualMachinePowerStatus
status, err := bmc.getVirtualMachinePowerStatus()
require.NoError(t, err)
require.True(t, status)

// Add another test case where the VM is not ready
mockVM.Status.Ready = false
mockVMInterface.On("Get", mock.Anything, "test-vm", mock.Anything).Return(mockVM, nil)

status, err = bmc.getVirtualMachinePowerStatus()
require.NoError(t, err)
require.False(t, status)
}

// Helper function to create a fake VM and set up common mock expectations
func setupVMTest(running bool) (*VirtBMC, *MockVirtualMachineInterface, *kubevirtv1.VirtualMachine) {
mockClient := new(MockKubevirtClient)
mockVMInterface := new(MockVirtualMachineInterface)
mockClient.On("VirtualMachines", "default").Return(mockVMInterface)

fakeVM := &kubevirtv1.VirtualMachine{
Spec: kubevirtv1.VirtualMachineSpec{
Running: func(b bool) *bool { return &b }(running),
},
}
mockVMInterface.On("Get", mock.Anything, "test-vm", mock.Anything).Return(fakeVM, nil)
mockVMInterface.On("Update", mock.Anything, fakeVM, mock.Anything).Return(fakeVM, nil)

bmc := &VirtBMC{
context: context.TODO(),
address: "127.0.0.1",
port: 623,
vmNamespace: "default",
vmName: "test-vm",
kvClient: mockClient,
}

return bmc, mockVMInterface, fakeVM
}

func TestStopVirtualMachine(t *testing.T) {
bmc, mockVMInterface, fakeVM := setupVMTest(true)

// Test stopVirtualMachine
err := bmc.stopVirtualMachine()
require.NoError(t, err)

// Assertion
mockVMInterface.AssertCalled(t, "Get", mock.Anything, "test-vm", mock.Anything)
mockVMInterface.AssertCalled(t, "Update", mock.Anything, fakeVM, mock.Anything)

require.NotNil(t, fakeVM.Spec.Running)
require.False(t, *fakeVM.Spec.Running)
}

func TestStartVirtualMachine(t *testing.T) {
bmc, mockVMInterface, fakeVM := setupVMTest(false)

// Test startVirtualMachine
err := bmc.startVirtualMachine()
require.NoError(t, err)

// Assertion
mockVMInterface.AssertCalled(t, "Get", mock.Anything, "test-vm", mock.Anything)
mockVMInterface.AssertCalled(t, "Update", mock.Anything, fakeVM, mock.Anything)

require.NotNil(t, fakeVM.Spec.Running)
require.True(t, *fakeVM.Spec.Running)
}

func TestRebootVirtualMachine(t *testing.T) {
mockClient := new(MockKubevirtClient)
mockVMIInterface := new(MockVirtualMachineInstanceInterface)
mockClient.On("VirtualMachineInstances", "default").Return(mockVMIInterface)

mockVMIInterface.On("Delete", mock.Anything, "test-vm", mock.Anything).Return(nil)

bmc := &VirtBMC{
context: context.TODO(),
address: "127.0.0.1",
port: 623,
vmNamespace: "default",
vmName: "test-vm",
kvClient: mockClient,
}

// Test rebootVirtualMachine
err := bmc.rebootVirtualMachine()
require.NoError(t, err)

// Assertion
mockVMIInterface.AssertCalled(t, "Delete", mock.Anything, "test-vm", mock.Anything)
}

func TestSetVirtualMachineBootDevice(t *testing.T) {
mockClient := new(MockKubevirtClient)
mockVMInterface := new(MockVirtualMachineInterface)
mockClient.On("VirtualMachines", "default").Return(mockVMInterface)

fakeVM := &kubevirtv1.VirtualMachine{
Spec: kubevirtv1.VirtualMachineSpec{
Template: &kubevirtv1.VirtualMachineInstanceTemplateSpec{
Spec: kubevirtv1.VirtualMachineInstanceSpec{
Domain: kubevirtv1.DomainSpec{
Devices: kubevirtv1.Devices{
Disks: []kubevirtv1.Disk{
{
Name: "disk1",
BootOrder: new(uint),
},
},
Interfaces: []kubevirtv1.Interface{
{
Name: "net1",
BootOrder: new(uint),
},
},
},
},
},
},
},
}
mockVMInterface.On("Get", mock.Anything, "test-vm", mock.Anything).Return(fakeVM, nil)
mockVMInterface.On("Update", mock.Anything, fakeVM, mock.Anything).Return(fakeVM, nil)

bmc := &VirtBMC{
context: context.TODO(),
address: "127.0.0.1",
port: 623,
vmNamespace: "default",
vmName: "test-vm",
kvClient: mockClient,
}

// Test PXE boot device
err := bmc.setVirtualMachineBootDevice(Pxe)
require.NoError(t, err)

require.Equal(t, uint(1), *fakeVM.Spec.Template.Spec.Domain.Devices.Interfaces[0].BootOrder)

// Test Disk boot device
err = bmc.setVirtualMachineBootDevice(Disk)
require.NoError(t, err)

require.Equal(t, uint(1), *fakeVM.Spec.Template.Spec.Domain.Devices.Disks[0].BootOrder)

mockVMInterface.AssertCalled(t, "Update", mock.Anything, fakeVM, mock.Anything)
}
7 changes: 6 additions & 1 deletion pkg/virtbmc/virtbmc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ type Options struct {
Port int
}

type KubeVirtClientInterface interface {
VirtualMachines(namespace string) kubevirtv1.VirtualMachineInterface
VirtualMachineInstances(namespace string) kubevirtv1.VirtualMachineInstanceInterface
}

type VirtBMC struct {
context context.Context
address string
port int
vmNamespace string
vmName string
kvClient *kubevirtv1.KubevirtV1Client
kvClient KubeVirtClientInterface
sim *ipmi.Simulator
}

Expand Down
Loading