Skip to content

Commit

Permalink
added call back function to mockClient
Browse files Browse the repository at this point in the history
  • Loading branch information
smritidahal653 committed Nov 2, 2023
1 parent 46e50da commit e69e4dd
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 92 deletions.
67 changes: 28 additions & 39 deletions pkg/controllers/workspace_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"knative.dev/pkg/apis"
)

Expand Down Expand Up @@ -220,66 +221,54 @@ func TestSelectWorkspaceNodes(t *testing.T) {
}

func TestCreateAndValidateNode(t *testing.T) {
mockClient := utils.NewClient()

mockMachine := utils.MockMachine

testcases := map[string]struct {
callMocks func(c *utils.Client)
expectedError error
callMocks func(c *utils.MockClient)
machineConditions apis.Conditions
expectedError error
}{
"Node is not created because machine creation fails": {
callMocks: func(c *utils.Client) {
//create machine call should not return any errors
callMocks: func(c *utils.MockClient) {
c.On("Create", mock.IsType(context.Background()), mock.IsType(&v1alpha5.Machine{}), mock.Anything).Return(nil)

machineConditions := apis.Conditions{
{
Type: v1alpha5.MachineLaunched,
Status: corev1.ConditionFalse,
Message: machine.ErrorInstanceTypesUnavailable,
},
}

mockMachine.Status.Conditions = machineConditions

//insert mock machine in map so mock Get call retrieves this object
c.InsertObjectInMap(&mockMachine)

c.On("Get", mock.IsType(context.Background()), mock.Anything, mock.IsType(&v1alpha5.Machine{}), mock.Anything).Return(nil)
c.On("Get", mock.IsType(context.Background()), mock.Anything, mock.IsType(&v1alpha1.Workspace{}), mock.Anything).Return(nil)
c.StatusMock.On("Update", mock.IsType(context.Background()), mock.IsType(&v1alpha1.Workspace{}), mock.Anything).Return(nil)
},
machineConditions: apis.Conditions{
{
Type: v1alpha5.MachineLaunched,
Status: corev1.ConditionFalse,
Message: machine.ErrorInstanceTypesUnavailable,
},
},
expectedError: errors.New(machine.ErrorInstanceTypesUnavailable),
},
"A machine is successfully created": {
callMocks: func(c *utils.Client) {
//create machine call should not return any errors
callMocks: func(c *utils.MockClient) {
c.On("Create", mock.IsType(context.Background()), mock.IsType(&v1alpha5.Machine{}), mock.Anything).Return(nil)

machineConditions := apis.Conditions{
{
Type: apis.ConditionReady,
Status: corev1.ConditionTrue,
},
}

mockMachine.Status.Conditions = machineConditions

//insert mock machine in map so mock Get call retrieves this object
c.InsertObjectInMap(&mockMachine)

//get machine call should not return any errors
c.On("Get", mock.IsType(context.Background()), mock.Anything, mock.IsType(&v1alpha5.Machine{}), mock.Anything).Return(nil)
//get machine call should not return any errors
c.On("Get", mock.IsType(context.Background()), mock.Anything, mock.IsType(&corev1.Node{}), mock.Anything).Return(nil)
},
machineConditions: apis.Conditions{
{
Type: apis.ConditionReady,
Status: corev1.ConditionTrue,
},
},
expectedError: nil,
},
}

for k, tc := range testcases {
t.Run(k, func(t *testing.T) {
mockClient := utils.NewClient()
mockMachine := &v1alpha5.Machine{}

mockClient.UpdateCb = func(key types.NamespacedName) {
mockClient.GetObjectFromMap(mockMachine, key)
mockMachine.Status.Conditions = tc.machineConditions
mockClient.CreateOrUpdateObjectInMap(mockMachine)
}

tc.callMocks(mockClient)

reconciler := &WorkspaceReconciler{
Expand Down
112 changes: 59 additions & 53 deletions pkg/utils/mockClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,134 +16,140 @@ import (
)

// Client is a mock for the controller-runtime dynamic client interface.
type Client struct {
type MockClient struct {
mock.Mock

ObjectMap map[reflect.Type]map[k8sClient.ObjectKey]k8sClient.Object
StatusMock *StatusClient
StatusMock *MockStatusClient
UpdateCb func(key types.NamespacedName)
}

var _ k8sClient.Client = &Client{}
var _ k8sClient.Client = &MockClient{}

func NewClient() *Client {
return &Client{
StatusMock: &StatusClient{},
func NewClient() *MockClient {
return &MockClient{
StatusMock: &MockStatusClient{},
ObjectMap: map[reflect.Type]map[k8sClient.ObjectKey]k8sClient.Object{},
}
}

// Retrieves or creates a map associated with the type of obj
func (c *Client) ensureMapFor(obj k8sClient.Object) map[k8sClient.ObjectKey]k8sClient.Object {
func (m *MockClient) ensureMapFor(obj k8sClient.Object) map[k8sClient.ObjectKey]k8sClient.Object {
t := reflect.TypeOf(obj)
if _, ok := c.ObjectMap[t]; !ok {
if _, ok := m.ObjectMap[t]; !ok {
//create a new map with the object key if it doesn't exist
c.ObjectMap[t] = map[k8sClient.ObjectKey]k8sClient.Object{}
m.ObjectMap[t] = map[k8sClient.ObjectKey]k8sClient.Object{}
}
return c.ObjectMap[t]
return m.ObjectMap[t]
}

func (c *Client) InsertObjectInMap(obj k8sClient.Object) {
relevantMap := c.ensureMapFor(obj)
func (m *MockClient) CreateOrUpdateObjectInMap(obj k8sClient.Object) {
relevantMap := m.ensureMapFor(obj)
objKey := k8sClient.ObjectKeyFromObject(obj)

relevantMap[objKey] = obj
}

// StatusClient interface

func (c *Client) Status() k8sClient.StatusWriter {
return c.StatusMock
}
func (m *MockClient) GetObjectFromMap(obj k8sClient.Object, key types.NamespacedName) {
relevantMap := m.ensureMapFor(obj)

// Reader interface

func (c *Client) Get(ctx context.Context, key types.NamespacedName, obj k8sClient.Object, opts ...k8sClient.GetOption) error {
relevantMap := c.ensureMapFor(obj)

for _, val := range relevantMap {
if val, ok := relevantMap[key]; ok {
v := reflect.ValueOf(obj).Elem()
v.Set(reflect.ValueOf(val).Elem())
break
}
}

args := c.Called(ctx, key, obj, opts)
// k8s Client interface
func (m *MockClient) Get(ctx context.Context, key types.NamespacedName, obj k8sClient.Object, opts ...k8sClient.GetOption) error {
//make any necessary changes to the object
m.UpdateCb(key)

m.GetObjectFromMap(obj, key)

args := m.Called(ctx, key, obj, opts)
return args.Error(0)
}

func (c *Client) List(ctx context.Context, list k8sClient.ObjectList, opts ...k8sClient.ListOption) error {
args := c.Called(ctx, list, opts)
func (m *MockClient) List(ctx context.Context, list k8sClient.ObjectList, opts ...k8sClient.ListOption) error {
args := m.Called(ctx, list, opts)
return args.Error(0)
}

// Writer interface
func (m *MockClient) Create(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.CreateOption) error {
m.CreateOrUpdateObjectInMap(obj)

func (c *Client) Create(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.CreateOption) error {
args := c.Called(ctx, obj, opts)
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

func (c *Client) Delete(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.DeleteOption) error {
args := c.Called(ctx, obj, opts)
func (m *MockClient) Delete(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.DeleteOption) error {
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

func (c *Client) Update(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.UpdateOption) error {
args := c.Called(ctx, obj, opts)
func (m *MockClient) Update(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.UpdateOption) error {
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

func (c *Client) Patch(ctx context.Context, obj k8sClient.Object, patch k8sClient.Patch, opts ...k8sClient.PatchOption) error {
args := c.Called(ctx, obj, patch, opts)
func (m *MockClient) Patch(ctx context.Context, obj k8sClient.Object, patch k8sClient.Patch, opts ...k8sClient.PatchOption) error {
args := m.Called(ctx, obj, patch, opts)
return args.Error(0)
}

func (c *Client) DeleteAllOf(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.DeleteAllOfOption) error {
args := c.Called(ctx, obj, opts)
func (m *MockClient) DeleteAllOf(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.DeleteAllOfOption) error {
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

// SubResource implements client.Client
func (*Client) SubResource(subResource string) k8sClient.SubResourceClient {
func (m *MockClient) SubResource(subResource string) k8sClient.SubResourceClient {
panic("unimplemented")
}

// GroupVersionKindFor implements client.Client
func (*Client) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
func (m *MockClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) {
panic("unimplemented")
}

// IsObjectNamespaced implements client.Client
func (*Client) IsObjectNamespaced(obj runtime.Object) (bool, error) {
func (m *MockClient) IsObjectNamespaced(obj runtime.Object) (bool, error) {
panic("unimplemented")
}

func (c *Client) Scheme() *runtime.Scheme {
args := c.Called()
func (m *MockClient) Scheme() *runtime.Scheme {
args := m.Called()
return args.Get(0).(*runtime.Scheme)
}

func (c *Client) RESTMapper() meta.RESTMapper {
args := c.Called()
func (m *MockClient) RESTMapper() meta.RESTMapper {
args := m.Called()
return args.Get(0).(meta.RESTMapper)
}

type StatusClient struct {
// StatusClient interface

func (m *MockClient) Status() k8sClient.StatusWriter {
return m.StatusMock
}

type MockStatusClient struct {
mock.Mock
}

func (c *StatusClient) Create(ctx context.Context, obj k8sClient.Object, subResource k8sClient.Object, opts ...k8sClient.SubResourceCreateOption) error {
args := c.Called(ctx, obj, opts)
func (m *MockStatusClient) Create(ctx context.Context, obj k8sClient.Object, subResource k8sClient.Object, opts ...k8sClient.SubResourceCreateOption) error {
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

func (c *StatusClient) Patch(ctx context.Context, obj k8sClient.Object, patch k8sClient.Patch, opts ...k8sClient.SubResourcePatchOption) error {
args := c.Called(ctx, obj, opts)
func (m *MockStatusClient) Patch(ctx context.Context, obj k8sClient.Object, patch k8sClient.Patch, opts ...k8sClient.SubResourcePatchOption) error {
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

func (c *StatusClient) Update(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.SubResourceUpdateOption) error {
args := c.Called(ctx, obj, opts)
func (m *MockStatusClient) Update(ctx context.Context, obj k8sClient.Object, opts ...k8sClient.SubResourceUpdateOption) error {
args := m.Called(ctx, obj, opts)
return args.Error(0)
}

var _ k8sClient.StatusWriter = &StatusClient{}
var _ k8sClient.StatusWriter = &MockStatusClient{}

0 comments on commit e69e4dd

Please sign in to comment.