github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/clusters/multiclusterapplicationconfiguration/controller_test.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package multiclusterapplicationconfiguration 5 6 import ( 7 "context" 8 "encoding/json" 9 "path/filepath" 10 "testing" 11 12 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" 13 "github.com/go-logr/logr" 14 "github.com/golang/mock/gomock" 15 asserts "github.com/stretchr/testify/assert" 16 clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 17 "github.com/verrazzano/verrazzano/application-operator/constants" 18 "github.com/verrazzano/verrazzano/application-operator/controllers/clusters" 19 clusterstest "github.com/verrazzano/verrazzano/application-operator/controllers/clusters/test" 20 "github.com/verrazzano/verrazzano/application-operator/mocks" 21 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 22 "go.uber.org/zap" 23 v1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/runtime/schema" 28 "k8s.io/apimachinery/pkg/types" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 ) 31 32 const namespace = "unit-mcappconfig-namespace" 33 const crName = "unit-mcappconfig" 34 35 // TestAppConfigReconcilerSetupWithManager test the creation of the MultiCluster app config Reconciler. 36 // GIVEN a controller implementation 37 // WHEN the controller is created 38 // THEN verify no error is returned 39 func TestAppConfigReconcilerSetupWithManager(t *testing.T) { 40 assert := asserts.New(t) 41 42 var mocker *gomock.Controller 43 var mgr *mocks.MockManager 44 var cli *mocks.MockClient 45 var scheme *runtime.Scheme 46 var reconciler Reconciler 47 var err error 48 49 mocker = gomock.NewController(t) 50 mgr = mocks.NewMockManager(mocker) 51 cli = mocks.NewMockClient(mocker) 52 scheme = runtime.NewScheme() 53 _ = clustersv1alpha1.AddToScheme(scheme) 54 reconciler = Reconciler{Client: cli, Scheme: scheme} 55 mgr.EXPECT().GetControllerOptions().AnyTimes() 56 mgr.EXPECT().GetScheme().Return(scheme) 57 mgr.EXPECT().GetLogger().Return(logr.Discard()) 58 mgr.EXPECT().SetFields(gomock.Any()).Return(nil).AnyTimes() 59 mgr.EXPECT().Add(gomock.Any()).Return(nil).AnyTimes() 60 err = reconciler.SetupWithManager(mgr) 61 mocker.Finish() 62 assert.NoError(err) 63 } 64 65 // TestReconcileCreateAppConfig tests the basic happy path of reconciling a 66 // MultiClusterApplicationConfiguration. We expect to write out an OAM app config 67 // GIVEN a MultiClusterApplicationConfiguration resource is created 68 // WHEN the controller Reconcile function is called 69 // THEN expect an OAM app config to be created 70 func TestReconcileCreateAppConfig(t *testing.T) { 71 assert := asserts.New(t) 72 73 mocker := gomock.NewController(t) 74 cli := mocks.NewMockClient(mocker) 75 mockStatusWriter := mocks.NewMockStatusWriter(mocker) 76 77 mcAppConfigSample, err := getSampleMCAppConfig() 78 79 if err != nil { 80 t.Fatalf(err.Error()) 81 } 82 83 // expect a call to fetch the MultiClusterApplicationConfiguration 84 doExpectGetMultiClusterAppConfig(cli, mcAppConfigSample, false) 85 86 // expect a call to fetch the MCRegistration secret 87 clusterstest.DoExpectGetMCRegistrationSecret(cli) 88 89 // expect a call to fetch existing OAM app config, and return not found error, to test create case 90 cli.EXPECT(). 91 Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: crName}, gomock.Not(gomock.Nil())). 92 Return(errors.NewNotFound(schema.GroupResource{Group: "core.oam.dev", Resource: "ApplicationConfiguration"}, crName)) 93 94 // expect a call to create the OAM app config 95 cli.EXPECT(). 96 Create(gomock.Any(), gomock.Any()). 97 DoAndReturn(func(ctx context.Context, c *v1alpha2.ApplicationConfiguration, opts ...client.CreateOption) error { 98 assertAppConfigValid(assert, c, mcAppConfigSample) 99 return nil 100 }) 101 102 // expect a call to update the resource with a finalizer 103 cli.EXPECT(). 104 Update(gomock.Any(), gomock.Any(), gomock.Any()). 105 DoAndReturn(func(ctx context.Context, appConfig *clustersv1alpha1.MultiClusterApplicationConfiguration, opts ...client.UpdateOption) error { 106 assert.True(len(appConfig.ObjectMeta.Finalizers) == 1, "Wrong number of finalizers") 107 assert.Equal(finalizerName, appConfig.ObjectMeta.Finalizers[0], "wrong finalizer") 108 return nil 109 }) 110 111 // expect a call to update the status of the MultiClusterApplicationConfiguration 112 doExpectStatusUpdateSucceeded(cli, mockStatusWriter, assert) 113 114 // create a request and reconcile it 115 request := clusterstest.NewRequest(namespace, crName) 116 reconciler := newReconciler(cli) 117 result, err := reconciler.Reconcile(context.TODO(), request) 118 119 mocker.Finish() 120 assert.NoError(err) 121 assert.Equal(false, result.Requeue) 122 } 123 124 // TestReconcileUpdateAppConfig tests the path of reconciling a MultiClusterApplicationConfiguration 125 // when the underlying OAM app config already exists i.e. update case 126 // GIVEN a MultiClusterApplicationConfiguration resource is created 127 // WHEN the controller Reconcile function is called 128 // THEN expect an OAM app config to be updated 129 func TestReconcileUpdateAppConfig(t *testing.T) { 130 assert := asserts.New(t) 131 132 mocker := gomock.NewController(t) 133 cli := mocks.NewMockClient(mocker) 134 mockStatusWriter := mocks.NewMockStatusWriter(mocker) 135 136 mcAppConfigSample, err := getSampleMCAppConfig() 137 if err != nil { 138 t.Fatalf(err.Error()) 139 } 140 141 existingOAMAppConfig, err := getExistingOAMAppConfig() 142 if err != nil { 143 t.Fatalf(err.Error()) 144 } 145 146 // expect a call to fetch the MultiClusterApplicationConfiguration 147 doExpectGetMultiClusterAppConfig(cli, mcAppConfigSample, true) 148 149 // expect a call to fetch the MCRegistration secret 150 clusterstest.DoExpectGetMCRegistrationSecret(cli) 151 152 // expect a call to fetch underlying OAM app config, and return an existing one 153 doExpectGetAppConfigExists(cli, mcAppConfigSample.ObjectMeta, existingOAMAppConfig.Spec) 154 155 // expect a call to update the OAM app config with the new app config data 156 cli.EXPECT(). 157 Update(gomock.Any(), gomock.Any(), gomock.Any()). 158 DoAndReturn(func(ctx context.Context, app *v1alpha2.ApplicationConfiguration, opts ...client.CreateOption) error { 159 assertAppConfigValid(assert, app, mcAppConfigSample) 160 return nil 161 }) 162 163 // expect a call to update the status of the multicluster app config 164 doExpectStatusUpdateSucceeded(cli, mockStatusWriter, assert) 165 166 // create a request and reconcile it 167 request := clusterstest.NewRequest(namespace, crName) 168 reconciler := newReconciler(cli) 169 result, err := reconciler.Reconcile(context.TODO(), request) 170 171 mocker.Finish() 172 assert.NoError(err) 173 assert.Equal(false, result.Requeue) 174 } 175 176 // TestReconcileCreateAppConfigFailed tests the path of reconciling a 177 // MultiClusterApplicationConfiguration when the underlying OAM app config does not exist and 178 // fails to be created due to some error condition 179 // GIVEN a MultiClusterApplicationConfiguration resource is created 180 // WHEN the controller Reconcile function is called and create underlying app config fails 181 // THEN expect the status of the MultiClusterApplicationConfiguration to be updated with failure 182 func TestReconcileCreateAppConfigFailed(t *testing.T) { 183 assert := asserts.New(t) 184 185 mocker := gomock.NewController(t) 186 cli := mocks.NewMockClient(mocker) 187 mockStatusWriter := mocks.NewMockStatusWriter(mocker) 188 189 mcAppConfigSample, err := getSampleMCAppConfig() 190 if err != nil { 191 t.Fatalf(err.Error()) 192 } 193 194 // expect a call to fetch the MultiClusterApplicationConfiguration 195 doExpectGetMultiClusterAppConfig(cli, mcAppConfigSample, false) 196 197 // expect a call to fetch the MCRegistration secret 198 clusterstest.DoExpectGetMCRegistrationSecret(cli) 199 200 // expect a call to fetch existing OAM app config and return not found error, to simulate create case 201 cli.EXPECT(). 202 Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: crName}, gomock.Not(gomock.Nil()), gomock.Any()). 203 Return(errors.NewNotFound(schema.GroupResource{Group: "core.oam.dev", Resource: "ApplicationConfiguration"}, crName)) 204 205 // expect a call to create the OAM app config and fail the call 206 cli.EXPECT(). 207 Create(gomock.Any(), gomock.Any()). 208 DoAndReturn(func(ctx context.Context, c *v1alpha2.ApplicationConfiguration, opts ...client.CreateOption) error { 209 return errors.NewBadRequest("will not create it") 210 }) 211 212 // expect that the status of MultiClusterApplicationConfiguration is updated to failed because we 213 // failed the underlying OAM app config's creation 214 doExpectStatusUpdateFailed(cli, mockStatusWriter, assert) 215 216 // create a request and reconcile it 217 request := clusterstest.NewRequest(namespace, crName) 218 reconciler := newReconciler(cli) 219 result, err := reconciler.Reconcile(context.TODO(), request) 220 221 mocker.Finish() 222 assert.Nil(err) 223 assert.Equal(true, result.Requeue) 224 } 225 226 // TestReconcileUpdateAppConfigFailed tests the path of reconciling a 227 // MultiClusterApplicationConfiguration when the underlying OAM app config exists and fails to be 228 // updated due to some error condition 229 // GIVEN a MultiClusterApplicationConfiguration resource is created 230 // WHEN the controller Reconcile function is called and update underlying app config fails 231 // THEN expect the status of the MultiClusterApplicationConfiguration to be updated with 232 // failure information 233 func TestReconcileUpdateAppConfigFailed(t *testing.T) { 234 assert := asserts.New(t) 235 236 mocker := gomock.NewController(t) 237 cli := mocks.NewMockClient(mocker) 238 mockStatusWriter := mocks.NewMockStatusWriter(mocker) 239 240 mcAppConfigSample, err := getSampleMCAppConfig() 241 if err != nil { 242 t.Fatalf(err.Error()) 243 } 244 245 // expect a call to fetch the MultiClusterApplicationConfiguration 246 doExpectGetMultiClusterAppConfig(cli, mcAppConfigSample, true) 247 248 // expect a call to fetch the MCRegistration secret 249 clusterstest.DoExpectGetMCRegistrationSecret(cli) 250 251 // expect a call to fetch existing OAM app config (simulate update case) 252 doExpectGetAppConfigExists(cli, mcAppConfigSample.ObjectMeta, mcAppConfigSample.Spec.Template.Spec) 253 254 // expect a call to update the OAM app config and fail the call 255 cli.EXPECT(). 256 Update(gomock.Any(), gomock.Any(), gomock.Any()). 257 DoAndReturn(func(ctx context.Context, c *v1alpha2.ApplicationConfiguration, opts ...client.CreateOption) error { 258 return errors.NewBadRequest("will not update it") 259 }) 260 261 // expect that the status of MultiClusterApplicationConfiguration is updated to failed because we 262 // failed the underlying OAM app config's creation 263 doExpectStatusUpdateFailed(cli, mockStatusWriter, assert) 264 265 // create a request and reconcile it 266 request := clusterstest.NewRequest(namespace, crName) 267 reconciler := newReconciler(cli) 268 result, err := reconciler.Reconcile(context.TODO(), request) 269 270 mocker.Finish() 271 assert.Nil(err) 272 assert.Equal(true, result.Requeue) 273 } 274 275 // TestReconcileResourceNotFound tests the path of reconciling a 276 // MultiClusterApplicationConfiguration resource which is non-existent when reconcile is called, 277 // possibly because it has been deleted. 278 // GIVEN a MultiClusterApplicationConfiguration resource has been deleted 279 // WHEN the controller Reconcile function is called 280 // THEN expect that no action is taken 281 func TestReconcileResourceNotFound(t *testing.T) { 282 assert := asserts.New(t) 283 284 mocker := gomock.NewController(t) 285 cli := mocks.NewMockClient(mocker) 286 287 // expect a call to fetch the MultiClusterApplicationConfiguration 288 // and return a not found error 289 cli.EXPECT(). 290 Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: crName}, gomock.Not(gomock.Nil()), gomock.Any()). 291 Return(errors.NewNotFound(schema.GroupResource{Group: clustersv1alpha1.SchemeGroupVersion.Group, Resource: clustersv1alpha1.MultiClusterAppConfigResource}, crName)) 292 293 // expect no further action to be taken 294 295 // create a request and reconcile it 296 request := clusterstest.NewRequest(namespace, crName) 297 reconciler := newReconciler(cli) 298 result, err := reconciler.Reconcile(context.TODO(), request) 299 300 mocker.Finish() 301 assert.NoError(err) 302 assert.Equal(false, result.Requeue) 303 } 304 305 // TestReconcilePlacementInDifferentCluster tests the path of reconciling a 306 // MultiClusterApplicationConfiguration which is placed on a cluster other than the current cluster. 307 // We expect this MultiClusterApplicationConfiguration to be ignored, i.e. no OAM app config created 308 // GIVEN a MultiClusterApplicationConfiguration resource is created with a placement in different cluster 309 // WHEN the controller Reconcile function is called 310 // THEN expect that no OAM app config is created 311 func TestReconcilePlacementInDifferentCluster(t *testing.T) { 312 assert := asserts.New(t) 313 314 mocker := gomock.NewController(t) 315 cli := mocks.NewMockClient(mocker) 316 statusWriter := mocks.NewMockStatusWriter(mocker) 317 318 mcAppConfigSample, err := getSampleMCAppConfig() 319 if err != nil { 320 t.Fatalf(err.Error()) 321 } 322 323 mcAppConfigSample.Spec.Placement.Clusters[0].Name = "not-my-cluster" 324 325 // expect a call to fetch the MultiClusterApplicationConfiguration 326 doExpectGetMultiClusterAppConfig(cli, mcAppConfigSample, true) 327 328 // expect a call to fetch the MCRegistration secret 329 clusterstest.DoExpectGetMCRegistrationSecret(cli) 330 331 // The effective state of the object will get updated even if it is note locally placed, 332 // since it would have changed 333 clusterstest.DoExpectUpdateState(t, cli, statusWriter, &mcAppConfigSample, clustersv1alpha1.Pending) 334 335 clusterstest.ExpectDeleteAssociatedResource(cli, &v1alpha2.Component{ 336 ObjectMeta: metav1.ObjectMeta{ 337 Name: mcAppConfigSample.Name, 338 Namespace: mcAppConfigSample.Namespace, 339 }, 340 }, types.NamespacedName{ 341 Namespace: mcAppConfigSample.Namespace, 342 Name: mcAppConfigSample.Name, 343 }) 344 // Expect no further action 345 346 // expect a call to update the resource with no finalizers 347 cli.EXPECT(). 348 Update(gomock.Any(), gomock.Any(), gomock.Any()). 349 DoAndReturn(func(ctx context.Context, mcAppConfig *clustersv1alpha1.MultiClusterApplicationConfiguration, opts ...client.UpdateOption) error { 350 assert.True(len(mcAppConfig.Finalizers) == 0, "Wrong number of finalizers") 351 return nil 352 }) 353 354 // create a request and reconcile it 355 request := clusterstest.NewRequest(namespace, crName) 356 reconciler := newReconciler(cli) 357 result, err := reconciler.Reconcile(context.TODO(), request) 358 359 mocker.Finish() 360 assert.NoError(err) 361 assert.Equal(false, result.Requeue) 362 } 363 364 // doExpectGetAppConfigExists expects a call to get an OAM app config and return an "existing" one 365 func doExpectGetAppConfigExists(cli *mocks.MockClient, metadata metav1.ObjectMeta, appConfigSpec v1alpha2.ApplicationConfigurationSpec) { 366 cli.EXPECT(). 367 Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: crName}, gomock.Not(gomock.Nil()), gomock.Any()). 368 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *v1alpha2.ApplicationConfiguration, opts ...client.GetOption) error { 369 appConfig.Spec = appConfigSpec 370 appConfig.ObjectMeta = metadata 371 return nil 372 }) 373 } 374 375 // doExpectStatusUpdateFailed expects a call to update status of 376 // MultiClusterApplicationConfiguration to failure 377 func doExpectStatusUpdateFailed(cli *mocks.MockClient, mockStatusWriter *mocks.MockStatusWriter, assert *asserts.Assertions) { 378 // expect a call to fetch the MCRegistration secret to get the cluster name for status update 379 clusterstest.DoExpectGetMCRegistrationSecret(cli) 380 381 // expect a call to update the status of the MultiClusterApplicationConfiguration 382 cli.EXPECT().Status().Return(mockStatusWriter) 383 384 // the status update should be to failure status/conditions on the MultiClusterApplicationConfiguration 385 mockStatusWriter.EXPECT(). 386 Update(gomock.Any(), gomock.AssignableToTypeOf(&clustersv1alpha1.MultiClusterApplicationConfiguration{}), gomock.Any()). 387 DoAndReturn(func(ctx context.Context, mcAppConfig *clustersv1alpha1.MultiClusterApplicationConfiguration, options ...client.UpdateOption) error { 388 clusterstest.AssertMultiClusterResourceStatus(assert, mcAppConfig.Status, clustersv1alpha1.Failed, clustersv1alpha1.DeployFailed, v1.ConditionTrue) 389 return nil 390 }) 391 } 392 393 // doExpectStatusUpdateSucceeded expects a call to update status of 394 // MultiClusterApplicationConfiguration to success 395 func doExpectStatusUpdateSucceeded(cli *mocks.MockClient, mockStatusWriter *mocks.MockStatusWriter, assert *asserts.Assertions) { 396 // expect a call to fetch the MCRegistration secret to get the cluster name for status update 397 clusterstest.DoExpectGetMCRegistrationSecret(cli) 398 399 // expect a call to update the status of the MultiClusterApplicationConfiguration 400 cli.EXPECT().Status().Return(mockStatusWriter) 401 402 // the status update should be to success status/conditions on the MultiClusterApplicationConfiguration 403 mockStatusWriter.EXPECT(). 404 Update(gomock.Any(), gomock.AssignableToTypeOf(&clustersv1alpha1.MultiClusterApplicationConfiguration{}), gomock.Any()). 405 DoAndReturn(func(ctx context.Context, mcAppConfig *clustersv1alpha1.MultiClusterApplicationConfiguration, options ...client.UpdateOption) error { 406 clusterstest.AssertMultiClusterResourceStatus(assert, mcAppConfig.Status, clustersv1alpha1.Succeeded, clustersv1alpha1.DeployComplete, v1.ConditionTrue) 407 return nil 408 }) 409 } 410 411 // doExpectGetMultiClusterAppConfig adds an expectation to the given MockClient to expect a Get 412 // call for a MultiClusterApplicationConfiguration, and populate it with given sample data 413 func doExpectGetMultiClusterAppConfig(cli *mocks.MockClient, mcAppConfigSample clustersv1alpha1.MultiClusterApplicationConfiguration, addFinalizer bool) { 414 cli.EXPECT(). 415 Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: crName}, gomock.AssignableToTypeOf(&mcAppConfigSample), gomock.Any()). 416 DoAndReturn(func(ctx context.Context, name types.NamespacedName, mcAppConfig *clustersv1alpha1.MultiClusterApplicationConfiguration, opts ...client.GetOption) error { 417 mcAppConfig.ObjectMeta = mcAppConfigSample.ObjectMeta 418 mcAppConfig.TypeMeta = mcAppConfigSample.TypeMeta 419 mcAppConfig.Spec = mcAppConfigSample.Spec 420 if addFinalizer { 421 mcAppConfig.Finalizers = append(mcAppConfigSample.Finalizers, finalizerName) 422 } 423 return nil 424 }) 425 } 426 427 // assertAppConfigValid asserts that the metadata and content of the created/updated OAM app config 428 // are valid 429 func assertAppConfigValid(assert *asserts.Assertions, app *v1alpha2.ApplicationConfiguration, mcAppConfig clustersv1alpha1.MultiClusterApplicationConfiguration) { 430 assert.Equal(namespace, app.ObjectMeta.Namespace) 431 assert.Equal(crName, app.ObjectMeta.Name) 432 assert.Equal(mcAppConfig.Spec.Template.Spec, app.Spec) 433 434 // assert that the app config is labeled verrazzano-managed=true since it was created by Verrazzano 435 assert.NotNil(app.Labels) 436 assert.Equal(constants.LabelVerrazzanoManagedDefault, app.Labels[vzconst.VerrazzanoManagedLabelKey]) 437 438 // assert some fields on the app config spec (e.g. in the case of update, these fields should 439 // be different from the mock pre existing OAM app config) 440 assert.Equal(len(mcAppConfig.Spec.Template.Spec.Components), len(app.Spec.Components)) 441 for i, comp := range mcAppConfig.Spec.Template.Spec.Components { 442 assert.Equal(comp.ComponentName, app.Spec.Components[i].ComponentName) 443 assert.Equal(comp.ParameterValues, app.Spec.Components[i].ParameterValues) 444 assert.Equal(comp.Scopes, app.Spec.Components[i].Scopes) 445 assert.Equal(comp.Traits, app.Spec.Components[i].Traits) 446 } 447 448 } 449 450 // getSampleMCAppConfig creates and returns a sample MultiClusterApplicationConfiguration used in tests 451 func getSampleMCAppConfig() (clustersv1alpha1.MultiClusterApplicationConfiguration, error) { 452 mcAppConfig := clustersv1alpha1.MultiClusterApplicationConfiguration{} 453 sampleMCAppConfigFile, err := filepath.Abs("testdata/hello-multiclusterappconfig.yaml") 454 if err != nil { 455 return mcAppConfig, err 456 } 457 458 rawMCAppConfig, err := clusterstest.ReadYaml2Json(sampleMCAppConfigFile) 459 if err != nil { 460 return mcAppConfig, err 461 } 462 463 err = json.Unmarshal(rawMCAppConfig, &mcAppConfig) 464 465 return mcAppConfig, err 466 } 467 468 func getExistingOAMAppConfig() (v1alpha2.ApplicationConfiguration, error) { 469 oamAppConfig := v1alpha2.ApplicationConfiguration{} 470 existingAppConfigFile, err := filepath.Abs("testdata/hello-oam-appconfig-existing.yaml") 471 if err != nil { 472 return oamAppConfig, err 473 } 474 rawMcAppConfig, err := clusterstest.ReadYaml2Json(existingAppConfigFile) 475 if err != nil { 476 return oamAppConfig, err 477 } 478 479 err = json.Unmarshal(rawMcAppConfig, &oamAppConfig) 480 return oamAppConfig, err 481 } 482 483 // newReconciler creates a new reconciler for testing 484 // c - The K8s client to inject into the reconciler 485 func newReconciler(c client.Client) Reconciler { 486 return Reconciler{ 487 Client: c, 488 Log: zap.S().With("test"), 489 Scheme: clusters.NewScheme(), 490 } 491 } 492 493 // TestReconcileKubeSystem tests to make sure we do not reconcile 494 // Any resource that belong to the kube-system namespace 495 func TestReconcileKubeSystem(t *testing.T) { 496 assert := asserts.New(t) 497 498 var mocker = gomock.NewController(t) 499 var cli = mocks.NewMockClient(mocker) 500 501 // create a request and reconcile it 502 request := clusterstest.NewRequest(vzconst.KubeSystem, "unit-test-verrazzano-helidon-workload") 503 reconciler := newReconciler(cli) 504 result, err := reconciler.Reconcile(context.TODO(), request) 505 506 mocker.Finish() 507 assert.Nil(err) 508 assert.True(result.IsZero()) 509 }