github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/clusters/cluster_utils_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 clusters 5 6 import ( 7 "context" 8 "errors" 9 "sigs.k8s.io/controller-runtime/pkg/reconcile" 10 "testing" 11 "time" 12 13 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" 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/mocks" 19 "go.uber.org/zap" 20 v1 "k8s.io/api/core/v1" 21 kerr "k8s.io/apimachinery/pkg/api/errors" 22 "k8s.io/apimachinery/pkg/runtime/schema" 23 "k8s.io/apimachinery/pkg/types" 24 controllerruntime "sigs.k8s.io/controller-runtime" 25 "sigs.k8s.io/controller-runtime/pkg/client" 26 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 27 ) 28 29 // TestGetClusterName tests fetching the cluster name from the managed cluster registration secret 30 // GIVEN The managed cluster registration secret exists 31 // WHEN GetClusterName function is called 32 // THEN expect the managed-cluster-name from the secret to be returned 33 // GIVEN the managed cluster secret does not exist but the local resgistration secret does 34 // WHEN GetClusterName function is called 35 // THEN expect that managed-cluster-name from the local secret to be returned 36 // GIVEN the managed cluster secret and the local do not exist 37 // WHEN GetClusterName function is called 38 // THEN expect that empty string is returned 39 func TestGetClusterName(t *testing.T) { 40 mocker := gomock.NewController(t) 41 cli := mocks.NewMockClient(mocker) 42 43 expectMCRegistrationSecret(cli, "mycluster1", MCRegistrationSecretFullName, 1) 44 45 name := GetClusterName(context.TODO(), cli) 46 asserts.Equal(t, "mycluster1", name) 47 48 // Repeat test for registration secret not found, then local registration secret should be used 49 cli.EXPECT(). 50 Get(gomock.Any(), MCRegistrationSecretFullName, gomock.Not(gomock.Nil()), gomock.Any()). 51 Return(kerr.NewNotFound(schema.ParseGroupResource("Secret"), MCRegistrationSecretFullName.Name)) 52 53 expectMCRegistrationSecret(cli, "mycluster1", MCLocalRegistrationSecretFullName, 1) 54 55 name = GetClusterName(context.TODO(), cli) 56 asserts.Equal(t, "mycluster1", name) 57 58 // Repeat test for registration secret and local secret not found 59 cli.EXPECT(). 60 Get(gomock.Any(), MCRegistrationSecretFullName, gomock.Not(gomock.Nil()), gomock.Any()). 61 Return(kerr.NewNotFound(schema.ParseGroupResource("Secret"), MCRegistrationSecretFullName.Name)) 62 63 cli.EXPECT(). 64 Get(gomock.Any(), MCLocalRegistrationSecretFullName, gomock.Not(gomock.Nil()), gomock.Any()). 65 Return(kerr.NewNotFound(schema.ParseGroupResource("Secret"), MCLocalRegistrationSecretFullName.Name)) 66 67 name = GetClusterName(context.TODO(), cli) 68 asserts.Equal(t, "", name) 69 70 mocker.Finish() 71 } 72 73 // TestGetConditionFromResult tests the GetConditionFromResult function 74 // GIVEN a nil or non-nil err 75 // WHEN GetConditionFromResult is called 76 // The returned condition and state show success or failure depending on err == nil or not, 77 // and the message is correctly populated 78 func TestGetConditionFromResult(t *testing.T) { 79 // GIVEN err == nil 80 condition := GetConditionFromResult(nil, controllerutil.OperationResultCreated, "myresource type") 81 // TODO Mar 3 asserts.Equal(t, clustersv1alpha1.Succeeded, stateType) 82 asserts.Equal(t, clustersv1alpha1.DeployComplete, condition.Type) 83 asserts.Equal(t, v1.ConditionTrue, condition.Status) 84 asserts.Contains(t, condition.Message, "myresource type") 85 asserts.Contains(t, condition.Message, controllerutil.OperationResultCreated) 86 87 // GIVEN err != nil 88 someerr := errors.New("some error msg") 89 condition = GetConditionFromResult(someerr, controllerutil.OperationResultCreated, "myresource type") 90 // TODO Mar 3 asserts.Equal(t, clustersv1alpha1.Failed, stateType) 91 asserts.Equal(t, clustersv1alpha1.DeployFailed, condition.Type) 92 asserts.Equal(t, v1.ConditionTrue, condition.Status) 93 asserts.Contains(t, condition.Message, someerr.Error()) 94 } 95 96 // TestIgnoreNotFoundWithLog tests the IgnoreNotFoundWithLog function 97 // GIVEN a K8S NotFound error 98 // WHEN IgnoreNotFoundWithLog is called 99 // THEN a nil error is returned 100 // GIVEN any other type of error 101 // WHEN IgnoreNotFoundWithLog is called 102 // THEN the error is returned 103 func TestIgnoreNotFoundWithLog(t *testing.T) { 104 log := zap.S().With("somelogger") 105 result, err := IgnoreNotFoundWithLog(kerr.NewNotFound(controllerruntime.GroupResource{}, ""), log) 106 asserts.Nil(t, err) 107 asserts.False(t, result.Requeue) 108 109 otherErr := kerr.NewBadRequest("some other error") 110 result, err = IgnoreNotFoundWithLog(otherErr, log) 111 asserts.Nil(t, err) 112 asserts.True(t, result.Requeue) 113 } 114 115 // TestIsPlacedInThisCluster tests the IsPlacedInThisCluster function 116 // GIVEN a placement object 117 // WHEN IsPlacedInThisCluster is called 118 // THEN it returns true if the placement includes this cluster's name, false otherwise 119 func TestIsPlacedInThisCluster(t *testing.T) { 120 mocker := gomock.NewController(t) 121 cli := mocks.NewMockClient(mocker) 122 placementOnlyMyCluster := clustersv1alpha1.Placement{Clusters: []clustersv1alpha1.Cluster{{Name: "mycluster"}}} 123 placementWithMyCluster := clustersv1alpha1.Placement{Clusters: []clustersv1alpha1.Cluster{{Name: "othercluster"}, {Name: "mycluster"}}} 124 placementNotMyCluster := clustersv1alpha1.Placement{Clusters: []clustersv1alpha1.Cluster{{Name: "othercluster"}, {Name: "NOTmycluster"}}} 125 126 expectMCRegistrationSecret(cli, "mycluster", MCRegistrationSecretFullName, 3) 127 128 asserts.True(t, IsPlacedInThisCluster(context.TODO(), cli, placementOnlyMyCluster)) 129 asserts.True(t, IsPlacedInThisCluster(context.TODO(), cli, placementWithMyCluster)) 130 asserts.False(t, IsPlacedInThisCluster(context.TODO(), cli, placementNotMyCluster)) 131 132 mocker.Finish() 133 } 134 135 // TestStatusNeedsUpdate tests various combinations of input to the StatusNeedsUpdate function 136 // GIVEN a current status present on the resource 137 // WHEN StatusNeedsUpdate is called with a new condition, state and cluster level status 138 // THEN it returns false if the new condition and cluster level status are already present 139 // in the status, AND the new state matches the current state, true otherwise 140 func TestStatusNeedsUpdate(t *testing.T) { 141 conditionTimestamp := time.Now() 142 formattedConditionTimestamp := conditionTimestamp.Format(time.RFC3339) 143 curConditions := []clustersv1alpha1.Condition{ 144 {Type: clustersv1alpha1.DeployComplete, Status: v1.ConditionTrue, LastTransitionTime: formattedConditionTimestamp}, 145 } 146 curState := clustersv1alpha1.Failed 147 curCluster1Status := clustersv1alpha1.ClusterLevelStatus{Name: "cluster1", State: clustersv1alpha1.Succeeded, Message: "success msg", LastUpdateTime: formattedConditionTimestamp} 148 curCluster2Status := clustersv1alpha1.ClusterLevelStatus{Name: "cluster2", State: clustersv1alpha1.Failed, Message: "failure msg", LastUpdateTime: formattedConditionTimestamp} 149 150 curStatus := clustersv1alpha1.MultiClusterResourceStatus{ 151 Conditions: curConditions, 152 State: curState, 153 Clusters: []clustersv1alpha1.ClusterLevelStatus{curCluster1Status, curCluster2Status}, 154 } 155 156 otherTimestamp := conditionTimestamp.AddDate(0, 0, 1).Format(time.RFC3339) 157 newCond := clustersv1alpha1.Condition{Type: clustersv1alpha1.DeployFailed, Status: v1.ConditionTrue} 158 existingCond := curConditions[0] 159 newCluster1Status := clustersv1alpha1.ClusterLevelStatus{ 160 Name: curCluster1Status.Name, 161 Message: "cluster failed", 162 State: clustersv1alpha1.Failed, 163 LastUpdateTime: formattedConditionTimestamp} 164 newCluster2Status := clustersv1alpha1.ClusterLevelStatus{ 165 Name: curCluster2Status.Name, 166 Message: "cluster succeeded", 167 State: clustersv1alpha1.Succeeded, 168 LastUpdateTime: formattedConditionTimestamp} 169 170 existingCondDiffTimestampCluster1 := clustersv1alpha1.Condition{ 171 Type: curConditions[0].Type, Status: curConditions[0].Status, 172 Message: curConditions[0].Message, LastTransitionTime: otherTimestamp} 173 174 existingCondDiffMessageCluster1 := clustersv1alpha1.Condition{ 175 Type: curConditions[0].Type, Status: curConditions[0].Status, 176 Message: "Some other different message", LastTransitionTime: curConditions[0].LastTransitionTime} 177 178 cluster1StatusDiffTimestamp := clustersv1alpha1.ClusterLevelStatus{ 179 Name: curCluster1Status.Name, 180 Message: curCluster1Status.Message, 181 State: curCluster1Status.State, 182 LastUpdateTime: otherTimestamp} 183 184 newClusterStatus := clustersv1alpha1.ClusterLevelStatus{ 185 Name: "newCluster", 186 Message: "cluster succeeded", 187 State: clustersv1alpha1.Succeeded, 188 LastUpdateTime: otherTimestamp} 189 190 // Asserts new condition, same cluster status for each cluster- needs update 191 asserts.True(t, StatusNeedsUpdate(curStatus, newCond, curCluster1Status)) 192 asserts.True(t, StatusNeedsUpdate(curStatus, newCond, curCluster2Status)) 193 194 // new condition, same cluster status for each cluster - needs update 195 asserts.True(t, StatusNeedsUpdate(curStatus, newCond, curCluster1Status)) 196 asserts.True(t, StatusNeedsUpdate(curStatus, newCond, curCluster2Status)) 197 198 // same condition, same cluster status for each clusters - does not need update 199 asserts.False(t, StatusNeedsUpdate(curStatus, existingCond, curCluster1Status)) 200 asserts.False(t, StatusNeedsUpdate(curStatus, existingCond, curCluster2Status)) 201 202 // same condition, different cluster status for each cluster - needs update 203 asserts.True(t, StatusNeedsUpdate(curStatus, existingCond, newCluster1Status)) 204 asserts.True(t, StatusNeedsUpdate(curStatus, existingCond, newCluster2Status)) 205 206 // same condition, differing in condition timestamp - does not need update 207 asserts.False(t, StatusNeedsUpdate(curStatus, existingCondDiffTimestampCluster1, curCluster1Status)) 208 209 // same condition, differing in cluster status timestamp - does not need update 210 asserts.False(t, StatusNeedsUpdate(curStatus, existingCondDiffTimestampCluster1, cluster1StatusDiffTimestamp)) 211 212 // same condition, differing in condition message - needs update 213 asserts.True(t, StatusNeedsUpdate(curStatus, existingCondDiffMessageCluster1, curCluster1Status)) 214 215 // same condition, differing in condition message - needs update 216 asserts.True(t, StatusNeedsUpdate(curStatus, existingCondDiffMessageCluster1, cluster1StatusDiffTimestamp)) 217 218 // same condition, new cluster not present in conditions - needs update 219 asserts.True(t, StatusNeedsUpdate(curStatus, existingCond, newClusterStatus)) 220 } 221 222 // TestCreateClusterLevelStatus tests the CreateClusterLevelStatus function 223 // GIVEN a condition and state 224 // WHEN CreateClusterLevelStatus is called 225 // THEN it returns a cluster state correctly populated 226 func TestCreateClusterLevelStatus(t *testing.T) { 227 formattedConditionTimestamp := time.Now().Format(time.RFC3339) 228 condition1 := clustersv1alpha1.Condition{ 229 Type: clustersv1alpha1.DeployComplete, Status: v1.ConditionTrue, Message: "cond1 msg", LastTransitionTime: formattedConditionTimestamp, 230 } 231 condition2 := clustersv1alpha1.Condition{ 232 Type: clustersv1alpha1.DeployFailed, Status: v1.ConditionTrue, Message: "cond2 msg", LastTransitionTime: formattedConditionTimestamp, 233 } 234 clusterState1 := CreateClusterLevelStatus(condition1, "cluster1") 235 asserts.Equal(t, "cluster1", clusterState1.Name) 236 asserts.Equal(t, clustersv1alpha1.Succeeded, clusterState1.State) 237 asserts.Equal(t, formattedConditionTimestamp, clusterState1.LastUpdateTime) 238 asserts.Equal(t, condition1.Message, clusterState1.Message) 239 240 clusterState2 := CreateClusterLevelStatus(condition2, "somecluster") 241 asserts.Equal(t, "somecluster", clusterState2.Name) 242 asserts.Equal(t, clustersv1alpha1.Failed, clusterState2.State) 243 asserts.Equal(t, formattedConditionTimestamp, clusterState2.LastUpdateTime) 244 asserts.Equal(t, condition2.Message, clusterState2.Message) 245 } 246 247 // TestComputeEffectiveState tests the ComputeEffectiveState function 248 // GIVEN a multi cluster resource status and its placements 249 // WHEN ComputeEffectiveState is called 250 // THEN it returns a cluster state that rolls up the states of individual cluster placements 251 func TestComputeEffectiveState(t *testing.T) { 252 placement := clustersv1alpha1.Placement{ 253 Clusters: []clustersv1alpha1.Cluster{ 254 {Name: "cluster1"}, 255 {Name: "cluster2"}, 256 }, 257 } 258 allSucceededStatus := clustersv1alpha1.MultiClusterResourceStatus{ 259 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 260 {Name: "cluster1", State: clustersv1alpha1.Succeeded}, 261 {Name: "cluster2", State: clustersv1alpha1.Succeeded}, 262 }, 263 } 264 somePendingStatus := clustersv1alpha1.MultiClusterResourceStatus{ 265 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 266 {Name: "cluster1", State: clustersv1alpha1.Succeeded}, 267 {Name: "cluster2", State: clustersv1alpha1.Pending}, 268 }, 269 } 270 someFailedSomePendingStatus := clustersv1alpha1.MultiClusterResourceStatus{ 271 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 272 {Name: "cluster1", State: clustersv1alpha1.Failed}, 273 {Name: "cluster2", State: clustersv1alpha1.Pending}, 274 }, 275 } 276 someFailedSomeSucceededStatus := clustersv1alpha1.MultiClusterResourceStatus{ 277 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 278 {Name: "cluster1", State: clustersv1alpha1.Succeeded}, 279 {Name: "cluster2", State: clustersv1alpha1.Failed}, 280 }, 281 } 282 failedWithUnknownClusterSucceededStatus := clustersv1alpha1.MultiClusterResourceStatus{ 283 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 284 {Name: "cluster3", State: clustersv1alpha1.Succeeded}, 285 {Name: "cluster2", State: clustersv1alpha1.Failed}, 286 }, 287 } 288 pendingWithUnknownClusterSucceededStatus := clustersv1alpha1.MultiClusterResourceStatus{ 289 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 290 {Name: "cluster3", State: clustersv1alpha1.Succeeded}, 291 {Name: "cluster2", State: clustersv1alpha1.Pending}, 292 }, 293 } 294 asserts.Equal(t, clustersv1alpha1.Succeeded, ComputeEffectiveState(allSucceededStatus, placement)) 295 asserts.Equal(t, clustersv1alpha1.Pending, ComputeEffectiveState(somePendingStatus, placement)) 296 asserts.Equal(t, clustersv1alpha1.Failed, ComputeEffectiveState(someFailedSomePendingStatus, placement)) 297 asserts.Equal(t, clustersv1alpha1.Failed, ComputeEffectiveState(someFailedSomeSucceededStatus, placement)) 298 asserts.Equal(t, clustersv1alpha1.Failed, ComputeEffectiveState(failedWithUnknownClusterSucceededStatus, placement)) 299 asserts.Equal(t, clustersv1alpha1.Pending, ComputeEffectiveState(pendingWithUnknownClusterSucceededStatus, placement)) 300 } 301 302 func TestUpdateClusterLevelStatus(t *testing.T) { 303 resourceStatus := clustersv1alpha1.MultiClusterResourceStatus{ 304 Clusters: []clustersv1alpha1.ClusterLevelStatus{ 305 {Name: "cluster1", State: clustersv1alpha1.Failed}, 306 {Name: "cluster2", State: clustersv1alpha1.Pending}, 307 }, 308 } 309 310 cluster1Succeeded := clustersv1alpha1.ClusterLevelStatus{ 311 Name: "cluster1", State: clustersv1alpha1.Succeeded, 312 } 313 314 cluster2Failed := clustersv1alpha1.ClusterLevelStatus{ 315 Name: "cluster2", State: clustersv1alpha1.Failed, 316 } 317 318 newClusterPending := clustersv1alpha1.ClusterLevelStatus{ 319 Name: "newCluster", State: clustersv1alpha1.Pending, 320 } 321 322 // existing cluster cluster1 should be updated 323 SetClusterLevelStatus(&resourceStatus, cluster1Succeeded) 324 asserts.Equal(t, 2, len(resourceStatus.Clusters)) 325 asserts.Equal(t, clustersv1alpha1.Succeeded, resourceStatus.Clusters[0].State) 326 327 // existing cluster cluster2 should be updated 328 SetClusterLevelStatus(&resourceStatus, cluster2Failed) 329 asserts.Equal(t, 2, len(resourceStatus.Clusters)) 330 asserts.Equal(t, clustersv1alpha1.Failed, resourceStatus.Clusters[1].State) 331 332 // hitherto unseen cluster should be added to the cluster level statuses list 333 SetClusterLevelStatus(&resourceStatus, newClusterPending) 334 asserts.Equal(t, 3, len(resourceStatus.Clusters)) 335 asserts.Equal(t, clustersv1alpha1.Pending, resourceStatus.Clusters[2].State) 336 337 } 338 339 func expectMCRegistrationSecret(cli *mocks.MockClient, clusterName string, secreteNameFullName types.NamespacedName, times int) { 340 regSecretData := map[string][]byte{constants.ClusterNameData: []byte(clusterName)} 341 cli.EXPECT(). 342 Get(gomock.Any(), secreteNameFullName, gomock.Not(gomock.Nil()), gomock.Any()). 343 Times(times). 344 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *v1.Secret, opts ...client.GetOption) error { 345 secret.Name = secreteNameFullName.Name 346 secret.Namespace = secreteNameFullName.Namespace 347 secret.Data = regSecretData 348 return nil 349 }) 350 } 351 352 // TestSetEffectiveStateIfChanged tests that if the effective state of a resource has changed, it's 353 // state is changed 354 // GIVEN a MultiCluster resource whose effective state is unchanged 355 // WHEN SetEffectiveStateIfChanged is called 356 // THEN the state should not be updated 357 // GIVEN a MultiCluster resource whose effective state has changed 358 // WHEN SetEffectiveStateIfChanged is called 359 // THEN the state should be updated 360 func TestSetEffectiveStateIfChanged(t *testing.T) { 361 placement := clustersv1alpha1.Placement{ 362 Clusters: []clustersv1alpha1.Cluster{ 363 {Name: "cluster1"}, 364 {Name: "cluster2"}, 365 }, 366 } 367 secret := clustersv1alpha1.MultiClusterSecret{ 368 Spec: clustersv1alpha1.MultiClusterSecretSpec{ 369 Placement: placement, 370 }, 371 Status: clustersv1alpha1.MultiClusterResourceStatus{State: clustersv1alpha1.Pending}, 372 } 373 secret.Name = "mysecret" 374 secret.Namespace = "myns" 375 376 // Make a call with the effective state of the resource unchanged, and no change should occur 377 SetEffectiveStateIfChanged(placement, &secret.Status) 378 asserts.Equal(t, clustersv1alpha1.Pending, secret.Status.State) 379 380 // add cluster level status info to the secret's status, and make a call again - this time 381 // it should update the status of the resource since the effective state changes 382 secret.Status.Clusters = []clustersv1alpha1.ClusterLevelStatus{ 383 {Name: "cluster1", State: clustersv1alpha1.Failed}, 384 } 385 386 SetEffectiveStateIfChanged(placement, &secret.Status) 387 asserts.Equal(t, clustersv1alpha1.Failed, secret.Status.State) 388 } 389 390 // TestDeleteAssociatedResource tests that if DeleteAssociatedResource is called 391 // the given resourceToDelete is deleted and the finalizer on the mcResource is removed 392 // GIVEN a MultiCluster resource and a resourceToDelete, 393 // WHEN TestDeleteAssociatedResource is called and the resourceToDelete is successfully deleted 394 // THEN the finalizer should be removed 395 // GIVEN a MultiCluster resource and a resourceToDelete, 396 // WHEN TestDeleteAssociatedResource is called and it fails to delete the resourceToDelete 397 // THEN the finalizer should NOT be removed 398 func TestDeleteAssociatedResource(t *testing.T) { 399 mocker := gomock.NewController(t) 400 cli := mocks.NewMockClient(mocker) 401 402 mcResource := clustersv1alpha1.MultiClusterApplicationConfiguration{ 403 Spec: clustersv1alpha1.MultiClusterApplicationConfigurationSpec{ 404 Placement: clustersv1alpha1.Placement{ 405 Clusters: []clustersv1alpha1.Cluster{{Name: "mycluster"}}, 406 }, 407 }, 408 } 409 mcResource.Name = "mymcappconfig" 410 mcResource.Namespace = "myns" 411 412 resourceToDeleteName := types.NamespacedName{Name: "myappconfig", Namespace: "myns"} 413 finalizerToDelete := "thisfinalizergoes" 414 finalizerNotDelete := "thisfinalizerstays" 415 416 mcResource.SetFinalizers([]string{finalizerNotDelete, finalizerToDelete}) 417 418 // GIVEN that the deletion succeeds 419 // THEN the finalizer should be removed 420 421 // expect get and delete the app config with name resourceToDeleteName, mocking successful deletion 422 expectGetAndDeleteAppConfig(t, cli, resourceToDeleteName, nil) 423 424 // The finalizer should be removed 425 cli.EXPECT(). 426 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 427 DoAndReturn(func(ctx context.Context, mcApp *clustersv1alpha1.MultiClusterApplicationConfiguration, opts ...client.UpdateOption) error { 428 asserts.NotContains(t, mcApp.GetFinalizers(), finalizerToDelete) 429 asserts.Contains(t, mcApp.GetFinalizers(), finalizerNotDelete) 430 return nil 431 }) 432 433 err := DeleteAssociatedResource(context.TODO(), cli, &mcResource, finalizerToDelete, &v1alpha2.ApplicationConfiguration{}, resourceToDeleteName) 434 asserts.Nil(t, err) 435 436 // GIVEN that the deletion fails 437 // THEN the finalizer should NOT be removed 438 439 // expect get and delete the app config with name resourceToDeleteName, mocking FAILED deletion 440 expectGetAndDeleteAppConfig(t, cli, resourceToDeleteName, errors.New("I will not delete you, resource")) 441 442 // There should be no more interactions i.e. the finalizer should not be removed 443 444 err = DeleteAssociatedResource(context.TODO(), cli, &mcResource, finalizerToDelete, &v1alpha2.ApplicationConfiguration{}, resourceToDeleteName) 445 asserts.NotNil(t, err) 446 447 mocker.Finish() 448 } 449 450 // TestRequeueWithDelay tests that when a result is requested it has requeue set to true and a requeue after greater 451 // than 2 seconds 452 // GIVEN a need for a requeue result 453 // WHEN NewRequeueWithDelay is called 454 // THEN the returned result indicates a requeue with a requeueAfter time greaater than or equal to 2 seconds 455 func TestRequeueWithDelay(t *testing.T) { 456 result := NewRequeueWithDelay() 457 asserts.True(t, result.Requeue) 458 asserts.GreaterOrEqual(t, result.RequeueAfter.Seconds(), time.Duration(2).Seconds()) 459 } 460 461 // GIVEN a need for a testing a result for requeueing 462 // WHEN ShouldRequeue is called 463 // THEN a value of true is returned if Requeue is set to true or the RequeueAfter time is greater than 0, false otherwise 464 func TestShouldRequeue(t *testing.T) { 465 val := ShouldRequeue(reconcile.Result{Requeue: true}) 466 asserts.True(t, val) 467 val = ShouldRequeue(reconcile.Result{Requeue: false}) 468 asserts.False(t, val) 469 val = ShouldRequeue(reconcile.Result{RequeueAfter: time.Duration(2)}) 470 asserts.True(t, val) 471 val = ShouldRequeue(reconcile.Result{RequeueAfter: time.Duration(0)}) 472 asserts.False(t, val) 473 } 474 475 func expectGetAndDeleteAppConfig(t *testing.T, cli *mocks.MockClient, name types.NamespacedName, deleteErr error) { 476 cli.EXPECT(). 477 Get(gomock.Any(), name, gomock.Not(gomock.Nil()), gomock.Any()). 478 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *v1alpha2.ApplicationConfiguration, opts ...client.GetOption) error { 479 appConfig.Name = name.Name 480 appConfig.Namespace = name.Namespace 481 return nil 482 }) 483 484 cli.EXPECT(). 485 Delete(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 486 DoAndReturn(func(ctx context.Context, appConfig *v1alpha2.ApplicationConfiguration, opt ...client.DeleteOption) error { 487 asserts.Equal(t, name.Name, appConfig.Name) 488 asserts.Equal(t, name.Namespace, appConfig.Namespace) 489 return deleteErr 490 }) 491 }