github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/staged_manager_test.go (about) 1 package process_test 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/kyma-project/kyma-environment-broker/internal/process" 11 12 "github.com/kyma-project/kyma-environment-broker/internal/ptr" 13 14 "github.com/kyma-project/kyma-environment-broker/internal/broker" 15 "github.com/kyma-project/kyma-environment-broker/internal/fixture" 16 "github.com/pivotal-cf/brokerapi/v8/domain" 17 "k8s.io/apimachinery/pkg/util/wait" 18 19 "github.com/kyma-project/kyma-environment-broker/internal" 20 "github.com/kyma-project/kyma-environment-broker/internal/event" 21 "github.com/kyma-project/kyma-environment-broker/internal/storage" 22 "github.com/sirupsen/logrus" 23 "github.com/stretchr/testify/assert" 24 ) 25 26 const ( 27 globalAccountID = "80ac17bd-33e8-4ffa-8d56-1d5367755723" 28 subAccountID = "12df5747-3efb-4df6-ad6f-4414bb661ce3" 29 ) 30 31 func TestHappyPath(t *testing.T) { 32 // given 33 const opID = "op-0001234" 34 operation := FixOperation("op-0001234") 35 mgr, operationStorage, eventCollector := SetupStagedManager(operation) 36 mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil) 37 mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil) 38 mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil) 39 mgr.AddStep("stage-2", &testingStep{name: "first-2", eventPublisher: eventCollector}, nil) 40 41 // when 42 mgr.Execute(operation.ID) 43 44 // then 45 eventCollector.AssertProcessedSteps(t, []string{"first", "second", "third", "first-2"}) 46 op, _ := operationStorage.GetOperationByID(operation.ID) 47 assert.True(t, op.IsStageFinished("stage-1")) 48 assert.True(t, op.IsStageFinished("stage-2")) 49 } 50 51 func TestHappyPathWithStepCondition(t *testing.T) { 52 // given 53 const opID = "op-0001234" 54 operation := FixOperation("op-0001234") 55 mgr, operationStorage, eventCollector := SetupStagedManager(operation) 56 mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil) 57 mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, func(_ internal.Operation) bool { 58 return false 59 }) 60 mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, func(_ internal.Operation) bool { 61 return true 62 }) 63 mgr.AddStep("stage-2", &testingStep{name: "first-2", eventPublisher: eventCollector}, nil) 64 65 // when 66 mgr.Execute(operation.ID) 67 68 // then 69 eventCollector.AssertProcessedSteps(t, []string{"first", "third", "first-2"}) 70 op, _ := operationStorage.GetOperationByID(operation.ID) 71 assert.True(t, op.IsStageFinished("stage-1")) 72 assert.True(t, op.IsStageFinished("stage-2")) 73 } 74 75 func TestWithRetry(t *testing.T) { 76 // given 77 const opID = "op-0001234" 78 operation := FixOperation("op-0001234") 79 mgr, operationStorage, eventCollector := SetupStagedManager(operation) 80 mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil) 81 mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil) 82 mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil) 83 mgr.AddStep("stage-2", &onceRetryingStep{name: "first-2", eventPublisher: eventCollector}, nil) 84 mgr.AddStep("stage-2", &testingStep{name: "second-2", eventPublisher: eventCollector}, nil) 85 86 // when 87 retry, _ := mgr.Execute(operation.ID) 88 89 // then 90 assert.Zero(t, retry) 91 eventCollector.AssertProcessedSteps(t, []string{"first", "second", "third", "first-2", "first-2", "second-2"}) 92 op, _ := operationStorage.GetOperationByID(operation.ID) 93 assert.True(t, op.IsStageFinished("stage-1")) 94 assert.True(t, op.IsStageFinished("stage-2")) 95 } 96 97 func TestWithPanic(t *testing.T) { 98 // given 99 const opID = "op-0001234" 100 operation := FixOperation("op-0001234") 101 mgr, operationStorage, eventCollector := SetupStagedManager(operation) 102 mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil) 103 mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil) 104 mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil) 105 mgr.AddStep("stage-2", &panicStep{name: "first-2-panic", eventPublisher: eventCollector}, nil) 106 mgr.AddStep("stage-2", &testingStep{name: "second-2-after-panic", eventPublisher: eventCollector}, nil) 107 108 // when 109 mgr.Execute(operation.ID) 110 111 // then 112 eventCollector.AssertProcessedSteps(t, []string{"first", "second", "third"}) 113 op, _ := operationStorage.GetOperationByID(operation.ID) 114 assert.Equal(t, op.State, domain.Failed) 115 assert.True(t, op.IsStageFinished("stage-1")) 116 assert.False(t, op.IsStageFinished("stage-2")) 117 } 118 119 func TestSkipFinishedStage(t *testing.T) { 120 // given 121 operation := FixOperation("op-0001234") 122 operation.FinishStage("stage-1") 123 124 mgr, operationStorage, eventCollector := SetupStagedManager(operation) 125 mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil) 126 mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil) 127 mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil) 128 mgr.AddStep("stage-2", &testingStep{name: "first-2", eventPublisher: eventCollector}, nil) 129 130 // when 131 retry, _ := mgr.Execute(operation.ID) 132 133 // then 134 assert.Zero(t, retry) 135 eventCollector.WaitForEvents(t, 1) 136 op, _ := operationStorage.GetOperationByID(operation.ID) 137 assert.True(t, op.IsStageFinished("stage-1")) 138 assert.True(t, op.IsStageFinished("stage-2")) 139 } 140 141 func SetupStagedManager(op internal.Operation) (*process.StagedManager, storage.Operations, *CollectingEventHandler) { 142 memoryStorage := storage.NewMemoryStorage() 143 memoryStorage.Operations().InsertOperation(op) 144 145 eventCollector := &CollectingEventHandler{} 146 l := logrus.New() 147 l.SetLevel(logrus.DebugLevel) 148 mgr := process.NewStagedManager(memoryStorage.Operations(), eventCollector, 3*time.Second, process.StagedManagerConfiguration{MaxStepProcessingTime: time.Second}, l) 149 mgr.SpeedUp(100000) 150 mgr.DefineStages([]string{"stage-1", "stage-2"}) 151 152 return mgr, memoryStorage.Operations(), eventCollector 153 } 154 155 type testingStep struct { 156 name string 157 eventPublisher event.Publisher 158 } 159 160 func (s *testingStep) Name() string { 161 return s.name 162 } 163 func (s *testingStep) Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) { 164 logger.Infof("Running") 165 s.eventPublisher.Publish(context.Background(), s.name) 166 return operation, 0, nil 167 } 168 169 type onceRetryingStep struct { 170 name string 171 processed bool 172 eventPublisher event.Publisher 173 } 174 175 func (s *onceRetryingStep) Name() string { 176 return s.name 177 } 178 func (s *onceRetryingStep) Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) { 179 s.eventPublisher.Publish(context.Background(), s.name) 180 if !s.processed { 181 s.processed = true 182 return operation, time.Millisecond, nil 183 } 184 logger.Infof("Running") 185 return operation, 0, nil 186 } 187 188 type panicStep struct { 189 name string 190 processed bool 191 eventPublisher event.Publisher 192 } 193 194 func (s *panicStep) Name() string { 195 return s.name 196 } 197 198 func (s *panicStep) Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) { 199 s.eventPublisher.Publish(context.Background(), s.name) 200 logger.Infof("Panic!") 201 panic("Panicking just for test") 202 return operation, 0, nil 203 } 204 205 func fixProvisioningParametersWithPlanID(planID, region string) internal.ProvisioningParameters { 206 return internal.ProvisioningParameters{ 207 PlanID: planID, 208 ServiceID: "", 209 ErsContext: internal.ERSContext{ 210 GlobalAccountID: globalAccountID, 211 SubAccountID: subAccountID, 212 }, 213 Parameters: internal.ProvisioningParametersDTO{ 214 Region: ptr.String(region), 215 Name: "dummy", 216 Zones: []string{"europe-west3-b", "europe-west3-c"}, 217 }, 218 } 219 } 220 221 func FixOperation(ID string) internal.Operation { 222 operation := fixture.FixOperation(ID, "fea2c1a1-139d-43f6-910a-a618828a79d5", internal.OperationTypeProvision) 223 operation.FinishedStages = make([]string, 0) 224 operation.State = domain.InProgress 225 operation.Description = "" 226 operation.ProvisioningParameters = fixProvisioningParametersWithPlanID(broker.AzurePlanID, "westeurope") 227 return operation 228 } 229 230 type CollectingEventHandler struct { 231 mu sync.Mutex 232 StepsProcessed []string // collects events from the Manager 233 stepsExecuted []string // collects events from testing steps 234 } 235 236 func (h *CollectingEventHandler) OnStepExecuted(_ context.Context, ev interface{}) error { 237 h.mu.Lock() 238 defer h.mu.Unlock() 239 h.stepsExecuted = append(h.stepsExecuted, ev.(string)) 240 return nil 241 } 242 243 func (h *CollectingEventHandler) OnStepProcessed(_ context.Context, ev interface{}) error { 244 h.mu.Lock() 245 defer h.mu.Unlock() 246 h.StepsProcessed = append(h.StepsProcessed, ev.(process.OperationStepProcessed).StepName) 247 return nil 248 } 249 250 func (h *CollectingEventHandler) Publish(ctx context.Context, ev interface{}) { 251 switch ev.(type) { 252 case process.OperationStepProcessed: 253 h.OnStepProcessed(ctx, ev) 254 case string: 255 h.OnStepExecuted(ctx, ev) 256 } 257 } 258 259 func (h *CollectingEventHandler) WaitForEvents(t *testing.T, count int) { 260 assert.NoError(t, wait.PollImmediate(time.Millisecond, time.Second, func() (bool, error) { 261 return len(h.StepsProcessed) == count, nil 262 })) 263 } 264 265 func (h *CollectingEventHandler) AssertProcessedSteps(t *testing.T, stepNames []string) { 266 h.WaitForEvents(t, len(stepNames)) 267 h.mu.Lock() 268 defer h.mu.Unlock() 269 270 for i, stepName := range stepNames { 271 processed := h.StepsProcessed[i] 272 executed := h.stepsExecuted[i] 273 assert.Equal(t, stepName, processed) 274 assert.Equal(t, stepName, executed) 275 } 276 assert.Len(t, h.StepsProcessed, len(stepNames)) 277 } 278 279 type resultCollector struct { 280 duration float64 281 state domain.LastOperationState 282 } 283 284 func (rc *resultCollector) OnOperationSucceed(ctx context.Context, ev interface{}) error { 285 operationSucceeded, ok := ev.(process.OperationSucceeded) 286 if !ok { 287 return fmt.Errorf("expected process.OperationSucceeded but got %+v", ev) 288 } 289 op := operationSucceeded.Operation 290 minutes := op.UpdatedAt.Sub(op.CreatedAt).Minutes() 291 rc.duration = minutes 292 rc.state = op.State 293 return nil 294 } 295 296 func (rc *resultCollector) WaitForState(t *testing.T, state domain.LastOperationState) { 297 assert.NoError(t, wait.PollImmediate(time.Millisecond, 5*time.Second, func() (bool, error) { 298 return rc.state == state, nil 299 })) 300 } 301 302 func (rc *resultCollector) AssertSucceededState(t *testing.T) error { 303 assert.Equal(t, domain.Succeeded, rc.state) 304 return nil 305 } 306 307 func (rc *resultCollector) AssertDurationGreaterThanZero(t *testing.T) error { 308 assert.Greater(t, rc.duration, 0.0) 309 return nil 310 } 311 312 func SetupStagedManager2(op internal.Operation) (*process.StagedManager, storage.Operations, *event.PubSub) { 313 memoryStorage := storage.NewMemoryStorage() 314 memoryStorage.Operations().InsertOperation(op) 315 316 l := logrus.New() 317 l.SetLevel(logrus.DebugLevel) 318 pubSub := event.NewPubSub(nil) 319 mgr := process.NewStagedManager(memoryStorage.Operations(), pubSub, 3*time.Second, process.StagedManagerConfiguration{MaxStepProcessingTime: time.Second}, l) 320 mgr.SpeedUp(100000) 321 mgr.DefineStages([]string{"stage-1", "stage-2"}) 322 323 return mgr, memoryStorage.Operations(), pubSub 324 } 325 326 func TestOperationSucceededEvent(t *testing.T) { 327 // given 328 const opID = "op-0001234" 329 operation := FixOperation("op-0001234") 330 mgr, _, pubSub := SetupStagedManager2(operation) 331 mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: pubSub}, nil) 332 333 rc := &resultCollector{} 334 rc.duration = 123 335 pubSub.Subscribe(process.OperationSucceeded{}, rc.OnOperationSucceed) 336 fmt.Printf("rc: %.4f \n", rc.duration) 337 338 // when 339 mgr.Execute(operation.ID) 340 341 // then 342 rc.WaitForState(t, domain.Succeeded) 343 rc.AssertDurationGreaterThanZero(t) 344 }