github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/deprovisioning/manager_test.go (about) 1 package deprovisioning 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/kyma-project/kyma-environment-broker/internal" 12 "github.com/kyma-project/kyma-environment-broker/internal/event" 13 "github.com/kyma-project/kyma-environment-broker/internal/fixture" 14 "github.com/kyma-project/kyma-environment-broker/internal/process" 15 "github.com/kyma-project/kyma-environment-broker/internal/storage" 16 mocks "github.com/kyma-project/kyma-environment-broker/internal/storage/automock" 17 "github.com/pivotal-cf/brokerapi/v8/domain" 18 "github.com/sirupsen/logrus" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/mock" 21 "k8s.io/apimachinery/pkg/util/wait" 22 ) 23 24 const ( 25 operationIDSuccess = "5b954fa8-fc34-4164-96e9-49e3b6741278" 26 operationIDFailed = "69b8ee2b-5c21-4997-9070-4fd356b24c46" 27 operationIDRepeat = "ca317a1e-ddab-44d2-b2ba-7bbd9df9066f" 28 fakeInstanceID = "fea2c1a1-139d-43f6-910a-a618828a79d5" 29 ) 30 31 func TestManager_Execute(t *testing.T) { 32 for name, tc := range map[string]struct { 33 operationID string 34 expectedError bool 35 expectedRepeat time.Duration 36 expectedDesc string 37 expectedNumberOfEvents int 38 }{ 39 "operation successful": { 40 operationID: operationIDSuccess, 41 expectedError: false, 42 expectedRepeat: time.Duration(0), 43 expectedDesc: "init one two final", 44 expectedNumberOfEvents: 4, 45 }, 46 "operation failed": { 47 operationID: operationIDFailed, 48 expectedError: true, 49 expectedNumberOfEvents: 1, 50 }, 51 "operation repeated": { 52 operationID: operationIDRepeat, 53 expectedError: false, 54 expectedRepeat: time.Duration(10), 55 expectedDesc: "init", 56 expectedNumberOfEvents: 1, 57 }, 58 } { 59 t.Run(name, func(t *testing.T) { 60 // given 61 log := logrus.New() 62 memoryStorage := storage.NewMemoryStorage() 63 operations := memoryStorage.Operations() 64 err := operations.InsertDeprovisioningOperation(fixDeprovisionOperation(tc.operationID)) 65 assert.NoError(t, err) 66 err = operations.InsertOperation(fixProvisionOperation()) 67 68 sInit := testStep{t: t, name: "init", storage: operations} 69 s1 := testStep{t: t, name: "one", storage: operations} 70 s2 := testStep{t: t, name: "two", storage: operations} 71 sFinal := testStep{t: t, name: "final", storage: operations} 72 73 eventBroker := event.NewPubSub(logrus.New()) 74 eventCollector := &collectingEventHandler{} 75 eventBroker.Subscribe(process.DeprovisioningStepProcessed{}, eventCollector.OnEvent) 76 77 manager := NewManager(operations, eventBroker, log) 78 manager.InitStep(&sInit) 79 80 manager.AddStep(2, &sFinal) 81 manager.AddStep(1, &s1) 82 manager.AddStep(1, &s2) 83 84 // when 85 repeat, err := manager.Execute(tc.operationID) 86 87 // then 88 if tc.expectedError { 89 assert.Error(t, err) 90 } else { 91 assert.NoError(t, err) 92 assert.Equal(t, tc.expectedRepeat, repeat) 93 94 operation, err := operations.GetOperationByID(tc.operationID) 95 assert.NoError(t, err) 96 assert.Equal(t, tc.expectedDesc, strings.Trim(operation.Description, " ")) 97 } 98 assert.NoError(t, wait.PollImmediate(20*time.Millisecond, 2*time.Second, func() (bool, error) { 99 return len(eventCollector.Events) == tc.expectedNumberOfEvents, nil 100 })) 101 }) 102 } 103 104 t.Run("should fail operation when provisioning operation not found", func(t *testing.T) { 105 // given 106 log := logrus.New() 107 memoryStorage := storage.NewMemoryStorage() 108 operations := memoryStorage.Operations() 109 err := operations.InsertDeprovisioningOperation(fixDeprovisionOperation(operationIDSuccess)) 110 111 assert.NoError(t, err) 112 113 eventBroker := event.NewPubSub(logrus.New()) 114 eventCollector := &collectingEventHandler{} 115 eventBroker.Subscribe(process.DeprovisioningStepProcessed{}, eventCollector.OnEvent) 116 117 manager := NewManager(operations, eventBroker, log) 118 119 // when 120 repeat, err := manager.Execute(operationIDSuccess) 121 assert.Equal(t, time.Duration(0), repeat) 122 assert.Error(t, err) 123 124 // assert operation state as failed 125 operation, err := memoryStorage.Deprovisioning(). 126 GetDeprovisioningOperationByID(operationIDSuccess) 127 128 assert.NoError(t, err) 129 assert.Equal(t, domain.Failed, operation.State) 130 131 assert.NoError(t, wait.PollImmediate(20*time.Millisecond, 2*time.Second, func() (bool, error) { 132 return len(eventCollector.Events) == 1, nil 133 })) 134 }) 135 136 t.Run("should repeat operation when provisioning operation error other than not found", func(t *testing.T) { 137 // given 138 log := logrus.New() 139 memoryStorage := mocks.Operations{} 140 operation := fixDeprovisionOperation(operationIDSuccess) 141 memoryStorage.On("GetDeprovisioningOperationByID", operationIDSuccess).Return(&operation, nil) 142 memoryStorage.On("GetProvisioningOperationByInstanceID", mock.Anything).Return(nil, fmt.Errorf("Error connecting to database")) 143 144 eventBroker := event.NewPubSub(logrus.New()) 145 eventCollector := &collectingEventHandler{} 146 eventBroker.Subscribe(process.DeprovisioningStepProcessed{}, eventCollector.OnEvent) 147 148 manager := NewManager(&memoryStorage, eventBroker, log) 149 150 // when 151 repeat, err := manager.Execute(operationIDSuccess) 152 assert.Equal(t, retryAfterTime, repeat) 153 assert.NoError(t, err) 154 155 // assert operation state as failed 156 assert.NoError(t, err) 157 // assert.True(t, dberr.IsNotFound(err)) 158 assert.Equal(t, domain.InProgress, operation.State) 159 160 assert.NoError(t, wait.PollImmediate(20*time.Millisecond, 2*time.Second, func() (bool, error) { 161 return len(eventCollector.Events) == 1, nil 162 })) 163 }) 164 165 } 166 167 func fixDeprovisionOperation(ID string) internal.DeprovisioningOperation { 168 deprovisioningOperation := fixture.FixDeprovisioningOperation(ID, fakeInstanceID) 169 deprovisioningOperation.State = domain.InProgress 170 deprovisioningOperation.Description = "" 171 172 return deprovisioningOperation 173 } 174 175 func fixProvisionOperation() internal.Operation { 176 return fixture.FixProvisioningOperation("6bc401aa-2ec4-4303-bf3f-2e04990f6d8f", fakeInstanceID) 177 } 178 179 type testStep struct { 180 t *testing.T 181 name string 182 storage storage.Operations 183 } 184 185 func (ts *testStep) Name() string { 186 return ts.name 187 } 188 189 func (ts *testStep) Run(operation internal.DeprovisioningOperation, logger logrus.FieldLogger) (internal.DeprovisioningOperation, time.Duration, error) { 190 logger.Infof("inside %s step", ts.name) 191 192 operation.Description = fmt.Sprintf("%s %s", operation.Description, ts.name) 193 updated, err := ts.storage.UpdateDeprovisioningOperation(operation) 194 if err != nil { 195 ts.t.Error(err) 196 } 197 198 switch operation.ID { 199 case operationIDFailed: 200 return *updated, 0, fmt.Errorf("operation %s failed", operation.ID) 201 case operationIDRepeat: 202 return *updated, time.Duration(10), nil 203 default: 204 return *updated, 0, nil 205 } 206 } 207 208 type collectingEventHandler struct { 209 mu sync.Mutex 210 Events []interface{} 211 } 212 213 func (h *collectingEventHandler) OnEvent(ctx context.Context, ev interface{}) error { 214 h.mu.Lock() 215 defer h.mu.Unlock() 216 217 h.Events = append(h.Events, ev) 218 return nil 219 }