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  }