github.com/kyma-project/kyma/components/asset-store-controller-manager@v0.0.0-20191203152857-3792b5df17c5/internal/handler/asset/asset_test.go (about) 1 package asset_test 2 3 import ( 4 "context" 5 "strings" 6 "testing" 7 "time" 8 9 engine "github.com/kyma-project/kyma/components/asset-store-controller-manager/internal/assethook" 10 engineMock "github.com/kyma-project/kyma/components/asset-store-controller-manager/internal/assethook/automock" 11 "github.com/kyma-project/kyma/components/asset-store-controller-manager/internal/handler/asset" 12 loaderMock "github.com/kyma-project/kyma/components/asset-store-controller-manager/internal/loader/automock" 13 storeMock "github.com/kyma-project/kyma/components/asset-store-controller-manager/internal/store/automock" 14 "github.com/kyma-project/kyma/components/asset-store-controller-manager/pkg/apis/assetstore/v1alpha2" 15 . "github.com/onsi/gomega" 16 "github.com/pkg/errors" 17 "github.com/stretchr/testify/mock" 18 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/client-go/tools/record" 20 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" 21 ) 22 23 var log = logf.Log.WithName("asset-test") 24 25 const ( 26 remoteBucketName = "bucket-name" 27 ) 28 29 func TestAssetHandler_Handle_OnAddOrUpdate(t *testing.T) { 30 t.Run("OnAdd", func(t *testing.T) { 31 // Given 32 g := NewGomegaWithT(t) 33 ctx := context.TODO() 34 relistInterval := time.Minute 35 now := time.Now() 36 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 37 38 handler, mocks := newHandler(relistInterval) 39 defer mocks.AssertExpectations(t) 40 41 // When 42 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 43 44 // Then 45 g.Expect(err).ToNot(HaveOccurred()) 46 g.Expect(status).ToNot(BeZero()) 47 g.Expect(status.Phase).To(Equal(v1alpha2.AssetPending)) 48 g.Expect(status.Reason).To(Equal(v1alpha2.AssetScheduled)) 49 }) 50 51 t.Run("OnUpdate", func(t *testing.T) { 52 // Given 53 g := NewGomegaWithT(t) 54 ctx := context.TODO() 55 relistInterval := time.Minute 56 now := time.Now() 57 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 58 asset.ObjectMeta.Generation = int64(2) 59 asset.Status.ObservedGeneration = int64(1) 60 61 handler, mocks := newHandler(relistInterval) 62 defer mocks.AssertExpectations(t) 63 64 // When 65 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 66 67 // Then 68 g.Expect(err).ToNot(HaveOccurred()) 69 g.Expect(status).ToNot(BeZero()) 70 g.Expect(status.Phase).To(Equal(v1alpha2.AssetPending)) 71 g.Expect(status.Reason).To(Equal(v1alpha2.AssetScheduled)) 72 }) 73 } 74 75 func TestAssetHandler_Handle_Default(t *testing.T) { 76 // Given 77 g := NewGomegaWithT(t) 78 ctx := context.TODO() 79 relistInterval := time.Minute 80 now := time.Now() 81 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 82 asset.ObjectMeta.Generation = int64(1) 83 asset.Status.ObservedGeneration = int64(1) 84 85 handler, mocks := newHandler(relistInterval) 86 defer mocks.AssertExpectations(t) 87 88 // When 89 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 90 91 // Then 92 g.Expect(err).ToNot(HaveOccurred()) 93 g.Expect(status).To(BeZero()) 94 } 95 96 func TestAssetHandler_Handle_OnReady(t *testing.T) { 97 t.Run("NotTaken", func(t *testing.T) { 98 // Given 99 g := NewGomegaWithT(t) 100 ctx := context.TODO() 101 relistInterval := time.Minute 102 now := time.Now() 103 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 104 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetReady 105 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.NewTime(now) 106 asset.Status.CommonAssetStatus.ObservedGeneration = asset.Generation 107 108 handler, mocks := newHandler(relistInterval) 109 defer mocks.AssertExpectations(t) 110 111 // When 112 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 113 114 // Then 115 g.Expect(err).ToNot(HaveOccurred()) 116 g.Expect(status).To(BeZero()) 117 }) 118 119 t.Run("NotChanged", func(t *testing.T) { 120 // Given 121 g := NewGomegaWithT(t) 122 ctx := context.TODO() 123 relistInterval := time.Minute 124 now := time.Now() 125 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 126 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetReady 127 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.NewTime(now.Add(-2 * relistInterval)) 128 asset.Status.CommonAssetStatus.ObservedGeneration = asset.Generation 129 130 handler, mocks := newHandler(relistInterval) 131 defer mocks.AssertExpectations(t) 132 mocks.store.On("ContainsAllObjects", ctx, remoteBucketName, asset.Name, mock.AnythingOfType("[]string")).Return(true, nil).Once() 133 134 // When 135 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 136 137 // Then 138 g.Expect(err).ToNot(HaveOccurred()) 139 g.Expect(status).ToNot(BeZero()) 140 g.Expect(status.Phase).To(Equal(v1alpha2.AssetReady)) 141 g.Expect(status.Reason).To(Equal(v1alpha2.AssetUploaded)) 142 }) 143 144 t.Run("BucketNotReady", func(t *testing.T) { 145 // Given 146 g := NewGomegaWithT(t) 147 ctx := context.TODO() 148 relistInterval := time.Minute 149 now := time.Now() 150 asset := testData("test-asset", "notReady", "https://localhost/test.md") 151 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetReady 152 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.NewTime(now.Add(-2 * relistInterval)) 153 asset.Status.CommonAssetStatus.ObservedGeneration = asset.Generation 154 155 handler, mocks := newHandler(relistInterval) 156 defer mocks.AssertExpectations(t) 157 158 // When 159 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 160 161 // Then 162 g.Expect(err).ToNot(HaveOccurred()) 163 g.Expect(status).ToNot(BeZero()) 164 g.Expect(status.Phase).To(Equal(v1alpha2.AssetPending)) 165 g.Expect(status.Reason).To(Equal(v1alpha2.AssetBucketNotReady)) 166 }) 167 168 t.Run("BucketError", func(t *testing.T) { 169 // Given 170 g := NewGomegaWithT(t) 171 ctx := context.TODO() 172 relistInterval := time.Minute 173 now := time.Now() 174 asset := testData("test-asset", "error", "https://localhost/test.md") 175 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetReady 176 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.NewTime(now.Add(-2 * relistInterval)) 177 asset.Status.CommonAssetStatus.ObservedGeneration = asset.Generation 178 179 handler, mocks := newHandler(relistInterval) 180 defer mocks.AssertExpectations(t) 181 182 // When 183 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 184 185 // Then 186 g.Expect(err).To(HaveOccurred()) 187 g.Expect(status).ToNot(BeZero()) 188 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 189 g.Expect(status.Reason).To(Equal(v1alpha2.AssetBucketError)) 190 }) 191 192 t.Run("MissingFiles", func(t *testing.T) { 193 // Given 194 g := NewGomegaWithT(t) 195 ctx := context.TODO() 196 relistInterval := time.Minute 197 now := time.Now() 198 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 199 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetReady 200 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.NewTime(now.Add(-2 * relistInterval)) 201 asset.Status.CommonAssetStatus.ObservedGeneration = asset.Generation 202 203 handler, mocks := newHandler(relistInterval) 204 defer mocks.AssertExpectations(t) 205 mocks.store.On("ContainsAllObjects", ctx, remoteBucketName, asset.Name, mock.AnythingOfType("[]string")).Return(false, nil).Once() 206 207 // When 208 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 209 210 // Then 211 g.Expect(err).ToNot(HaveOccurred()) 212 g.Expect(status).ToNot(BeZero()) 213 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 214 g.Expect(status.Reason).To(Equal(v1alpha2.AssetMissingContent)) 215 }) 216 217 t.Run("ContainsError", func(t *testing.T) { 218 // Given 219 g := NewGomegaWithT(t) 220 ctx := context.TODO() 221 relistInterval := time.Minute 222 now := time.Now() 223 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 224 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetReady 225 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.NewTime(now.Add(-2 * relistInterval)) 226 asset.Status.CommonAssetStatus.ObservedGeneration = asset.Generation 227 228 handler, mocks := newHandler(relistInterval) 229 defer mocks.AssertExpectations(t) 230 mocks.store.On("ContainsAllObjects", ctx, remoteBucketName, asset.Name, mock.AnythingOfType("[]string")).Return(false, errors.New("nope")).Once() 231 232 // When 233 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 234 235 // Then 236 g.Expect(err).To(HaveOccurred()) 237 g.Expect(status).ToNot(BeZero()) 238 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 239 g.Expect(status.Reason).To(Equal(v1alpha2.AssetRemoteContentVerificationError)) 240 }) 241 } 242 243 func TestAssetHandler_Handle_OnPending(t *testing.T) { 244 t.Run("WithWebhooks", func(t *testing.T) { 245 // Given 246 g := NewGomegaWithT(t) 247 ctx := context.TODO() 248 relistInterval := time.Minute 249 now := time.Now() 250 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 251 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 252 asset.Status.ObservedGeneration = asset.Generation 253 254 handler, mocks := newHandler(relistInterval) 255 defer mocks.AssertExpectations(t) 256 257 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 258 mocks.store.On("PutObjects", ctx, remoteBucketName, asset.Name, "/tmp", mock.AnythingOfType("[]string")).Return(nil).Once() 259 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 260 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 261 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: true}, nil).Once() 262 mocks.validator.On("Validate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.ValidationWebhookService).Return(engine.Result{Success: true}, nil).Once() 263 mocks.metadataExtractor.On("Extract", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MetadataWebhookService).Return(nil, nil).Once() 264 265 // When 266 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 267 268 // Then 269 g.Expect(err).ToNot(HaveOccurred()) 270 g.Expect(status).ToNot(BeZero()) 271 g.Expect(status.Phase).To(Equal(v1alpha2.AssetReady)) 272 g.Expect(status.Reason).To(Equal(v1alpha2.AssetUploaded)) 273 }) 274 275 t.Run("WithoutWebhooks", func(t *testing.T) { 276 // Given 277 g := NewGomegaWithT(t) 278 ctx := context.TODO() 279 relistInterval := time.Minute 280 now := time.Now() 281 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 282 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 283 asset.Status.ObservedGeneration = asset.Generation 284 asset.Spec.Source.ValidationWebhookService = nil 285 asset.Spec.Source.MutationWebhookService = nil 286 asset.Spec.Source.MetadataWebhookService = nil 287 288 handler, mocks := newHandler(relistInterval) 289 defer mocks.AssertExpectations(t) 290 291 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 292 mocks.store.On("PutObjects", ctx, remoteBucketName, asset.Name, "/tmp", mock.AnythingOfType("[]string")).Return(nil).Once() 293 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 294 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 295 296 // When 297 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 298 299 // Then 300 g.Expect(err).ToNot(HaveOccurred()) 301 g.Expect(status).ToNot(BeZero()) 302 g.Expect(status.Phase).To(Equal(v1alpha2.AssetReady)) 303 g.Expect(status.Reason).To(Equal(v1alpha2.AssetUploaded)) 304 }) 305 306 t.Run("LoadError", func(t *testing.T) { 307 // Given 308 g := NewGomegaWithT(t) 309 ctx := context.TODO() 310 relistInterval := time.Minute 311 now := time.Now() 312 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 313 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 314 asset.Status.ObservedGeneration = asset.Generation 315 316 handler, mocks := newHandler(relistInterval) 317 defer mocks.AssertExpectations(t) 318 319 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 320 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, errors.New("nope")).Once() 321 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 322 323 // When 324 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 325 326 // Then 327 g.Expect(err).To(HaveOccurred()) 328 g.Expect(status).ToNot(BeZero()) 329 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 330 g.Expect(status.Reason).To(Equal(v1alpha2.AssetPullingFailed)) 331 }) 332 333 t.Run("MutationFailed", func(t *testing.T) { 334 // Given 335 g := NewGomegaWithT(t) 336 ctx := context.TODO() 337 relistInterval := time.Minute 338 now := time.Now() 339 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 340 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 341 asset.Status.ObservedGeneration = asset.Generation 342 343 handler, mocks := newHandler(relistInterval) 344 defer mocks.AssertExpectations(t) 345 346 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 347 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 348 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 349 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: false}, nil).Once() 350 351 // When 352 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 353 354 // Then 355 g.Expect(err).ToNot(HaveOccurred()) 356 g.Expect(status).ToNot(BeZero()) 357 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 358 g.Expect(status.Reason).To(Equal(v1alpha2.AssetMutationFailed)) 359 }) 360 361 t.Run("MutationError", func(t *testing.T) { 362 // Given 363 g := NewGomegaWithT(t) 364 ctx := context.TODO() 365 relistInterval := time.Minute 366 now := time.Now() 367 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 368 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 369 asset.Status.ObservedGeneration = asset.Generation 370 371 handler, mocks := newHandler(relistInterval) 372 defer mocks.AssertExpectations(t) 373 374 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 375 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 376 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 377 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: false}, errors.New("nope")).Once() 378 379 // When 380 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 381 382 // Then 383 g.Expect(err).To(HaveOccurred()) 384 g.Expect(status).ToNot(BeZero()) 385 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 386 g.Expect(status.Reason).To(Equal(v1alpha2.AssetMutationError)) 387 }) 388 389 t.Run("MetadataExtractionFailed", func(t *testing.T) { 390 // Given 391 g := NewGomegaWithT(t) 392 ctx := context.TODO() 393 relistInterval := time.Minute 394 now := time.Now() 395 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 396 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 397 asset.Status.ObservedGeneration = asset.Generation 398 399 handler, mocks := newHandler(relistInterval) 400 defer mocks.AssertExpectations(t) 401 402 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 403 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 404 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 405 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: true}, nil).Once() 406 mocks.validator.On("Validate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.ValidationWebhookService).Return(engine.Result{Success: true}, nil).Once() 407 mocks.metadataExtractor.On("Extract", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MetadataWebhookService).Return(nil, errors.New("nope")).Once() 408 409 // When 410 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 411 412 // Then 413 g.Expect(err).To(HaveOccurred()) 414 g.Expect(status).ToNot(BeZero()) 415 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 416 g.Expect(status.Reason).To(Equal(v1alpha2.AssetMetadataExtractionFailed)) 417 }) 418 419 t.Run("ValidationError", func(t *testing.T) { 420 // Given 421 g := NewGomegaWithT(t) 422 ctx := context.TODO() 423 relistInterval := time.Minute 424 now := time.Now() 425 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 426 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 427 asset.Status.ObservedGeneration = asset.Generation 428 429 handler, mocks := newHandler(relistInterval) 430 defer mocks.AssertExpectations(t) 431 432 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 433 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 434 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 435 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: true}, nil).Once() 436 mocks.validator.On("Validate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.ValidationWebhookService).Return(engine.Result{Success: false}, errors.New("nope")).Once() 437 438 // When 439 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 440 441 // Then 442 g.Expect(err).To(HaveOccurred()) 443 g.Expect(status).ToNot(BeZero()) 444 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 445 g.Expect(status.Reason).To(Equal(v1alpha2.AssetValidationError)) 446 }) 447 448 t.Run("ValidationFailed", func(t *testing.T) { 449 // Given 450 g := NewGomegaWithT(t) 451 ctx := context.TODO() 452 relistInterval := time.Minute 453 now := time.Now() 454 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 455 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 456 asset.Status.ObservedGeneration = asset.Generation 457 458 handler, mocks := newHandler(relistInterval) 459 defer mocks.AssertExpectations(t) 460 461 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 462 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 463 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 464 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: true}, nil).Once() 465 mocks.validator.On("Validate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.ValidationWebhookService).Return(engine.Result{Success: false}, nil).Once() 466 467 // When 468 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 469 470 // Then 471 g.Expect(err).ToNot(HaveOccurred()) 472 g.Expect(status).ToNot(BeZero()) 473 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 474 g.Expect(status.Reason).To(Equal(v1alpha2.AssetValidationFailed)) 475 }) 476 477 t.Run("UploadError", func(t *testing.T) { 478 // Given 479 g := NewGomegaWithT(t) 480 ctx := context.TODO() 481 relistInterval := time.Minute 482 now := time.Now() 483 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 484 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 485 asset.Status.ObservedGeneration = asset.Generation 486 487 handler, mocks := newHandler(relistInterval) 488 defer mocks.AssertExpectations(t) 489 490 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 491 mocks.store.On("PutObjects", ctx, remoteBucketName, asset.Name, "/tmp", mock.AnythingOfType("[]string")).Return(errors.New("nope")).Once() 492 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 493 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 494 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: true}, nil).Once() 495 mocks.validator.On("Validate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.ValidationWebhookService).Return(engine.Result{Success: true}, nil).Once() 496 mocks.metadataExtractor.On("Extract", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MetadataWebhookService).Return(nil, nil).Once() 497 498 // When 499 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 500 501 // Then 502 g.Expect(err).To(HaveOccurred()) 503 g.Expect(status).ToNot(BeZero()) 504 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 505 g.Expect(status.Reason).To(Equal(v1alpha2.AssetUploadFailed)) 506 }) 507 508 t.Run("BucketNotReady", func(t *testing.T) { 509 // Given 510 g := NewGomegaWithT(t) 511 ctx := context.TODO() 512 relistInterval := time.Minute 513 now := time.Now() 514 asset := testData("test-asset", "notReady", "https://localhost/test.md") 515 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 516 asset.Status.ObservedGeneration = asset.Generation 517 518 handler, mocks := newHandler(relistInterval) 519 defer mocks.AssertExpectations(t) 520 521 // When 522 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 523 524 // Then 525 g.Expect(err).ToNot(HaveOccurred()) 526 g.Expect(status).ToNot(BeZero()) 527 g.Expect(status.Phase).To(Equal(v1alpha2.AssetPending)) 528 g.Expect(status.Reason).To(Equal(v1alpha2.AssetBucketNotReady)) 529 }) 530 531 t.Run("BucketStatusError", func(t *testing.T) { 532 // Given 533 g := NewGomegaWithT(t) 534 ctx := context.TODO() 535 relistInterval := time.Minute 536 now := time.Now() 537 asset := testData("test-asset", "error", "https://localhost/test.md") 538 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 539 asset.Status.ObservedGeneration = asset.Generation 540 541 handler, mocks := newHandler(relistInterval) 542 defer mocks.AssertExpectations(t) 543 544 // When 545 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 546 547 // Then 548 g.Expect(err).To(HaveOccurred()) 549 g.Expect(status).ToNot(BeZero()) 550 g.Expect(status.Phase).To(Equal(v1alpha2.AssetFailed)) 551 g.Expect(status.Reason).To(Equal(v1alpha2.AssetBucketError)) 552 }) 553 554 t.Run("OnBucketNotReadyBeforeTime", func(t *testing.T) { 555 // Given 556 g := NewGomegaWithT(t) 557 ctx := context.TODO() 558 relistInterval := time.Minute 559 now := time.Now() 560 asset := testData("test-asset", "error", "https://localhost/test.md") 561 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetPending 562 asset.Status.CommonAssetStatus.Reason = v1alpha2.AssetBucketNotReady 563 asset.Status.CommonAssetStatus.LastHeartbeatTime = v1.Now() 564 asset.Status.ObservedGeneration = asset.Generation 565 566 handler, mocks := newHandler(relistInterval) 567 defer mocks.AssertExpectations(t) 568 569 // When 570 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 571 572 // Then 573 g.Expect(err).ToNot(HaveOccurred()) 574 g.Expect(status).To(BeZero()) 575 }) 576 } 577 578 func TestAssetHandler_Handle_OnFailed(t *testing.T) { 579 t.Run("ShouldHandle", func(t *testing.T) { 580 // Given 581 g := NewGomegaWithT(t) 582 ctx := context.TODO() 583 relistInterval := time.Minute 584 now := time.Now() 585 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 586 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetFailed 587 asset.Status.CommonAssetStatus.Reason = v1alpha2.AssetBucketError 588 asset.Status.ObservedGeneration = asset.Generation 589 590 handler, mocks := newHandler(relistInterval) 591 defer mocks.AssertExpectations(t) 592 593 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 594 mocks.store.On("PutObjects", ctx, remoteBucketName, asset.Name, "/tmp", mock.AnythingOfType("[]string")).Return(nil).Once() 595 mocks.loader.On("Load", asset.Spec.Source.URL, asset.Name, asset.Spec.Source.Mode, asset.Spec.Source.Filter).Return("/tmp", nil, nil).Once() 596 mocks.loader.On("Clean", "/tmp").Return(nil).Once() 597 mocks.mutator.On("Mutate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MutationWebhookService).Return(engine.Result{Success: true}, nil).Once() 598 mocks.validator.On("Validate", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.ValidationWebhookService).Return(engine.Result{Success: true}, nil).Once() 599 mocks.metadataExtractor.On("Extract", ctx, "/tmp", mock.AnythingOfType("[]string"), asset.Spec.Source.MetadataWebhookService).Return(nil, nil).Once() 600 601 // When 602 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 603 604 // Then 605 g.Expect(err).ToNot(HaveOccurred()) 606 g.Expect(status).ToNot(BeZero()) 607 g.Expect(status.Phase).To(Equal(v1alpha2.AssetReady)) 608 g.Expect(status.Reason).To(Equal(v1alpha2.AssetUploaded)) 609 }) 610 611 t.Run("ValidationFailed", func(t *testing.T) { 612 // Given 613 g := NewGomegaWithT(t) 614 ctx := context.TODO() 615 relistInterval := time.Minute 616 now := time.Now() 617 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 618 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetFailed 619 asset.Status.CommonAssetStatus.Reason = v1alpha2.AssetValidationFailed 620 asset.Status.ObservedGeneration = asset.Generation 621 622 handler, mocks := newHandler(relistInterval) 623 defer mocks.AssertExpectations(t) 624 625 // When 626 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 627 628 // Then 629 g.Expect(err).ToNot(HaveOccurred()) 630 g.Expect(status).To(BeZero()) 631 }) 632 633 t.Run("MutationFailed", func(t *testing.T) { 634 // Given 635 g := NewGomegaWithT(t) 636 ctx := context.TODO() 637 relistInterval := time.Minute 638 now := time.Now() 639 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 640 asset.Status.CommonAssetStatus.Phase = v1alpha2.AssetFailed 641 asset.Status.CommonAssetStatus.Reason = v1alpha2.AssetMutationFailed 642 asset.Status.ObservedGeneration = asset.Generation 643 644 handler, mocks := newHandler(relistInterval) 645 defer mocks.AssertExpectations(t) 646 647 // When 648 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 649 650 // Then 651 g.Expect(err).ToNot(HaveOccurred()) 652 g.Expect(status).To(BeZero()) 653 }) 654 } 655 656 func TestAssetHandler_Handle_OnDelete(t *testing.T) { 657 t.Run("NoFiles", func(t *testing.T) { 658 // Given 659 g := NewGomegaWithT(t) 660 ctx := context.TODO() 661 relistInterval := time.Minute 662 now := time.Now() 663 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 664 nowMeta := v1.Now() 665 asset.ObjectMeta.DeletionTimestamp = &nowMeta 666 667 handler, mocks := newHandler(relistInterval) 668 defer mocks.AssertExpectations(t) 669 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, nil).Once() 670 671 // When 672 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 673 674 // Then 675 g.Expect(err).ToNot(HaveOccurred()) 676 g.Expect(status).To(BeZero()) 677 }) 678 679 t.Run("MultipleFiles", func(t *testing.T) { 680 // Given 681 g := NewGomegaWithT(t) 682 ctx := context.TODO() 683 relistInterval := time.Minute 684 now := time.Now() 685 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 686 nowMeta := v1.Now() 687 asset.ObjectMeta.DeletionTimestamp = &nowMeta 688 files := []string{"test/a.txt", "test/b.txt"} 689 690 handler, mocks := newHandler(relistInterval) 691 defer mocks.AssertExpectations(t) 692 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(files, nil).Once() 693 mocks.store.On("DeleteObjects", ctx, remoteBucketName, asset.Name).Return(nil).Once() 694 695 // When 696 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 697 698 // Then 699 g.Expect(err).ToNot(HaveOccurred()) 700 g.Expect(status).To(BeZero()) 701 }) 702 703 t.Run("ListError", func(t *testing.T) { 704 // Given 705 g := NewGomegaWithT(t) 706 ctx := context.TODO() 707 relistInterval := time.Minute 708 now := time.Now() 709 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 710 nowMeta := v1.Now() 711 asset.ObjectMeta.DeletionTimestamp = &nowMeta 712 713 handler, mocks := newHandler(relistInterval) 714 defer mocks.AssertExpectations(t) 715 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(nil, errors.New("nope")).Once() 716 717 // When 718 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 719 720 // Then 721 g.Expect(err).To(HaveOccurred()) 722 g.Expect(status).To(BeZero()) 723 }) 724 725 t.Run("Error", func(t *testing.T) { 726 // Given 727 g := NewGomegaWithT(t) 728 ctx := context.TODO() 729 relistInterval := time.Minute 730 now := time.Now() 731 asset := testData("test-asset", "test-bucket", "https://localhost/test.md") 732 nowMeta := v1.Now() 733 asset.ObjectMeta.DeletionTimestamp = &nowMeta 734 files := []string{"test/a.txt", "test/b.txt"} 735 736 handler, mocks := newHandler(relistInterval) 737 defer mocks.AssertExpectations(t) 738 mocks.store.On("ListObjects", ctx, remoteBucketName, asset.Name).Return(files, nil).Once() 739 mocks.store.On("DeleteObjects", ctx, remoteBucketName, asset.Name).Return(errors.New("nope")).Once() 740 741 // When 742 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 743 744 // Then 745 g.Expect(err).To(HaveOccurred()) 746 g.Expect(status).To(BeZero()) 747 }) 748 749 t.Run("BucketNotReady", func(t *testing.T) { 750 // Given 751 g := NewGomegaWithT(t) 752 ctx := context.TODO() 753 relistInterval := time.Minute 754 now := time.Now() 755 asset := testData("test-asset", "notReady", "https://localhost/test.md") 756 nowMeta := v1.Now() 757 asset.ObjectMeta.DeletionTimestamp = &nowMeta 758 759 handler, mocks := newHandler(relistInterval) 760 defer mocks.AssertExpectations(t) 761 762 // When 763 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 764 765 // Then 766 g.Expect(err).ToNot(HaveOccurred()) 767 g.Expect(status).To(BeZero()) 768 }) 769 770 t.Run("BucketStatusError", func(t *testing.T) { 771 // Given 772 g := NewGomegaWithT(t) 773 ctx := context.TODO() 774 relistInterval := time.Minute 775 now := time.Now() 776 asset := testData("test-asset", "error", "https://localhost/test.md") 777 nowMeta := v1.Now() 778 asset.ObjectMeta.DeletionTimestamp = &nowMeta 779 780 handler, mocks := newHandler(relistInterval) 781 defer mocks.AssertExpectations(t) 782 783 // When 784 status, err := handler.Do(ctx, now, asset, asset.Spec.CommonAssetSpec, asset.Status.CommonAssetStatus) 785 786 // Then 787 g.Expect(err).To(HaveOccurred()) 788 g.Expect(status).To(BeZero()) 789 }) 790 } 791 792 func bucketStatusFinder(ctx context.Context, namespace, name string) (*v1alpha2.CommonBucketStatus, bool, error) { 793 switch { 794 case strings.Contains(name, "notReady"): 795 return nil, false, nil 796 case strings.Contains(name, "error"): 797 return nil, false, errors.New("test-error") 798 default: 799 return &v1alpha2.CommonBucketStatus{ 800 Phase: v1alpha2.BucketReady, 801 URL: "http://test-url.com/bucket-name", 802 RemoteName: remoteBucketName, 803 }, true, nil 804 } 805 } 806 807 type mocks struct { 808 store *storeMock.Store 809 loader *loaderMock.Loader 810 validator *engineMock.Validator 811 mutator *engineMock.Mutator 812 metadataExtractor *engineMock.MetadataExtractor 813 } 814 815 func (m *mocks) AssertExpectations(t *testing.T) { 816 m.store.AssertExpectations(t) 817 m.loader.AssertExpectations(t) 818 m.validator.AssertExpectations(t) 819 m.mutator.AssertExpectations(t) 820 m.metadataExtractor.AssertExpectations(t) 821 } 822 823 func newHandler(relistInterval time.Duration) (asset.Handler, mocks) { 824 mocks := mocks{ 825 store: new(storeMock.Store), 826 loader: new(loaderMock.Loader), 827 validator: new(engineMock.Validator), 828 mutator: new(engineMock.Mutator), 829 metadataExtractor: new(engineMock.MetadataExtractor), 830 } 831 832 handler := asset.New(log, fakeRecorder(), mocks.store, mocks.loader, bucketStatusFinder, mocks.validator, mocks.mutator, mocks.metadataExtractor, relistInterval) 833 834 return handler, mocks 835 } 836 837 func fakeRecorder() record.EventRecorder { 838 return record.NewFakeRecorder(20) 839 } 840 841 func testData(assetName, bucketName, url string) *v1alpha2.Asset { 842 return &v1alpha2.Asset{ 843 ObjectMeta: v1.ObjectMeta{ 844 Name: assetName, 845 Generation: int64(1), 846 }, 847 Spec: v1alpha2.AssetSpec{ 848 CommonAssetSpec: v1alpha2.CommonAssetSpec{ 849 BucketRef: v1alpha2.AssetBucketRef{Name: bucketName}, 850 Source: v1alpha2.AssetSource{ 851 URL: url, 852 Mode: v1alpha2.AssetSingle, 853 ValidationWebhookService: make([]v1alpha2.AssetWebhookService, 3), 854 MutationWebhookService: make([]v1alpha2.AssetWebhookService, 3), 855 MetadataWebhookService: make([]v1alpha2.WebhookService, 3), 856 }, 857 }, 858 }, 859 } 860 }