From 0ab00b7b5dee60042bf6a48d459cb1d0665fa6c3 Mon Sep 17 00:00:00 2001
From: carmal891 <carmal911@gmail.com>
Date: Wed, 13 Nov 2024 11:32:24 +0530
Subject: [PATCH] UT for ReconcileCOSInstance

---
 cloud/scope/powervs_cluster_test.go | 702 ++++++++++++++++++++++++++++
 1 file changed, 702 insertions(+)

diff --git a/cloud/scope/powervs_cluster_test.go b/cloud/scope/powervs_cluster_test.go
index ad3318705..fdb0dc523 100644
--- a/cloud/scope/powervs_cluster_test.go
+++ b/cloud/scope/powervs_cluster_test.go
@@ -19,10 +19,12 @@ package scope
 import (
 	"errors"
 	"fmt"
+	"os"
 	"testing"
 
 	"github.com/IBM-Cloud/power-go-client/power/models"
 	"github.com/IBM/go-sdk-core/v5/core"
+	"github.com/IBM/ibm-cos-sdk-go/aws/awserr"
 	"github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"
 	"github.com/IBM/vpc-go-sdk/vpcv1"
 	"go.uber.org/mock/gomock"
@@ -36,6 +38,7 @@ import (
 	capiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
 
 	"sigs.k8s.io/cluster-api-provider-ibmcloud/cmd/capibmadm/utils"
+	mockcos "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/cos/mock"
 	"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/powervs"
 	"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/resourcecontroller"
 	mockRC "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/resourcecontroller/mock"
@@ -45,6 +48,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/vpc"
 	"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/vpc/mock"
 
+	"github.com/IBM/ibm-cos-sdk-go/service/s3"
 	. "github.com/onsi/gomega"
 )
 
@@ -4628,3 +4632,701 @@ func TestDeleteTransitGatewayConnections(t *testing.T) {
 		g.Expect(requeue).To(BeFalse())
 	})
 }
+func TestReconcileCOSInstance(t *testing.T) {
+	var (
+		mockResourceController *mockRC.MockResourceController
+		mockCtrl               *gomock.Controller
+	)
+	setup := func(t *testing.T) {
+		t.Helper()
+		mockCtrl = gomock.NewController(t)
+		mockResourceController = mockRC.NewMockResourceController(mockCtrl)
+	}
+	teardown := func() {
+		mockCtrl.Finish()
+	}
+
+	t.Run("When fetch for COS service instance fails", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+		err := os.Setenv("IBMCLOUD_APIKEY", "test-api-key")
+		g.Expect(err).To(BeNil())
+		defer os.Unsetenv("IBMCLOUD_APIKEY")
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{
+						BucketRegion: "test-region",
+					},
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error fetching instance by name"))
+
+		err = clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance).To(BeNil())
+	})
+
+	t.Run("When COS service instance is found in IBM Cloud and cluster status is updated", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+		err := os.Setenv("IBMCLOUD_APIKEY", "test-api-key")
+		g.Expect(err).To(BeNil())
+		defer os.Unsetenv("IBMCLOUD_APIKEY")
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{
+						BucketRegion: "test-region",
+					},
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resource-group-id"),
+					},
+					Zone: ptr.To("test-zone"),
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			Name:  ptr.To("test-cos-resource-name"),
+			State: ptr.To(string(infrav1beta2.ServiceInstanceStateActive)),
+			GUID:  ptr.To("test-cos-instance-guid"),
+		}, nil)
+
+		err = clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ID).To(Equal(ptr.To("test-cos-instance-guid")))
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ControllerCreated).To(Equal(ptr.To(bool(false))))
+	})
+
+	t.Run("When COS service instance is not found in IBM Cloud and hence creates COS instance in cloud", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+		err := os.Setenv("IBMCLOUD_APIKEY", "test-api-key")
+		g.Expect(err).To(BeNil())
+		defer os.Unsetenv("IBMCLOUD_APIKEY")
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{
+						BucketRegion: "test-region",
+					},
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resource-group-id"),
+					},
+					Zone: ptr.To("test-zone"),
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
+		mockResourceController.EXPECT().CreateResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			ID:   ptr.To("test-resource-instance-id"),
+			GUID: ptr.To("test-resource-instance-guid"),
+			Name: ptr.To("test-resource-instance-name"),
+		}, nil, nil)
+
+		err = clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ID).To(Equal(ptr.To("test-resource-instance-guid")))
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ControllerCreated).To(Equal(ptr.To(bool(true))))
+	})
+
+	t.Run("When COS service instance is not found in IBM Cloud and hence creates COS instance in cloud but fails during the creation", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+		err := os.Setenv("IBMCLOUD_APIKEY", "test-api-key")
+		g.Expect(err).To(BeNil())
+		defer os.Unsetenv("IBMCLOUD_APIKEY")
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{
+						BucketRegion: "test-region",
+					},
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resource-group-id"),
+					},
+					Zone: ptr.To("test-zone"),
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
+		mockResourceController.EXPECT().CreateResourceInstance(gomock.Any()).Return(nil, nil, errors.New("failed to create COS service instance"))
+
+		err = clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance).To(BeNil())
+	})
+
+	t.Run("When fetch for API_KEY fails", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{
+						BucketRegion: "test-region",
+					},
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			Name:  ptr.To("test-cos-resource-name"),
+			State: ptr.To(string(infrav1beta2.ServiceInstanceStateActive)),
+			GUID:  ptr.To("test-cos-instance-guid"),
+		}, nil)
+
+		err := clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ID).To(Equal(ptr.To("test-cos-instance-guid")))
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ControllerCreated).To(Equal(ptr.To(bool(false))))
+	})
+
+	t.Run("When COS bucket region is failed to be determined", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+		err := os.Setenv("IBMCLOUD_APIKEY", "test-api-key")
+		g.Expect(err).To(BeNil())
+		defer os.Unsetenv("IBMCLOUD_APIKEY")
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{},
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resource-group-id"),
+					},
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
+
+		mockResourceController.EXPECT().CreateResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			ID:   ptr.To("test-resource-instance-id"),
+			GUID: ptr.To("test-resource-instance-guid"),
+			Name: ptr.To("test-resource-instance-name"),
+		}, nil, nil)
+
+		err = clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ID).To(Equal(ptr.To("test-resource-instance-guid")))
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ControllerCreated).To(Equal(ptr.To(bool(true))))
+	})
+
+	t.Run("When checkCOSBucket fails to determine whether bucket exist in cloud due to an unexpected error", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+		err := os.Setenv("IBMCLOUD_APIKEY", "test-api-key")
+		g.Expect(err).To(BeNil())
+		defer os.Unsetenv("IBMCLOUD_APIKEY")
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					CosInstance: &infrav1beta2.CosInstance{
+						BucketRegion: "test-bucket-region",
+					},
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resource-group-id"),
+					},
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
+
+		mockResourceController.EXPECT().CreateResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			ID:   ptr.To("test-resource-instance-id"),
+			GUID: ptr.To("test-resource-instance-guid"),
+			Name: ptr.To("test-resource-instance-name"),
+		}, nil, nil)
+
+		err = clusterScope.ReconcileCOSInstance()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ID).To(Equal(ptr.To("test-resource-instance-guid")))
+		g.Expect(clusterScope.IBMPowerVSCluster.Status.COSInstance.ControllerCreated).To(Equal(ptr.To(bool(true))))
+	})
+
+	//TODO: Complete cases to cover control flow on checkCOSBucket and createCOSBucket
+	// Github issue: https://github.com/kubernetes-sigs/cluster-api-provider-ibmcloud/issues/2034
+}
+
+func TestCheckCOSServiceInstance(t *testing.T) {
+	var (
+		mockResourceController *mockRC.MockResourceController
+		mockCtrl               *gomock.Controller
+	)
+	setup := func(t *testing.T) {
+		t.Helper()
+		mockCtrl = gomock.NewController(t)
+		mockResourceController = mockRC.NewMockResourceController(mockCtrl)
+	}
+	teardown := func() {
+		mockCtrl.Finish()
+	}
+
+	t.Run("When fetching of cos service instance returns error", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error listing COS instances"))
+
+		cosResourceInstance, err := clusterScope.checkCOSServiceInstance()
+		g.Expect(cosResourceInstance).To(BeNil())
+		g.Expect(err).ToNot(BeNil())
+	})
+
+	t.Run("When COS service instance is not found in IBM Cloud", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil)
+
+		cosResourceInstance, err := clusterScope.checkCOSServiceInstance()
+		g.Expect(cosResourceInstance).To(BeNil())
+		g.Expect(err).To(BeNil())
+	})
+
+	t.Run("When COS service instance exists but state is not active", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			Name:  ptr.To("test-cos-resource-name"),
+			State: ptr.To("failed"),
+		}, nil)
+
+		cosResourceInstance, err := clusterScope.checkCOSServiceInstance()
+		g.Expect(cosResourceInstance).To(BeNil())
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(err).To(MatchError(fmt.Errorf("COS service instance is not in active state, current state: %s", "failed")))
+	})
+	t.Run("When COS service instance exists and state is active", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().GetInstanceByName(gomock.Any(), gomock.Any(), gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			Name:  ptr.To("test-cos-resource-name"),
+			State: ptr.To(string(infrav1beta2.ServiceInstanceStateActive)),
+		}, nil)
+
+		cosResourceInstance, err := clusterScope.checkCOSServiceInstance()
+		g.Expect(cosResourceInstance.Name).To(Equal(ptr.To("test-cos-resource-name")))
+		g.Expect(cosResourceInstance.State).To(Equal(ptr.To(string(infrav1beta2.ServiceInstanceStateActive))))
+		g.Expect(err).To(BeNil())
+	})
+}
+
+func TestCreateCOSBucket(t *testing.T) {
+	var (
+		mockResourceController *mockRC.MockResourceController
+		mockCOSController      *mockcos.MockCos
+		mockCtrl               *gomock.Controller
+	)
+	setup := func(t *testing.T) {
+		t.Helper()
+		mockCtrl = gomock.NewController(t)
+		mockResourceController = mockRC.NewMockResourceController(mockCtrl)
+		mockCOSController = mockcos.NewMockCos(mockCtrl)
+	}
+	teardown := func() {
+		mockCtrl.Finish()
+	}
+
+	t.Run("When COS bucket creation fails in cloud", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			COSClient:      mockCOSController,
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+		mockCOSController.EXPECT().CreateBucket(gomock.Any()).Return(nil, fmt.Errorf("failed to create COS bucket"))
+		err := clusterScope.createCOSBucket()
+		g.Expect(err).ToNot(BeNil())
+	})
+
+	t.Run("When COS bucket already exists and is owned by the user", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			COSClient:      mockCOSController,
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockCOSController.EXPECT().CreateBucket(gomock.Any()).Return(nil, awserr.New(s3.ErrCodeBucketAlreadyOwnedByYou, "Bucket already owned by user", nil))
+
+		err := clusterScope.createCOSBucket()
+		g.Expect(err).To(BeNil())
+	})
+
+	t.Run("When COS bucket already exists but is owned by someone else", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			COSClient:      mockCOSController,
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+		mockCOSController.EXPECT().CreateBucket(gomock.Any()).Return(nil, awserr.New(s3.ErrCodeBucketAlreadyExists, "Bucket already exists", nil))
+		err := clusterScope.createCOSBucket()
+		g.Expect(err).To(BeNil())
+	})
+
+	t.Run("When an unexpected error occurs during COS bucket creation", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			COSClient:      mockCOSController,
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockCOSController.EXPECT().CreateBucket(gomock.Any()).Return(nil, awserr.New("UnexpectedError", "An unexpected error occurred", nil))
+
+		err := clusterScope.createCOSBucket()
+		g.Expect(err).ToNot(BeNil())
+		g.Expect(err.Error()).To(ContainSubstring("failed to create COS bucket"))
+	})
+
+	t.Run("When COS bucket is created successfully", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			COSClient:      mockCOSController,
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockCOSController.EXPECT().CreateBucket(gomock.Any()).Return(&s3.CreateBucketOutput{}, nil)
+
+		err := clusterScope.createCOSBucket()
+		g.Expect(err).To(BeNil())
+	})
+}
+
+func TestCheckCOSBucket(t *testing.T) {
+	var (
+		mockResourceController *mockRC.MockResourceController
+		mockCOSController      *mockcos.MockCos
+		mockCtrl               *gomock.Controller
+	)
+	setup := func(t *testing.T) {
+		t.Helper()
+		mockCtrl = gomock.NewController(t)
+		mockResourceController = mockRC.NewMockResourceController(mockCtrl)
+		mockCOSController = mockcos.NewMockCos(mockCtrl)
+	}
+	teardown := func() {
+		mockCtrl.Finish()
+	}
+	t.Run("When checking if COS bucket exists in cloud", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			COSClient:      mockCOSController,
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		testScenarios := []struct {
+			name         string
+			mockError    error
+			bucketExists bool
+			expectErr    bool
+		}{
+			{
+				name:         "NoSuchBucket error",
+				mockError:    awserr.New(s3.ErrCodeNoSuchBucket, "NoSuchBucket", nil),
+				bucketExists: false,
+				expectErr:    false,
+			},
+			{
+				name:         "Forbidden error",
+				mockError:    awserr.New("Forbidden", "Forbidden", nil),
+				bucketExists: false,
+				expectErr:    false,
+			},
+			{
+				name:         "NotFound error",
+				mockError:    awserr.New("NotFound", "NotFound", nil),
+				bucketExists: false,
+				expectErr:    false,
+			},
+			{
+				name:         "Other aws error",
+				mockError:    awserr.New("OtherAWSError", "OtherAWSError", nil),
+				bucketExists: false,
+				expectErr:    true,
+			},
+			{
+				name:         "Bucket exists",
+				mockError:    nil,
+				bucketExists: true,
+				expectErr:    false,
+			},
+		}
+
+		for _, scenario := range testScenarios {
+			t.Run(scenario.name, func(_ *testing.T) {
+				mockCOSController.EXPECT().GetBucketByName(gomock.Any()).Return(nil, scenario.mockError)
+				exists, err := clusterScope.checkCOSBucket()
+				g.Expect(exists).To(Equal(scenario.bucketExists))
+				if scenario.expectErr {
+					g.Expect(err).ToNot(BeNil())
+				} else {
+					g.Expect(err).To(BeNil())
+				}
+			})
+		}
+	})
+}
+
+func TestCreateCOSServiceInstance(t *testing.T) {
+	var (
+		mockResourceController *mockRC.MockResourceController
+		mockCtrl               *gomock.Controller
+	)
+	setup := func(t *testing.T) {
+		t.Helper()
+		mockCtrl = gomock.NewController(t)
+		mockResourceController = mockRC.NewMockResourceController(mockCtrl)
+	}
+	teardown := func() {
+		mockCtrl.Finish()
+	}
+
+	t.Run("When creating COS resource instance fails due to missing resource group id", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		cosResourceInstance, err := clusterScope.createCOSServiceInstance()
+		g.Expect(cosResourceInstance).To(BeNil())
+		g.Expect(err).ToNot(BeNil())
+	})
+
+	t.Run("When creating COS resource instance fails", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resourcegroup-id"),
+					},
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().CreateResourceInstance(gomock.Any()).Return(nil, nil, fmt.Errorf("error creating resource instance"))
+
+		cosResourceInstance, err := clusterScope.createCOSServiceInstance()
+		g.Expect(cosResourceInstance).To(BeNil())
+		g.Expect(err).ToNot(BeNil())
+	})
+
+	t.Run("When COS resource instance creation is successful", func(t *testing.T) {
+		g := NewWithT(t)
+		setup(t)
+		t.Cleanup(teardown)
+
+		clusterScope := PowerVSClusterScope{
+			ResourceClient: mockResourceController,
+			IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{
+				Spec: infrav1beta2.IBMPowerVSClusterSpec{
+					ResourceGroup: &infrav1beta2.IBMPowerVSResourceReference{
+						ID: ptr.To("test-resourcegroup-id"),
+					},
+				},
+				Status: infrav1beta2.IBMPowerVSClusterStatus{
+					ServiceInstance: &infrav1beta2.ResourceReference{
+						ID: ptr.To("test-serviceinstance-id"),
+					},
+				},
+			},
+		}
+
+		mockResourceController.EXPECT().CreateResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{
+			ID: ptr.To("new-resource-instance-id"),
+		}, nil, nil)
+
+		cosResourceInstance, err := clusterScope.createCOSServiceInstance()
+		g.Expect(err).To(BeNil())
+		g.Expect(cosResourceInstance.ID).To(Equal(ptr.To("new-resource-instance-id")))
+	})
+}