github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/apptemplate/service_test.go (about) 1 package apptemplate_test 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 9 "github.com/kyma-incubator/compass/components/director/pkg/resource" 10 11 "github.com/kyma-incubator/compass/components/director/internal/labelfilter" 12 13 "github.com/kyma-incubator/compass/components/director/internal/domain/apptemplate" 14 "github.com/kyma-incubator/compass/components/director/internal/domain/apptemplate/automock" 15 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 16 "github.com/kyma-incubator/compass/components/director/internal/model" 17 "github.com/kyma-incubator/compass/components/director/pkg/str" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/mock" 20 21 "testing" 22 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestService_Create(t *testing.T) { 27 // GIVEN 28 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 29 30 uidSvcFn := func() *automock.UIDService { 31 uidSvc := &automock.UIDService{} 32 uidSvc.On("Generate").Return(testID) 33 return uidSvc 34 } 35 36 predefinedID := "123-465-789" 37 38 appInputJSON := fmt.Sprintf(appInputJSONWithAppTypeLabelString, testName) 39 appInputJSONOtherSystemType := fmt.Sprintf(appInputJSONWithAppTypeLabelString, "random-name") 40 41 modelAppTemplate := fixModelAppTemplateWithAppInputJSON(testID, testName, appInputJSON, []*model.Webhook{}) 42 modelAppTemplateOtherSystemType := fixModelAppTemplateWithAppInputJSON(testID, testNameOtherSystemType, appInputJSONOtherSystemType, []*model.Webhook{}) 43 44 appTemplateInputWithWebhooks := fixModelAppTemplateInput(testName, appInputJSONString) 45 appTemplateInputWithWebhooks.Webhooks = []*model.WebhookInput{ 46 { 47 Type: model.WebhookTypeConfigurationChanged, 48 URL: str.Ptr("foourl"), 49 Auth: &model.AuthInput{}, 50 }, 51 } 52 appTemplateInputMatcher := func(webhooks []*model.Webhook) bool { 53 return len(webhooks) == 1 && webhooks[0].ObjectID == testID && webhooks[0].Type == model.WebhookTypeConfigurationChanged && *webhooks[0].URL == "foourl" 54 } 55 56 testCases := []struct { 57 Name string 58 Input func() *model.ApplicationTemplateInput 59 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 60 WebhookRepoFn func() *automock.WebhookRepository 61 LabelUpsertSvcFn func() *automock.LabelUpsertService 62 LabelRepoFn func() *automock.LabelRepository 63 ExpectedError error 64 ExpectedOutput string 65 }{ 66 { 67 Name: "Success", 68 Input: func() *model.ApplicationTemplateInput { 69 return fixModelAppTemplateInput(testName, appInputJSON) 70 }, 71 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 72 appTemplateRepo := &automock.ApplicationTemplateRepository{} 73 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 74 appTemplateRepo.On("Create", ctx, *modelAppTemplate).Return(nil).Once() 75 return appTemplateRepo 76 }, 77 WebhookRepoFn: func() *automock.WebhookRepository { 78 webhookRepo := &automock.WebhookRepository{} 79 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 80 return webhookRepo 81 }, 82 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 83 labelUpsertService := &automock.LabelUpsertService{} 84 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, "foo", map[string]interface{}{"test": "test"}).Return(nil).Once() 85 return labelUpsertService 86 }, 87 LabelRepoFn: UnusedLabelRepo, 88 ExpectedOutput: testID, 89 }, 90 { 91 Name: "Success without app input labels", 92 Input: func() *model.ApplicationTemplateInput { 93 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 94 return fixModelAppTemplateInput(testName, appInputJSON) 95 }, 96 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 97 appTemplateRepo := &automock.ApplicationTemplateRepository{} 98 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 99 appTemplateRepo.On("Create", ctx, mock.AnythingOfType("model.ApplicationTemplate")).Return(nil).Once() 100 return appTemplateRepo 101 }, 102 WebhookRepoFn: func() *automock.WebhookRepository { 103 webhookRepo := &automock.WebhookRepository{} 104 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 105 return webhookRepo 106 }, 107 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 108 labelUpsertService := &automock.LabelUpsertService{} 109 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, "foo", map[string]interface{}{"test": "test"}).Return(nil).Once() 110 return labelUpsertService 111 }, 112 LabelRepoFn: UnusedLabelRepo, 113 ExpectedOutput: testID, 114 }, 115 { 116 Name: "Success when ID is already generated", 117 Input: func() *model.ApplicationTemplateInput { 118 return fixModelAppTemplateWithIDInput(testName, appInputJSON, &predefinedID) 119 }, 120 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 121 appTemplateRepo := &automock.ApplicationTemplateRepository{} 122 modelAppTemplateWithPredefinedID := *modelAppTemplate 123 modelAppTemplateWithPredefinedID.ID = predefinedID 124 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 125 appTemplateRepo.On("Create", ctx, modelAppTemplateWithPredefinedID).Return(nil).Once() 126 return appTemplateRepo 127 }, 128 WebhookRepoFn: func() *automock.WebhookRepository { 129 webhookRepo := &automock.WebhookRepository{} 130 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 131 return webhookRepo 132 }, 133 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 134 labelUpsertService := &automock.LabelUpsertService{} 135 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, predefinedID, map[string]interface{}{"test": "test"}).Return(nil).Once() 136 return labelUpsertService 137 }, 138 LabelRepoFn: UnusedLabelRepo, 139 ExpectedOutput: predefinedID, 140 }, 141 { 142 Name: "Success for Application Template with webhooks", 143 Input: func() *model.ApplicationTemplateInput { 144 return appTemplateInputWithWebhooks 145 }, 146 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 147 appTemplateRepo := &automock.ApplicationTemplateRepository{} 148 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 149 appTemplateRepo.On("Create", ctx, mock.AnythingOfType("model.ApplicationTemplate")).Return(nil).Once() 150 return appTemplateRepo 151 }, 152 WebhookRepoFn: func() *automock.WebhookRepository { 153 webhookRepo := &automock.WebhookRepository{} 154 webhookRepo.On("CreateMany", ctx, "", mock.MatchedBy(appTemplateInputMatcher)).Return(nil).Once() 155 return webhookRepo 156 }, 157 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 158 labelUpsertService := &automock.LabelUpsertService{} 159 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, "foo", map[string]interface{}{"test": "test"}).Return(nil).Once() 160 return labelUpsertService 161 }, 162 LabelRepoFn: UnusedLabelRepo, 163 ExpectedOutput: testID, 164 }, 165 { 166 Name: "Error when creating application template", 167 Input: func() *model.ApplicationTemplateInput { 168 return fixModelAppTemplateInput(testName, appInputJSON) 169 }, 170 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 171 appTemplateRepo := &automock.ApplicationTemplateRepository{} 172 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 173 appTemplateRepo.On("Create", ctx, *modelAppTemplate).Return(testError).Once() 174 return appTemplateRepo 175 }, 176 WebhookRepoFn: UnusedWebhookRepo, 177 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 178 LabelRepoFn: UnusedLabelRepo, 179 ExpectedError: testError, 180 ExpectedOutput: "", 181 }, 182 { 183 Name: "Error when checking application type label - labels are not map[string]interface{}", 184 Input: func() *model.ApplicationTemplateInput { 185 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":123,"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 186 return fixModelAppTemplateInput(testName, appInputJSON) 187 }, 188 AppTemplateRepoFn: UnusedAppTemplateRepo, 189 WebhookRepoFn: UnusedWebhookRepo, 190 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 191 LabelRepoFn: UnusedLabelRepo, 192 ExpectedError: errors.New("app input json labels are type map[string]interface {} instead of map[string]interface{}"), 193 }, 194 { 195 Name: "Error when checking application type label - application type is not string", 196 Input: func() *model.ApplicationTemplateInput { 197 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":{"applicationType":123,"test":["val","val2"]},"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 198 return fixModelAppTemplateInput(testName, appInputJSON) 199 }, 200 AppTemplateRepoFn: UnusedAppTemplateRepo, 201 WebhookRepoFn: UnusedWebhookRepo, 202 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 203 LabelRepoFn: UnusedLabelRepo, 204 ExpectedError: errors.New("\"applicationType\" label value must be string"), 205 }, 206 { 207 Name: "Error when checking application type label - application type does not match the application template name", 208 Input: func() *model.ApplicationTemplateInput { 209 return fixModelAppTemplateInput(testName, fmt.Sprintf(appInputJSONWithAppTypeLabelString, "random-name")) 210 }, 211 AppTemplateRepoFn: UnusedAppTemplateRepo, 212 WebhookRepoFn: UnusedWebhookRepo, 213 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 214 LabelRepoFn: UnusedLabelRepo, 215 ExpectedError: errors.New("\"applicationType\" label value does not match the application template name"), 216 }, 217 { 218 Name: "No error when checking application type label - application type does not match the application template name", 219 Input: func() *model.ApplicationTemplateInput { 220 return fixModelAppTemplateInput(testNameOtherSystemType, fmt.Sprintf(appInputJSONWithAppTypeLabelString, "random-name")) 221 }, 222 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 223 appTemplateRepo := &automock.ApplicationTemplateRepository{} 224 appTemplateRepo.On("ListByName", ctx, testNameOtherSystemType).Return([]*model.ApplicationTemplate{}, nil).Once() 225 appTemplateRepo.On("Create", ctx, *modelAppTemplateOtherSystemType).Return(nil).Once() 226 return appTemplateRepo 227 }, 228 WebhookRepoFn: func() *automock.WebhookRepository { 229 webhookRepo := &automock.WebhookRepository{} 230 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 231 return webhookRepo 232 }, 233 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 234 labelUpsertService := &automock.LabelUpsertService{} 235 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, "foo", map[string]interface{}{"test": "test"}).Return(nil).Once() 236 return labelUpsertService 237 }, 238 LabelRepoFn: UnusedLabelRepo, 239 ExpectedOutput: testID, 240 }, 241 { 242 Name: "Error when checking if application template already exists - GetByName returns error", 243 Input: func() *model.ApplicationTemplateInput { 244 return fixModelAppTemplateInput(testName, appInputJSONString) 245 }, 246 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 247 appTemplateRepo := &automock.ApplicationTemplateRepository{} 248 appTemplateRepo.On("ListByName", ctx, testName).Return(nil, testError).Once() 249 return appTemplateRepo 250 }, 251 WebhookRepoFn: UnusedWebhookRepo, 252 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 253 LabelRepoFn: UnusedLabelRepo, 254 ExpectedError: testError, 255 }, 256 { 257 Name: "Error when application template already exists", 258 Input: func() *model.ApplicationTemplateInput { 259 return fixModelAppTemplateInput(testName, appInputJSONString) 260 }, 261 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 262 appTemplateRepo := &automock.ApplicationTemplateRepository{} 263 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{modelAppTemplate}, nil).Once() 264 return appTemplateRepo 265 }, 266 WebhookRepoFn: UnusedWebhookRepo, 267 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 268 LabelRepoFn: func() *automock.LabelRepository { 269 labelRepo := &automock.LabelRepository{} 270 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 271 return labelRepo 272 }, 273 ExpectedError: errors.New("application template with name \"bar\" and region <nil> already exists"), 274 }, 275 { 276 Name: "Error when creating webhooks", 277 Input: func() *model.ApplicationTemplateInput { 278 return fixModelAppTemplateInput(testName, appInputJSON) 279 }, 280 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 281 appTemplateRepo := &automock.ApplicationTemplateRepository{} 282 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 283 appTemplateRepo.On("Create", ctx, *modelAppTemplate).Return(nil).Once() 284 return appTemplateRepo 285 }, 286 WebhookRepoFn: func() *automock.WebhookRepository { 287 webhookRepo := &automock.WebhookRepository{} 288 webhookRepo.On("CreateMany", ctx, "", mock.AnythingOfType("[]*model.Webhook")).Return(testError).Once() 289 return webhookRepo 290 }, 291 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 292 LabelRepoFn: UnusedLabelRepo, 293 ExpectedError: testError, 294 }, 295 } 296 297 for _, testCase := range testCases { 298 t.Run(testCase.Name, func(t *testing.T) { 299 appTemplateRepo := testCase.AppTemplateRepoFn() 300 webhookRepo := testCase.WebhookRepoFn() 301 labelUpsertSvc := testCase.LabelUpsertSvcFn() 302 labelRepo := testCase.LabelRepoFn() 303 idSvc := uidSvcFn() 304 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, idSvc, labelUpsertSvc, labelRepo, nil) 305 306 // WHEN 307 result, err := svc.Create(ctx, *testCase.Input()) 308 309 // THEN 310 if testCase.ExpectedError != nil { 311 require.Error(t, err) 312 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 313 } else { 314 assert.NoError(t, err) 315 } 316 assert.Equal(t, testCase.ExpectedOutput, result) 317 318 appTemplateRepo.AssertExpectations(t) 319 labelUpsertSvc.AssertExpectations(t) 320 labelRepo.AssertExpectations(t) 321 idSvc.AssertExpectations(t) 322 }) 323 } 324 } 325 326 func TestService_CreateWithLabels(t *testing.T) { 327 // GIVEN 328 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 329 330 uidSvcFn := func() *automock.UIDService { 331 uidSvc := &automock.UIDService{} 332 uidSvc.On("Generate").Return(testID) 333 return uidSvc 334 } 335 336 appInputJSON := fmt.Sprintf(appInputJSONWithAppTypeLabelString, testName) 337 modelAppTemplate := fixModelAppTemplateWithAppInputJSON(testID, testName, appInputJSON, []*model.Webhook{}) 338 339 testCases := []struct { 340 Name string 341 Input *model.ApplicationTemplateInput 342 AppTemplateID string 343 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 344 WebhookRepoFn func() *automock.WebhookRepository 345 LabelUpsertSvcFn func() *automock.LabelUpsertService 346 ExpectedError error 347 ExpectedOutput string 348 }{ 349 { 350 Name: "Success", 351 Input: fixModelAppTemplateInput(testName, appInputJSON), 352 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 353 appTemplateRepo := &automock.ApplicationTemplateRepository{} 354 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 355 appTemplateRepo.On("Create", ctx, *modelAppTemplate).Return(nil).Once() 356 return appTemplateRepo 357 }, 358 WebhookRepoFn: func() *automock.WebhookRepository { 359 webhookRepo := &automock.WebhookRepository{} 360 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 361 return webhookRepo 362 }, 363 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 364 labelUpsertService := &automock.LabelUpsertService{} 365 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, "foo", map[string]interface{}{"createWithLabels": "OK", "test": "test"}).Return(nil).Once() 366 return labelUpsertService 367 }, 368 ExpectedError: nil, 369 ExpectedOutput: testID, 370 }, 371 { 372 Name: "Error when creating application template", 373 Input: fixModelAppTemplateInput(testName, appInputJSON), 374 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 375 appTemplateRepo := &automock.ApplicationTemplateRepository{} 376 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 377 appTemplateRepo.On("Create", ctx, *modelAppTemplate).Return(testError).Once() 378 return appTemplateRepo 379 }, 380 WebhookRepoFn: UnusedWebhookRepo, 381 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 382 ExpectedError: testError, 383 ExpectedOutput: "", 384 }, 385 } 386 387 for _, testCase := range testCases { 388 t.Run(testCase.Name, func(t *testing.T) { 389 appTemplateRepo := testCase.AppTemplateRepoFn() 390 webhookRepo := testCase.WebhookRepoFn() 391 labelUpsertSvc := testCase.LabelUpsertSvcFn() 392 idSvc := uidSvcFn() 393 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, idSvc, labelUpsertSvc, nil, nil) 394 395 defer mock.AssertExpectationsForObjects(t, appTemplateRepo, labelUpsertSvc, idSvc) 396 397 // WHEN 398 result, err := svc.CreateWithLabels(ctx, *testCase.Input, map[string]interface{}{"createWithLabels": "OK"}) 399 400 // THEN 401 if testCase.ExpectedError != nil { 402 require.Error(t, err) 403 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 404 } else { 405 assert.NoError(t, err) 406 } 407 assert.Equal(t, testCase.ExpectedOutput, result) 408 }) 409 } 410 } 411 412 func TestService_Get(t *testing.T) { 413 // GIVEN 414 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 415 416 modelAppTemplate := fixModelApplicationTemplate(testID, testName, fixModelApplicationTemplateWebhooks(testWebhookID, testID)) 417 418 testCases := []struct { 419 Name string 420 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 421 WebhookRepoFn func() *automock.WebhookRepository 422 ExpectedError error 423 ExpectedOutput *model.ApplicationTemplate 424 }{ 425 { 426 Name: "Success", 427 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 428 appTemplateRepo := &automock.ApplicationTemplateRepository{} 429 appTemplateRepo.On("Get", ctx, testID).Return(modelAppTemplate, nil).Once() 430 return appTemplateRepo 431 }, 432 WebhookRepoFn: UnusedWebhookRepo, 433 ExpectedOutput: modelAppTemplate, 434 }, 435 { 436 Name: "Error when getting application template", 437 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 438 appTemplateRepo := &automock.ApplicationTemplateRepository{} 439 appTemplateRepo.On("Get", ctx, testID).Return(nil, testError).Once() 440 return appTemplateRepo 441 }, 442 WebhookRepoFn: UnusedWebhookRepo, 443 ExpectedError: testError, 444 }, 445 } 446 447 for _, testCase := range testCases { 448 t.Run(testCase.Name, func(t *testing.T) { 449 appTemplateRepo := testCase.AppTemplateRepoFn() 450 webhookRepo := testCase.WebhookRepoFn() 451 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, nil, nil, nil, nil) 452 453 // WHEN 454 result, err := svc.Get(ctx, testID) 455 456 // THEN 457 if testCase.ExpectedError != nil { 458 require.Error(t, err) 459 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 460 } else { 461 assert.NoError(t, err) 462 } 463 assert.Equal(t, testCase.ExpectedOutput, result) 464 465 appTemplateRepo.AssertExpectations(t) 466 }) 467 } 468 } 469 470 func TestService_ListLabels(t *testing.T) { 471 // GIVEN 472 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 473 474 modelAppTemplate := fixModelApplicationTemplate(testID, testName, fixModelApplicationTemplateWebhooks(testWebhookID, testID)) 475 476 id := "foo" 477 labelKey1 := "key1" 478 labelValue1 := "val1" 479 labelKey2 := "key2" 480 labelValue2 := "val2" 481 labels := map[string]*model.Label{ 482 "abc": { 483 ID: "abc", 484 Tenant: str.Ptr(testTenant), 485 Key: labelKey1, 486 Value: labelValue1, 487 ObjectID: id, 488 ObjectType: model.AppTemplateLabelableObject, 489 }, 490 "def": { 491 ID: "def", 492 Tenant: str.Ptr(testTenant), 493 Key: labelKey2, 494 Value: labelValue2, 495 ObjectID: id, 496 ObjectType: model.AppTemplateLabelableObject, 497 }, 498 } 499 500 testCases := []struct { 501 Name string 502 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 503 LabelRepoFn func() *automock.LabelRepository 504 ExpectedError error 505 ExpectedOutput *model.ApplicationTemplate 506 }{ 507 { 508 Name: "Success", 509 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 510 appTemplateRepo := &automock.ApplicationTemplateRepository{} 511 appTemplateRepo.On("Exists", ctx, testID).Return(true, nil).Once() 512 return appTemplateRepo 513 }, 514 LabelRepoFn: func() *automock.LabelRepository { 515 labelRepo := &automock.LabelRepository{} 516 labelRepo.On("ListForGlobalObject", ctx, model.AppTemplateLabelableObject, testID).Return(labels, nil).Once() 517 return labelRepo 518 }, 519 ExpectedOutput: modelAppTemplate, 520 }, 521 { 522 Name: "Error when listing labels", 523 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 524 appTemplateRepo := &automock.ApplicationTemplateRepository{} 525 appTemplateRepo.On("Exists", ctx, testID).Return(true, nil).Once() 526 return appTemplateRepo 527 }, 528 LabelRepoFn: func() *automock.LabelRepository { 529 labelRepo := &automock.LabelRepository{} 530 labelRepo.On("ListForGlobalObject", ctx, model.AppTemplateLabelableObject, testID).Return(nil, testError).Once() 531 return labelRepo 532 }, 533 ExpectedError: testError, 534 }, 535 { 536 Name: "Error when checking app template existence", 537 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 538 appTemplateRepo := &automock.ApplicationTemplateRepository{} 539 appTemplateRepo.On("Exists", ctx, testID).Return(false, testError).Once() 540 return appTemplateRepo 541 }, 542 LabelRepoFn: UnusedLabelRepo, 543 ExpectedError: testError, 544 }, 545 { 546 Name: "Error when app template does not exists", 547 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 548 appTemplateRepo := &automock.ApplicationTemplateRepository{} 549 appTemplateRepo.On("Exists", ctx, testID).Return(false, nil).Once() 550 return appTemplateRepo 551 }, 552 LabelRepoFn: UnusedLabelRepo, 553 ExpectedError: errors.New("application template with ID foo doesn't exist"), 554 }, 555 } 556 557 for _, testCase := range testCases { 558 t.Run(testCase.Name, func(t *testing.T) { 559 appTemplateRepo := testCase.AppTemplateRepoFn() 560 labelRepo := testCase.LabelRepoFn() 561 svc := apptemplate.NewService(appTemplateRepo, nil, nil, nil, labelRepo, nil) 562 563 // WHEN 564 result, err := svc.ListLabels(ctx, testID) 565 566 // THEN 567 if testCase.ExpectedError != nil { 568 require.Error(t, err) 569 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 570 } else { 571 assert.NoError(t, err) 572 assert.Equal(t, labels, result) 573 } 574 575 appTemplateRepo.AssertExpectations(t) 576 labelRepo.AssertExpectations(t) 577 }) 578 } 579 } 580 581 func TestService_GetLabel(t *testing.T) { 582 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 583 584 id := "foo" 585 labelKey1 := "key1" 586 labelValue1 := "val1" 587 labels := map[string]*model.Label{ 588 "abc": { 589 ID: "abc", 590 Tenant: str.Ptr(testTenant), 591 Key: labelKey1, 592 Value: labelValue1, 593 ObjectID: id, 594 ObjectType: model.AppTemplateLabelableObject, 595 }, 596 } 597 598 testCases := []struct { 599 Name string 600 Key string 601 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 602 LabelRepoFn func() *automock.LabelRepository 603 ExpectedError error 604 ExpectedOutput *model.Label 605 }{ 606 { 607 Name: "Success", 608 Key: "abc", 609 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 610 appTemplateRepo := &automock.ApplicationTemplateRepository{} 611 appTemplateRepo.On("Exists", ctx, testID).Return(true, nil).Once() 612 return appTemplateRepo 613 }, 614 LabelRepoFn: func() *automock.LabelRepository { 615 labelRepo := &automock.LabelRepository{} 616 labelRepo.On("ListForGlobalObject", ctx, model.AppTemplateLabelableObject, testID).Return(labels, nil).Once() 617 return labelRepo 618 }, 619 ExpectedOutput: labels["abc"], 620 ExpectedError: nil, 621 }, 622 { 623 Name: "Error when listing labels", 624 Key: "abc", 625 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 626 appTemplateRepo := &automock.ApplicationTemplateRepository{} 627 appTemplateRepo.On("Exists", ctx, testID).Return(true, nil).Once() 628 return appTemplateRepo 629 }, 630 LabelRepoFn: func() *automock.LabelRepository { 631 labelRepo := &automock.LabelRepository{} 632 labelRepo.On("ListForGlobalObject", ctx, model.AppTemplateLabelableObject, testID).Return(nil, testError).Once() 633 return labelRepo 634 }, 635 ExpectedOutput: nil, 636 ExpectedError: testError, 637 }, 638 { 639 Name: "Error when checking label with key", 640 Key: "fake-key", 641 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 642 appTemplateRepo := &automock.ApplicationTemplateRepository{} 643 appTemplateRepo.On("Exists", ctx, testID).Return(true, nil).Once() 644 return appTemplateRepo 645 }, 646 LabelRepoFn: func() *automock.LabelRepository { 647 labelRepo := &automock.LabelRepository{} 648 labelRepo.On("ListForGlobalObject", ctx, model.AppTemplateLabelableObject, testID).Return(labels, nil).Once() 649 return labelRepo 650 }, 651 ExpectedOutput: nil, 652 ExpectedError: fmt.Errorf("label fake-key for application template with ID %s doesn't exist", testID), 653 }, 654 } 655 656 for _, testCase := range testCases { 657 t.Run(testCase.Name, func(t *testing.T) { 658 appTemplateRepo := testCase.AppTemplateRepoFn() 659 labelRepo := testCase.LabelRepoFn() 660 svc := apptemplate.NewService(appTemplateRepo, nil, nil, nil, labelRepo, nil) 661 662 // WHEN 663 result, err := svc.GetLabel(ctx, testID, testCase.Key) 664 665 // THEN 666 if testCase.ExpectedError != nil { 667 require.Error(t, err) 668 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 669 } else { 670 assert.NoError(t, err) 671 assert.Equal(t, testCase.ExpectedOutput, result) 672 } 673 674 appTemplateRepo.AssertExpectations(t) 675 labelRepo.AssertExpectations(t) 676 }) 677 } 678 } 679 680 func TestService_ListByName(t *testing.T) { 681 // GIVEN 682 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 683 684 modelAppTemplates := []*model.ApplicationTemplate{fixModelApplicationTemplate(testID, testName, fixModelApplicationTemplateWebhooks(testWebhookID, testID))} 685 686 testCases := []struct { 687 Name string 688 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 689 ExpectedError error 690 ExpectedOutput []*model.ApplicationTemplate 691 }{ 692 { 693 Name: "Success", 694 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 695 appTemplateRepo := &automock.ApplicationTemplateRepository{} 696 appTemplateRepo.On("ListByName", ctx, testName).Return(modelAppTemplates, nil).Once() 697 return appTemplateRepo 698 }, 699 ExpectedOutput: modelAppTemplates, 700 }, 701 { 702 Name: "Error when getting application template", 703 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 704 appTemplateRepo := &automock.ApplicationTemplateRepository{} 705 appTemplateRepo.On("ListByName", ctx, testName).Return(nil, testError).Once() 706 return appTemplateRepo 707 }, 708 ExpectedError: testError, 709 }, 710 } 711 712 for _, testCase := range testCases { 713 t.Run(testCase.Name, func(t *testing.T) { 714 appTemplateRepo := testCase.AppTemplateRepoFn() 715 svc := apptemplate.NewService(appTemplateRepo, nil, nil, nil, nil, nil) 716 717 // WHEN 718 result, err := svc.ListByName(ctx, testName) 719 720 // THEN 721 if testCase.ExpectedError != nil { 722 require.Error(t, err) 723 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 724 } else { 725 assert.NoError(t, err) 726 } 727 assert.Equal(t, testCase.ExpectedOutput, result) 728 729 appTemplateRepo.AssertExpectations(t) 730 }) 731 } 732 } 733 734 func TestService_ListByFilters(t *testing.T) { 735 // GIVEN 736 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 737 738 modelAppTemplate := fixModelApplicationTemplate(testID, testName, fixModelApplicationTemplateWebhooks(testWebhookID, testID)) 739 filters := []*labelfilter.LabelFilter{labelfilter.NewForKey("someKey")} 740 741 testCases := []struct { 742 Name string 743 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 744 ExpectedError error 745 ExpectedOutput []*model.ApplicationTemplate 746 }{ 747 { 748 Name: "Success", 749 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 750 appTemplateRepo := &automock.ApplicationTemplateRepository{} 751 appTemplateRepo.On("ListByFilters", ctx, filters).Return([]*model.ApplicationTemplate{modelAppTemplate}, nil).Once() 752 return appTemplateRepo 753 }, 754 ExpectedOutput: []*model.ApplicationTemplate{modelAppTemplate}, 755 }, 756 { 757 Name: "Error when listing application templates by filters", 758 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 759 appTemplateRepo := &automock.ApplicationTemplateRepository{} 760 appTemplateRepo.On("ListByFilters", ctx, filters).Return(nil, testError).Once() 761 return appTemplateRepo 762 }, 763 ExpectedError: testError, 764 }, 765 } 766 767 for _, testCase := range testCases { 768 t.Run(testCase.Name, func(t *testing.T) { 769 appTemplateRepo := testCase.AppTemplateRepoFn() 770 svc := apptemplate.NewService(appTemplateRepo, nil, nil, nil, nil, nil) 771 772 // WHEN 773 result, err := svc.ListByFilters(ctx, filters) 774 775 // THEN 776 if testCase.ExpectedError != nil { 777 require.Error(t, err) 778 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 779 } else { 780 assert.NoError(t, err) 781 } 782 assert.Equal(t, testCase.ExpectedOutput, result) 783 784 appTemplateRepo.AssertExpectations(t) 785 }) 786 } 787 } 788 789 func TestService_GetByNameAndRegion(t *testing.T) { 790 // GIVEN 791 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 792 modelAppTemplate := fixModelApplicationTemplate(testID, testName, fixModelApplicationTemplateWebhooks(testWebhookID, testID)) 793 modelAppTemplates := []*model.ApplicationTemplate{modelAppTemplate} 794 795 testCases := []struct { 796 Name string 797 Region interface{} 798 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 799 LabelRepoFn func() *automock.LabelRepository 800 ExpectedError error 801 ExpectedOutput *model.ApplicationTemplate 802 }{ 803 { 804 Name: "Success", 805 Region: nil, 806 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 807 appTemplateRepo := &automock.ApplicationTemplateRepository{} 808 appTemplateRepo.On("ListByName", ctx, testName).Return(modelAppTemplates, nil).Once() 809 return appTemplateRepo 810 }, 811 LabelRepoFn: func() *automock.LabelRepository { 812 labelRepo := &automock.LabelRepository{} 813 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 814 return labelRepo 815 }, 816 ExpectedOutput: modelAppTemplate, 817 }, 818 { 819 Name: "Success with region", 820 Region: "eu-1", 821 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 822 appTemplateRepo := &automock.ApplicationTemplateRepository{} 823 appTemplateRepo.On("ListByName", ctx, testName).Return(modelAppTemplates, nil).Once() 824 return appTemplateRepo 825 }, 826 LabelRepoFn: func() *automock.LabelRepository { 827 labelRepo := &automock.LabelRepository{} 828 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(&model.Label{Value: "eu-1"}, nil).Once() 829 return labelRepo 830 }, 831 ExpectedOutput: modelAppTemplate, 832 }, 833 { 834 Name: "Error when getting application templates by name", 835 Region: nil, 836 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 837 appTemplateRepo := &automock.ApplicationTemplateRepository{} 838 appTemplateRepo.On("ListByName", ctx, testName).Return(nil, testError).Once() 839 return appTemplateRepo 840 }, 841 LabelRepoFn: UnusedLabelRepo, 842 ExpectedError: testError, 843 }, 844 { 845 Name: "Error when retrieving region label", 846 Region: nil, 847 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 848 appTemplateRepo := &automock.ApplicationTemplateRepository{} 849 appTemplateRepo.On("ListByName", ctx, testName).Return(modelAppTemplates, nil).Once() 850 return appTemplateRepo 851 }, 852 LabelRepoFn: func() *automock.LabelRepository { 853 labelRepo := &automock.LabelRepository{} 854 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, testError).Once() 855 return labelRepo 856 }, 857 ExpectedError: testError, 858 }, 859 { 860 Name: "Error when application template not found", 861 Region: nil, 862 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 863 appTemplateRepo := &automock.ApplicationTemplateRepository{} 864 appTemplateRepo.On("ListByName", ctx, testName).Return([]*model.ApplicationTemplate{}, nil).Once() 865 return appTemplateRepo 866 }, 867 LabelRepoFn: UnusedLabelRepo, 868 ExpectedError: apperrors.NewNotFoundErrorWithType(resource.ApplicationTemplate), 869 }, 870 } 871 872 for _, testCase := range testCases { 873 t.Run(testCase.Name, func(t *testing.T) { 874 appTemplateRepo := testCase.AppTemplateRepoFn() 875 labelRepo := testCase.LabelRepoFn() 876 svc := apptemplate.NewService(appTemplateRepo, nil, nil, nil, labelRepo, nil) 877 878 // WHEN 879 result, err := svc.GetByNameAndRegion(ctx, testName, testCase.Region) 880 881 // THEN 882 if testCase.ExpectedError != nil { 883 require.Error(t, err) 884 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 885 } else { 886 assert.NoError(t, err) 887 } 888 assert.Equal(t, testCase.ExpectedOutput, result) 889 890 appTemplateRepo.AssertExpectations(t) 891 labelRepo.AssertExpectations(t) 892 }) 893 } 894 } 895 896 func TestService_GetByFilters(t *testing.T) { 897 // GIVEN 898 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 899 900 modelAppTemplate := fixModelApplicationTemplate(testID, testName, fixModelApplicationTemplateWebhooks(testWebhookID, testID)) 901 filters := []*labelfilter.LabelFilter{labelfilter.NewForKey("someKey")} 902 903 testCases := []struct { 904 Name string 905 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 906 ExpectedError error 907 ExpectedOutput *model.ApplicationTemplate 908 }{ 909 { 910 Name: "Success", 911 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 912 appTemplateRepo := &automock.ApplicationTemplateRepository{} 913 appTemplateRepo.On("GetByFilters", ctx, filters).Return(modelAppTemplate, nil).Once() 914 return appTemplateRepo 915 }, 916 ExpectedOutput: modelAppTemplate, 917 }, 918 { 919 Name: "Error when getting application template", 920 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 921 appTemplateRepo := &automock.ApplicationTemplateRepository{} 922 appTemplateRepo.On("GetByFilters", ctx, filters).Return(nil, testError).Once() 923 return appTemplateRepo 924 }, 925 ExpectedError: testError, 926 }, 927 } 928 929 for _, testCase := range testCases { 930 t.Run(testCase.Name, func(t *testing.T) { 931 appTemplateRepo := testCase.AppTemplateRepoFn() 932 svc := apptemplate.NewService(appTemplateRepo, nil, nil, nil, nil, nil) 933 934 // WHEN 935 result, err := svc.GetByFilters(ctx, filters) 936 937 // THEN 938 if testCase.ExpectedError != nil { 939 require.Error(t, err) 940 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 941 } else { 942 assert.NoError(t, err) 943 } 944 assert.Equal(t, testCase.ExpectedOutput, result) 945 946 appTemplateRepo.AssertExpectations(t) 947 }) 948 } 949 } 950 951 func TestService_Exists(t *testing.T) { 952 // GIVEN 953 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 954 955 testCases := []struct { 956 Name string 957 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 958 WebhookRepoFn func() *automock.WebhookRepository 959 ExpectedError error 960 ExpectedOutput bool 961 }{ 962 { 963 Name: "Success", 964 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 965 appTemplateRepo := &automock.ApplicationTemplateRepository{} 966 appTemplateRepo.On("Exists", ctx, testID).Return(true, nil).Once() 967 return appTemplateRepo 968 }, 969 WebhookRepoFn: UnusedWebhookRepo, 970 ExpectedOutput: true, 971 }, 972 { 973 Name: "Error when getting application template", 974 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 975 appTemplateRepo := &automock.ApplicationTemplateRepository{} 976 appTemplateRepo.On("Exists", ctx, testID).Return(false, testError).Once() 977 return appTemplateRepo 978 }, 979 WebhookRepoFn: UnusedWebhookRepo, 980 ExpectedError: testError, 981 ExpectedOutput: false, 982 }, 983 } 984 985 for _, testCase := range testCases { 986 t.Run(testCase.Name, func(t *testing.T) { 987 appTemplateRepo := testCase.AppTemplateRepoFn() 988 webhookRepo := testCase.WebhookRepoFn() 989 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, nil, nil, nil, nil) 990 991 // WHEN 992 result, err := svc.Exists(ctx, testID) 993 994 // THEN 995 if testCase.ExpectedError != nil { 996 require.Error(t, err) 997 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 998 } else { 999 assert.NoError(t, err) 1000 } 1001 assert.Equal(t, testCase.ExpectedOutput, result) 1002 1003 appTemplateRepo.AssertExpectations(t) 1004 }) 1005 } 1006 } 1007 1008 func TestService_List(t *testing.T) { 1009 // GIVEN 1010 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 1011 modelAppTemplate := fixModelAppTemplatePage([]*model.ApplicationTemplate{ 1012 fixModelApplicationTemplate("foo1", "bar1", fixModelApplicationTemplateWebhooks("webhook-id-1", "foo1")), 1013 fixModelApplicationTemplate("foo2", "bar2", fixModelApplicationTemplateWebhooks("webhook-id-2", "foo2")), 1014 }) 1015 labelFilters := []*labelfilter.LabelFilter{labelfilter.NewForKeyWithQuery(RegionKey, "eu-1")} 1016 1017 testCases := []struct { 1018 Name string 1019 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 1020 WebhookRepoFn func() *automock.WebhookRepository 1021 InputPageSize int 1022 ExpectedError error 1023 ExpectedOutput model.ApplicationTemplatePage 1024 }{ 1025 { 1026 Name: "Success", 1027 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1028 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1029 appTemplateRepo.On("List", ctx, labelFilters, 50, testCursor).Return(modelAppTemplate, nil).Once() 1030 return appTemplateRepo 1031 }, 1032 WebhookRepoFn: UnusedWebhookRepo, 1033 InputPageSize: 50, 1034 ExpectedOutput: modelAppTemplate, 1035 }, 1036 { 1037 Name: "Error when listing application template", 1038 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1039 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1040 appTemplateRepo.On("List", ctx, labelFilters, 50, testCursor).Return(model.ApplicationTemplatePage{}, testError).Once() 1041 return appTemplateRepo 1042 }, 1043 WebhookRepoFn: UnusedWebhookRepo, 1044 InputPageSize: 50, 1045 ExpectedError: testError, 1046 ExpectedOutput: model.ApplicationTemplatePage{}, 1047 }, 1048 { 1049 Name: "Error when page size too small", 1050 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1051 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1052 return appTemplateRepo 1053 }, 1054 WebhookRepoFn: UnusedWebhookRepo, 1055 InputPageSize: 0, 1056 ExpectedError: errors.New("page size must be between 1 and 200"), 1057 ExpectedOutput: model.ApplicationTemplatePage{}, 1058 }, 1059 { 1060 Name: "Error when page size too big", 1061 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1062 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1063 return appTemplateRepo 1064 }, 1065 WebhookRepoFn: UnusedWebhookRepo, 1066 InputPageSize: 201, 1067 ExpectedError: errors.New("page size must be between 1 and 200"), 1068 ExpectedOutput: model.ApplicationTemplatePage{}, 1069 }, 1070 } 1071 1072 for _, testCase := range testCases { 1073 t.Run(testCase.Name, func(t *testing.T) { 1074 appTemplateRepo := testCase.AppTemplateRepoFn() 1075 webhookRepo := testCase.WebhookRepoFn() 1076 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, nil, nil, nil, nil) 1077 1078 // WHEN 1079 result, err := svc.List(ctx, labelFilters, testCase.InputPageSize, testCursor) 1080 1081 // THEN 1082 if testCase.ExpectedError != nil { 1083 require.Error(t, err) 1084 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 1085 } else { 1086 assert.NoError(t, err) 1087 } 1088 assert.Equal(t, testCase.ExpectedOutput, result) 1089 1090 appTemplateRepo.AssertExpectations(t) 1091 }) 1092 } 1093 } 1094 1095 func TestService_Update(t *testing.T) { 1096 // GIVEN 1097 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 1098 appInputJSON := fmt.Sprintf(appInputJSONWithAppTypeLabelString, testName) 1099 appInputJSONWithNewAppType := fmt.Sprintf(appInputJSONWithAppTypeLabelString, updatedAppTemplateTestName) 1100 1101 modelAppTemplate := fixModelAppTemplateWithAppInputJSON(testID, testName, appInputJSON, nil) 1102 modelAppTemplateWithNewName := fixModelAppTemplateWithAppInputJSON(testID, updatedAppTemplateTestName, appInputJSONWithNewAppType, []*model.Webhook{}) 1103 modelAppTemplateOtherSystemType := fixModelAppTemplateWithAppInputJSON(testID, testNameOtherSystemType, appInputJSON, nil) 1104 modelAppTemplateWithLabels := fixModelAppTemplateWithAppInputJSONAndLabels(testID, testName, appInputJSON, []*model.Webhook{}, newTestLabels) 1105 modelAppTemplateUpdateInput := fixModelAppTemplateUpdateInputWithLabels(testName, appInputJSON, newTestLabels) 1106 1107 modelApplicationFromTemplate := fixModelApplication(testAppID, testAppName) 1108 modelLabelInput := fixLabelInput(testLabelInputKey, updatedAppTemplateTestName, testAppID, model.ApplicationLabelableObject) 1109 1110 testCases := []struct { 1111 Name string 1112 Input func() *model.ApplicationTemplateUpdateInput 1113 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 1114 WebhookRepoFn func() *automock.WebhookRepository 1115 LabelUpsertSvcFn func() *automock.LabelUpsertService 1116 LabelRepoFn func() *automock.LabelRepository 1117 AppRepoFn func() *automock.ApplicationRepository 1118 ExpectedError error 1119 }{ 1120 { 1121 Name: "Success", 1122 Input: func() *model.ApplicationTemplateUpdateInput { 1123 return modelAppTemplateUpdateInput 1124 }, 1125 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1126 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1127 appTemplateRepo.On("Get", ctx, modelAppTemplateWithLabels.ID).Return(modelAppTemplateWithLabels, nil).Once() 1128 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithLabels).Return(nil).Once() 1129 return appTemplateRepo 1130 }, 1131 WebhookRepoFn: func() *automock.WebhookRepository { 1132 webhookRepo := &automock.WebhookRepository{} 1133 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1134 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1135 return webhookRepo 1136 }, 1137 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1138 labelUpsertService := &automock.LabelUpsertService{} 1139 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, modelAppTemplateUpdateInput.Labels).Return(nil).Once() 1140 return labelUpsertService 1141 }, 1142 LabelRepoFn: func() *automock.LabelRepository { 1143 labelRepo := &automock.LabelRepository{} 1144 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1145 return labelRepo 1146 }, 1147 AppRepoFn: UnusedAppRepo, 1148 }, 1149 { 1150 Name: "Success - app input json without labels", 1151 Input: func() *model.ApplicationTemplateUpdateInput { 1152 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 1153 return fixModelAppTemplateUpdateInput(testName, appInputJSON) 1154 }, 1155 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1156 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1157 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1158 appTemplateRepo.On("Update", ctx, mock.AnythingOfType("model.ApplicationTemplate")).Return(nil).Once() 1159 return appTemplateRepo 1160 }, 1161 WebhookRepoFn: func() *automock.WebhookRepository { 1162 webhookRepo := &automock.WebhookRepository{} 1163 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1164 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1165 return webhookRepo 1166 }, 1167 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1168 labelUpsertService := &automock.LabelUpsertService{} 1169 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, mock.Anything).Return(nil).Once() 1170 return labelUpsertService 1171 }, 1172 LabelRepoFn: func() *automock.LabelRepository { 1173 labelRepo := &automock.LabelRepository{} 1174 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1175 return labelRepo 1176 }, 1177 AppRepoFn: UnusedAppRepo, 1178 }, 1179 { 1180 Name: "Success update of app template - no applications registered from the app template with changed name", 1181 Input: func() *model.ApplicationTemplateUpdateInput { 1182 return fixModelAppTemplateUpdateInput(updatedAppTemplateTestName, appInputJSONWithNewAppType) 1183 }, 1184 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1185 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1186 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1187 appTemplateRepo.On("ListByName", ctx, updatedAppTemplateTestName).Return([]*model.ApplicationTemplate{}, nil).Once() 1188 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithNewName).Return(nil).Once() 1189 return appTemplateRepo 1190 }, 1191 WebhookRepoFn: func() *automock.WebhookRepository { 1192 webhookRepo := &automock.WebhookRepository{} 1193 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1194 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1195 return webhookRepo 1196 }, 1197 LabelRepoFn: func() *automock.LabelRepository { 1198 labelRepo := &automock.LabelRepository{} 1199 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1200 return labelRepo 1201 }, 1202 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1203 labelUpsertService := &automock.LabelUpsertService{} 1204 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, mock.Anything).Return(nil).Once() 1205 return labelUpsertService 1206 }, 1207 AppRepoFn: func() *automock.ApplicationRepository { 1208 appRepo := &automock.ApplicationRepository{} 1209 appRepo.On("ListAllByApplicationTemplateID", ctx, testID).Return([]*model.Application{}, nil).Once() 1210 return appRepo 1211 }, 1212 }, 1213 { 1214 Name: "Error while listing applications from this app template when app template name is changed", 1215 Input: func() *model.ApplicationTemplateUpdateInput { 1216 return fixModelAppTemplateUpdateInput(updatedAppTemplateTestName, appInputJSONWithNewAppType) 1217 }, 1218 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1219 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1220 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1221 appTemplateRepo.On("ListByName", ctx, updatedAppTemplateTestName).Return([]*model.ApplicationTemplate{}, nil).Once() 1222 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithNewName).Return(nil).Once() 1223 return appTemplateRepo 1224 }, 1225 WebhookRepoFn: func() *automock.WebhookRepository { 1226 webhookRepo := &automock.WebhookRepository{} 1227 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1228 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1229 return webhookRepo 1230 }, 1231 LabelRepoFn: func() *automock.LabelRepository { 1232 labelRepo := &automock.LabelRepository{} 1233 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1234 return labelRepo 1235 }, 1236 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1237 labelUpsertService := &automock.LabelUpsertService{} 1238 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, mock.Anything).Return(nil).Once() 1239 return labelUpsertService 1240 }, 1241 AppRepoFn: func() *automock.ApplicationRepository { 1242 appRepo := &automock.ApplicationRepository{} 1243 appRepo.On("ListAllByApplicationTemplateID", ctx, testID).Return(nil, testError).Once() 1244 return appRepo 1245 }, 1246 ExpectedError: testError, 1247 }, 1248 { 1249 Name: "Success update of app template with changed name and the applicationType labels of applications registered from the app template", 1250 Input: func() *model.ApplicationTemplateUpdateInput { 1251 return fixModelAppTemplateUpdateInput(updatedAppTemplateTestName, appInputJSONWithNewAppType) 1252 }, 1253 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1254 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1255 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1256 appTemplateRepo.On("ListByName", ctx, updatedAppTemplateTestName).Return([]*model.ApplicationTemplate{}, nil).Once() 1257 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithNewName).Return(nil).Once() 1258 return appTemplateRepo 1259 }, 1260 WebhookRepoFn: func() *automock.WebhookRepository { 1261 webhookRepo := &automock.WebhookRepository{} 1262 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1263 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1264 return webhookRepo 1265 }, 1266 LabelRepoFn: func() *automock.LabelRepository { 1267 labelRepo := &automock.LabelRepository{} 1268 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1269 return labelRepo 1270 }, 1271 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1272 labelUpsertSvc := &automock.LabelUpsertService{} 1273 labelUpsertSvc.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, mock.Anything).Return(nil).Once() 1274 labelUpsertSvc.On("UpsertLabelGlobal", ctx, modelLabelInput).Return(nil).Once() 1275 return labelUpsertSvc 1276 }, 1277 AppRepoFn: func() *automock.ApplicationRepository { 1278 appRepo := &automock.ApplicationRepository{} 1279 appRepo.On("ListAllByApplicationTemplateID", ctx, testID).Return([]*model.Application{&modelApplicationFromTemplate}, nil).Once() 1280 return appRepo 1281 }, 1282 }, 1283 { 1284 Name: "Error while updating applicationType label of applications registered from the app template with changed name", 1285 Input: func() *model.ApplicationTemplateUpdateInput { 1286 return fixModelAppTemplateUpdateInput(updatedAppTemplateTestName, appInputJSONWithNewAppType) 1287 }, 1288 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1289 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1290 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1291 appTemplateRepo.On("ListByName", ctx, updatedAppTemplateTestName).Return([]*model.ApplicationTemplate{}, nil).Once() 1292 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithNewName).Return(nil).Once() 1293 return appTemplateRepo 1294 }, 1295 WebhookRepoFn: func() *automock.WebhookRepository { 1296 webhookRepo := &automock.WebhookRepository{} 1297 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1298 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1299 return webhookRepo 1300 }, 1301 LabelRepoFn: func() *automock.LabelRepository { 1302 labelRepo := &automock.LabelRepository{} 1303 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1304 return labelRepo 1305 }, 1306 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1307 labelUpsertSvc := &automock.LabelUpsertService{} 1308 labelUpsertSvc.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, mock.Anything).Return(nil).Once() 1309 labelUpsertSvc.On("UpsertLabelGlobal", ctx, modelLabelInput).Return(testError).Once() 1310 return labelUpsertSvc 1311 }, 1312 AppRepoFn: func() *automock.ApplicationRepository { 1313 appRepo := &automock.ApplicationRepository{} 1314 appRepo.On("ListAllByApplicationTemplateID", ctx, testID).Return([]*model.Application{&modelApplicationFromTemplate}, nil).Once() 1315 return appRepo 1316 }, 1317 ExpectedError: testError, 1318 }, 1319 { 1320 Name: "Error when getting application template", 1321 Input: func() *model.ApplicationTemplateUpdateInput { 1322 return fixModelAppTemplateUpdateInput(testName, appInputJSON) 1323 }, 1324 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1325 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1326 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(nil, testError).Once() 1327 return appTemplateRepo 1328 }, 1329 WebhookRepoFn: UnusedWebhookRepo, 1330 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1331 LabelRepoFn: UnusedLabelRepo, 1332 AppRepoFn: UnusedAppRepo, 1333 ExpectedError: testError, 1334 }, 1335 { 1336 Name: "Error when func enriching app input json with applicationType label - labels are not map[string]interface{}", 1337 Input: func() *model.ApplicationTemplateUpdateInput { 1338 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":123,"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 1339 return fixModelAppTemplateUpdateInput(testName, appInputJSON) 1340 }, 1341 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1342 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1343 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1344 return appTemplateRepo 1345 }, 1346 WebhookRepoFn: UnusedWebhookRepo, 1347 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1348 LabelRepoFn: func() *automock.LabelRepository { 1349 labelRepo := &automock.LabelRepository{} 1350 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(&model.Label{Value: "eu-1"}, nil).Once() 1351 return labelRepo 1352 }, 1353 AppRepoFn: UnusedAppRepo, 1354 ExpectedError: errors.New("app input json labels are type map[string]interface {} instead of map[string]interface{}"), 1355 }, 1356 { 1357 Name: "Error when func enriching app input json with applicationType label - application type is not string", 1358 Input: func() *model.ApplicationTemplateUpdateInput { 1359 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":{"applicationType":123,"test":["val","val2"]},"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 1360 return fixModelAppTemplateUpdateInput(testName, appInputJSON) 1361 }, 1362 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1363 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1364 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1365 return appTemplateRepo 1366 }, 1367 WebhookRepoFn: UnusedWebhookRepo, 1368 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1369 LabelRepoFn: func() *automock.LabelRepository { 1370 labelRepo := &automock.LabelRepository{} 1371 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(&model.Label{Value: "eu-1"}, nil).Once() 1372 return labelRepo 1373 }, 1374 AppRepoFn: UnusedAppRepo, 1375 ExpectedError: errors.New("\"applicationType\" label value must be string"), 1376 }, 1377 { 1378 Name: "Error when func enriching app input json with applicationType label - application type value does not match the application template name", 1379 Input: func() *model.ApplicationTemplateUpdateInput { 1380 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":{"applicationType":"random-text","test":["val","val2"]},"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 1381 return fixModelAppTemplateUpdateInput(testName, appInputJSON) 1382 }, 1383 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1384 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1385 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1386 return appTemplateRepo 1387 }, 1388 WebhookRepoFn: UnusedWebhookRepo, 1389 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1390 LabelRepoFn: func() *automock.LabelRepository { 1391 labelRepo := &automock.LabelRepository{} 1392 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(&model.Label{Value: "eu-1"}, nil).Once() 1393 return labelRepo 1394 }, 1395 AppRepoFn: UnusedAppRepo, 1396 ExpectedError: errors.New("\"applicationType\" label value does not match the application template name"), 1397 }, 1398 { 1399 Name: "No error in func enriching app input json with applicationType label when application type value does not match the application template name for Other System Type", 1400 Input: func() *model.ApplicationTemplateUpdateInput { 1401 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":{"applicationType":"random-text","test":["val","val2"]},"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 1402 return fixModelAppTemplateUpdateInputWithLabels(testNameOtherSystemType, appInputJSON, newTestLabels) 1403 }, 1404 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1405 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1406 appInputJSON := `{"name":"foo","providerName":"compass","description":"Lorem ipsum","labels":{"applicationType":"random-text","test":["val","val2"]},"healthCheckURL":"https://foo.bar","webhooks":[{"type":"","url":"webhook1.foo.bar","auth":null},{"type":"","url":"webhook2.foo.bar","auth":null}],"integrationSystemID":"iiiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii"}` 1407 1408 modelOtherSystemTypeUpdate := fixModelAppTemplateWithAppInputJSONAndLabels(testID, testNameOtherSystemType, appInputJSON, []*model.Webhook{}, newTestLabels) 1409 appTemplateRepo.On("Get", ctx, modelAppTemplateOtherSystemType.ID).Return(modelAppTemplateOtherSystemType, nil).Once() 1410 appTemplateRepo.On("Update", ctx, *modelOtherSystemTypeUpdate).Return(nil).Once() 1411 return appTemplateRepo 1412 }, 1413 WebhookRepoFn: func() *automock.WebhookRepository { 1414 webhookRepo := &automock.WebhookRepository{} 1415 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1416 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1417 return webhookRepo 1418 }, 1419 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1420 labelUpsertService := &automock.LabelUpsertService{} 1421 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, mock.Anything).Return(nil).Once() 1422 return labelUpsertService 1423 }, 1424 LabelRepoFn: func() *automock.LabelRepository { 1425 labelRepo := &automock.LabelRepository{} 1426 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(&model.Label{Value: "eu-1"}, nil).Once() 1427 1428 return labelRepo 1429 }, 1430 AppRepoFn: UnusedAppRepo, 1431 ExpectedError: nil, 1432 }, 1433 { 1434 Name: "Error when updating application template - retrieve region failed", 1435 Input: func() *model.ApplicationTemplateUpdateInput { 1436 return fixModelAppTemplateUpdateInput(testName+"test", appInputJSON) 1437 }, 1438 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1439 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1440 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1441 return appTemplateRepo 1442 }, 1443 WebhookRepoFn: UnusedWebhookRepo, 1444 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1445 LabelRepoFn: func() *automock.LabelRepository { 1446 labelRepo := &automock.LabelRepository{} 1447 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, testError).Once() 1448 return labelRepo 1449 }, 1450 AppRepoFn: UnusedAppRepo, 1451 ExpectedError: testError, 1452 }, 1453 { 1454 Name: "Error when updating application template - exists check failed", 1455 Input: func() *model.ApplicationTemplateUpdateInput { 1456 return fixModelAppTemplateUpdateInput(testName+"test", appInputJSONString) 1457 }, 1458 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1459 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1460 appTemplateRepo.On("ListByName", ctx, testName+"test").Return([]*model.ApplicationTemplate{modelAppTemplate}, nil).Once() 1461 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1462 return appTemplateRepo 1463 }, 1464 WebhookRepoFn: UnusedWebhookRepo, 1465 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1466 LabelRepoFn: func() *automock.LabelRepository { 1467 labelRepo := &automock.LabelRepository{} 1468 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1469 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, testError).Once() 1470 return labelRepo 1471 }, 1472 AppRepoFn: UnusedAppRepo, 1473 ExpectedError: testError, 1474 }, 1475 { 1476 Name: "Error when application template already exists", 1477 Input: func() *model.ApplicationTemplateUpdateInput { 1478 return fixModelAppTemplateUpdateInput(testName+"test", appInputJSONString) 1479 }, 1480 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1481 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1482 appTemplateRepo.On("ListByName", ctx, testName+"test").Return([]*model.ApplicationTemplate{modelAppTemplate}, nil).Once() 1483 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1484 return appTemplateRepo 1485 }, 1486 WebhookRepoFn: UnusedWebhookRepo, 1487 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1488 LabelRepoFn: func() *automock.LabelRepository { 1489 labelRepo := &automock.LabelRepository{} 1490 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1491 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1492 return labelRepo 1493 }, 1494 AppRepoFn: UnusedAppRepo, 1495 ExpectedError: errors.New("application template with name \"bartest\" and region <nil> already exists"), 1496 }, 1497 { 1498 Name: "Error when updating application template - update failed", 1499 Input: func() *model.ApplicationTemplateUpdateInput { 1500 return fixModelAppTemplateUpdateInputWithLabels(testName, appInputJSON, newTestLabels) 1501 }, 1502 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1503 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1504 appTemplateRepo.On("Get", ctx, modelAppTemplate.ID).Return(modelAppTemplate, nil).Once() 1505 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithLabels).Return(testError).Once() 1506 return appTemplateRepo 1507 }, 1508 WebhookRepoFn: UnusedWebhookRepo, 1509 LabelUpsertSvcFn: UnusedLabelUpsertSvc, 1510 LabelRepoFn: func() *automock.LabelRepository { 1511 labelRepo := &automock.LabelRepository{} 1512 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1513 return labelRepo 1514 }, 1515 AppRepoFn: UnusedAppRepo, 1516 ExpectedError: testError, 1517 }, 1518 { 1519 Name: "Error when updating labels of application template - upsert failed", 1520 Input: func() *model.ApplicationTemplateUpdateInput { 1521 return modelAppTemplateUpdateInput 1522 }, 1523 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1524 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1525 appTemplateRepo.On("Get", ctx, modelAppTemplateWithLabels.ID).Return(modelAppTemplateWithLabels, nil).Once() 1526 appTemplateRepo.On("Update", ctx, *modelAppTemplateWithLabels).Return(nil).Once() 1527 return appTemplateRepo 1528 }, 1529 WebhookRepoFn: func() *automock.WebhookRepository { 1530 webhookRepo := &automock.WebhookRepository{} 1531 webhookRepo.On("DeleteAllByApplicationTemplateID", ctx, modelAppTemplateWithLabels.ID).Return(nil).Once() 1532 webhookRepo.On("CreateMany", ctx, "", []*model.Webhook{}).Return(nil).Once() 1533 return webhookRepo 1534 }, 1535 LabelUpsertSvcFn: func() *automock.LabelUpsertService { 1536 labelUpsertService := &automock.LabelUpsertService{} 1537 labelUpsertService.On("UpsertMultipleLabels", ctx, "", model.AppTemplateLabelableObject, testID, modelAppTemplateUpdateInput.Labels).Return(testError).Once() 1538 return labelUpsertService 1539 }, 1540 LabelRepoFn: func() *automock.LabelRepository { 1541 labelRepo := &automock.LabelRepository{} 1542 labelRepo.On("GetByKey", ctx, "", model.AppTemplateLabelableObject, modelAppTemplate.ID, "region").Return(nil, apperrors.NewNotFoundError(resource.Label, "id")).Once() 1543 return labelRepo 1544 }, 1545 AppRepoFn: UnusedAppRepo, 1546 ExpectedError: testError, 1547 }, 1548 } 1549 1550 for _, testCase := range testCases { 1551 t.Run(testCase.Name, func(t *testing.T) { 1552 appTemplateRepo := testCase.AppTemplateRepoFn() 1553 webhookRepo := testCase.WebhookRepoFn() 1554 labelRepo := testCase.LabelRepoFn() 1555 labelUpsertService := testCase.LabelUpsertSvcFn() 1556 appRepo := testCase.AppRepoFn() 1557 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, nil, labelUpsertService, labelRepo, appRepo) 1558 1559 // WHEN 1560 err := svc.Update(ctx, testID, *testCase.Input()) 1561 1562 // THEN 1563 if testCase.ExpectedError != nil { 1564 require.Error(t, err) 1565 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 1566 } else { 1567 assert.NoError(t, err) 1568 } 1569 1570 appTemplateRepo.AssertExpectations(t) 1571 labelRepo.AssertExpectations(t) 1572 labelUpsertService.AssertExpectations(t) 1573 appRepo.AssertExpectations(t) 1574 }) 1575 } 1576 } 1577 1578 func TestService_Delete(t *testing.T) { 1579 // GIVEN 1580 ctx := tenant.SaveToContext(context.TODO(), testTenant, testExternalTenant) 1581 1582 testCases := []struct { 1583 Name string 1584 AppTemplateRepoFn func() *automock.ApplicationTemplateRepository 1585 WebhookRepoFn func() *automock.WebhookRepository 1586 ExpectedError error 1587 }{ 1588 { 1589 Name: "Success", 1590 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1591 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1592 appTemplateRepo.On("Delete", ctx, testID).Return(nil).Once() 1593 return appTemplateRepo 1594 }, 1595 WebhookRepoFn: UnusedWebhookRepo, 1596 }, 1597 { 1598 Name: "Error when deleting application template", 1599 AppTemplateRepoFn: func() *automock.ApplicationTemplateRepository { 1600 appTemplateRepo := &automock.ApplicationTemplateRepository{} 1601 appTemplateRepo.On("Delete", ctx, testID).Return(testError).Once() 1602 return appTemplateRepo 1603 }, 1604 WebhookRepoFn: UnusedWebhookRepo, 1605 ExpectedError: testError, 1606 }, 1607 } 1608 1609 for _, testCase := range testCases { 1610 t.Run(testCase.Name, func(t *testing.T) { 1611 appTemplateRepo := testCase.AppTemplateRepoFn() 1612 webhookRepo := testCase.WebhookRepoFn() 1613 svc := apptemplate.NewService(appTemplateRepo, webhookRepo, nil, nil, nil, nil) 1614 1615 // WHEN 1616 err := svc.Delete(ctx, testID) 1617 1618 // THEN 1619 if testCase.ExpectedError != nil { 1620 require.Error(t, err) 1621 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 1622 } else { 1623 assert.NoError(t, err) 1624 } 1625 1626 appTemplateRepo.AssertExpectations(t) 1627 }) 1628 } 1629 } 1630 1631 func TestService_PrepareApplicationCreateInputJSON(t *testing.T) { 1632 // GIVEN 1633 svc := apptemplate.NewService(nil, nil, nil, nil, nil, nil) 1634 placeholderNotOptional := false 1635 placeholderIsOptional := true 1636 1637 testCases := []struct { 1638 Name string 1639 InputAppTemplate *model.ApplicationTemplate 1640 InputValues model.ApplicationFromTemplateInputValues 1641 ExpectedOutput string 1642 ExpectedError error 1643 }{ 1644 { 1645 Name: "Success when no placeholders", 1646 InputAppTemplate: &model.ApplicationTemplate{ 1647 ApplicationInputJSON: `{"Name": "my-app", "Description": "Lorem ipsum"}`, 1648 Placeholders: nil, 1649 }, 1650 InputValues: nil, 1651 ExpectedOutput: `{"Name": "my-app", "Description": "Lorem ipsum"}`, 1652 ExpectedError: nil, 1653 }, 1654 { 1655 Name: "Success when with placeholders", 1656 InputAppTemplate: &model.ApplicationTemplate{ 1657 ApplicationInputJSON: `{"Name": "{{name}}", "Description": "Lorem ipsum"}`, 1658 Placeholders: []model.ApplicationTemplatePlaceholder{ 1659 {Name: "name", Description: str.Ptr("Application name"), JSONPath: str.Ptr("displayName"), Optional: &placeholderNotOptional}, 1660 }, 1661 }, 1662 InputValues: []*model.ApplicationTemplateValueInput{ 1663 {Placeholder: "name", Value: "my-application"}, 1664 }, 1665 ExpectedOutput: `{"Name": "my-application", "Description": "Lorem ipsum"}`, 1666 ExpectedError: nil, 1667 }, 1668 { 1669 Name: "Success when optional placeholder is not provided on root level", 1670 InputAppTemplate: &model.ApplicationTemplate{ 1671 ApplicationInputJSON: `{"Name": "{{Name}}", "Description": "Lorem ipsum"}`, 1672 Placeholders: []model.ApplicationTemplatePlaceholder{ 1673 {Name: "Name", Description: str.Ptr("Application name"), JSONPath: str.Ptr("displayName"), Optional: &placeholderIsOptional}, 1674 }, 1675 }, 1676 InputValues: []*model.ApplicationTemplateValueInput{}, 1677 ExpectedOutput: `{"Name":"","Description":"Lorem ipsum"}`, 1678 ExpectedError: nil, 1679 }, 1680 { 1681 Name: "Success when optional placeholder is not provided in labels", 1682 InputAppTemplate: &model.ApplicationTemplate{ 1683 ApplicationInputJSON: `{"Name": "{{Name}}", "labels": {"label1":"label1" ,"optionalLabel": "{{optionalLabel}}"}, "Description": "Lorem ipsum"}`, 1684 Placeholders: []model.ApplicationTemplatePlaceholder{ 1685 {Name: "Name", Description: str.Ptr("Application name"), JSONPath: str.Ptr("displayName"), Optional: &placeholderIsOptional}, 1686 {Name: "optionalLabel", Description: str.Ptr("optionalLabel description"), JSONPath: str.Ptr("optionalLabel"), Optional: &placeholderIsOptional}, 1687 }, 1688 }, 1689 InputValues: []*model.ApplicationTemplateValueInput{}, 1690 ExpectedOutput: `{"Name":"","Description":"Lorem ipsum", "labels":{"label1":"label1"}}`, 1691 ExpectedError: nil, 1692 }, 1693 { 1694 Name: "Success when optional placeholder value in labels is empty string", 1695 InputAppTemplate: &model.ApplicationTemplate{ 1696 ApplicationInputJSON: `{"labels": {"label1":"label1" ,"nonOptionalLabel": "{{nonOptionalLabel}}"}, "Description": "Lorem ipsum"}`, 1697 Placeholders: []model.ApplicationTemplatePlaceholder{ 1698 {Name: "nonOptionalLabel", Description: str.Ptr("nonOptionalLabel description"), JSONPath: str.Ptr("nonOptionalLabel"), Optional: &placeholderNotOptional}, 1699 }, 1700 }, 1701 InputValues: []*model.ApplicationTemplateValueInput{ 1702 {Placeholder: "nonOptionalLabel", Value: ""}, 1703 }, 1704 ExpectedOutput: `{"Description":"Lorem ipsum","labels":{"label1":"label1"}}`, 1705 ExpectedError: nil, 1706 }, 1707 { 1708 Name: "Returns error when app input is invalid JSON", 1709 InputAppTemplate: &model.ApplicationTemplate{ 1710 ApplicationInputJSON: `{{}`, 1711 Placeholders: []model.ApplicationTemplatePlaceholder{ 1712 {Name: "nonOptionalLabel", Description: str.Ptr("nonOptionalLabel description"), JSONPath: str.Ptr("nonOptionalLabel"), Optional: &placeholderNotOptional}, 1713 }, 1714 }, 1715 InputValues: []*model.ApplicationTemplateValueInput{ 1716 {Placeholder: "nonOptionalLabel", Value: ""}, 1717 }, 1718 ExpectedOutput: "", 1719 ExpectedError: errors.New("error while clear optional empty value"), 1720 }, 1721 { 1722 Name: "Returns error when required placeholder value not provided in labels", 1723 InputAppTemplate: &model.ApplicationTemplate{ 1724 ApplicationInputJSON: `{"labels": {"label1":"label1" ,"nonOptionalLabel": "{{nonOptionalLabel}}"}, "Description": "Lorem ipsum"}`, 1725 Placeholders: []model.ApplicationTemplatePlaceholder{ 1726 {Name: "nonOptionalLabel", Description: str.Ptr("nonOptionalLabel description"), JSONPath: str.Ptr("nonOptionalLabel"), Optional: &placeholderNotOptional}, 1727 }, 1728 }, 1729 InputValues: []*model.ApplicationTemplateValueInput{}, 1730 ExpectedOutput: "", 1731 ExpectedError: errors.New("required placeholder not provided: value for placeholder name 'nonOptionalLabel' not found"), 1732 }, 1733 { 1734 Name: "Returns error when required placeholder value not provided on root level", 1735 InputAppTemplate: &model.ApplicationTemplate{ 1736 ApplicationInputJSON: `{"Name": "{{name}}", "Description": "Lorem ipsum"}`, 1737 Placeholders: []model.ApplicationTemplatePlaceholder{ 1738 {Name: "name", Description: str.Ptr("Application name"), JSONPath: str.Ptr("displayName"), Optional: &placeholderNotOptional}, 1739 }, 1740 }, 1741 InputValues: []*model.ApplicationTemplateValueInput{}, 1742 ExpectedOutput: "", 1743 ExpectedError: errors.New("required placeholder not provided: value for placeholder name 'name' not found"), 1744 }, 1745 { 1746 Name: "Returns error when provider placeholder value contains \"SAP\"", 1747 InputAppTemplate: &model.ApplicationTemplate{ 1748 ApplicationInputJSON: `{"Provider": "{{provider}}", "Description": "Lorem ipsum"}`, 1749 Placeholders: []model.ApplicationTemplatePlaceholder{ 1750 {Name: "provider", Description: str.Ptr("Application's provider"), JSONPath: str.Ptr("provider"), Optional: &placeholderNotOptional}, 1751 }, 1752 }, 1753 InputValues: []*model.ApplicationTemplateValueInput{ 1754 {Placeholder: "provider", Value: "invalid SAP provider"}, 1755 }, 1756 ExpectedOutput: "", 1757 ExpectedError: errors.New("value of placeholder is invalid: provider cannot contain \"SAP\""), 1758 }, 1759 { 1760 Name: "Returns error when application type placeholder value starts with \"SAP\"", 1761 InputAppTemplate: &model.ApplicationTemplate{ 1762 ApplicationInputJSON: `{"Application type": "{{application-type}}", "Description": "Lorem ipsum"}`, 1763 Placeholders: []model.ApplicationTemplatePlaceholder{ 1764 {Name: "application-type", Description: str.Ptr("Application type"), JSONPath: str.Ptr("application-type"), Optional: &placeholderNotOptional}, 1765 }, 1766 }, 1767 InputValues: []*model.ApplicationTemplateValueInput{ 1768 {Placeholder: "application-type", Value: "SAP type"}, 1769 }, 1770 ExpectedOutput: "", 1771 ExpectedError: errors.New("value of placeholder is invalid: your application type cannot start with \"SAP\""), 1772 }, 1773 } 1774 1775 for _, testCase := range testCases { 1776 t.Run(testCase.Name, func(t *testing.T) { 1777 // WHEN 1778 result, err := svc.PrepareApplicationCreateInputJSON(testCase.InputAppTemplate, testCase.InputValues) 1779 1780 // THEN 1781 if testCase.ExpectedError != nil { 1782 require.Error(t, err) 1783 assert.Empty(t, result) 1784 assert.Contains(t, err.Error(), testCase.ExpectedError.Error()) 1785 } else { 1786 require.NoError(t, err) 1787 assert.JSONEq(t, testCase.ExpectedOutput, result) 1788 } 1789 }) 1790 } 1791 } 1792 1793 func UnusedLabelRepo() *automock.LabelRepository { 1794 return &automock.LabelRepository{} 1795 } 1796 1797 func UnusedWebhookRepo() *automock.WebhookRepository { 1798 return &automock.WebhookRepository{} 1799 } 1800 1801 func UnusedLabelUpsertSvc() *automock.LabelUpsertService { 1802 return &automock.LabelUpsertService{} 1803 } 1804 1805 func UnusedAppTemplateRepo() *automock.ApplicationTemplateRepository { 1806 return &automock.ApplicationTemplateRepository{} 1807 } 1808 1809 func UnusedAppRepo() *automock.ApplicationRepository { 1810 return &automock.ApplicationRepository{} 1811 }