github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/fetchrequest/service_test.go (about) 1 package fetchrequest_test 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "net/http" 10 "testing" 11 "time" 12 13 "github.com/kyma-incubator/compass/components/director/pkg/retry" 14 15 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 16 "github.com/kyma-incubator/compass/components/director/pkg/certloader" 17 18 "github.com/kyma-incubator/compass/components/director/pkg/accessstrategy" 19 accessstrategyautomock "github.com/kyma-incubator/compass/components/director/pkg/accessstrategy/automock" 20 21 "github.com/kyma-incubator/compass/components/director/pkg/str" 22 "github.com/stretchr/testify/mock" 23 24 "github.com/kyma-incubator/compass/components/director/internal/domain/fetchrequest/automock" 25 26 "github.com/kyma-incubator/compass/components/director/internal/domain/fetchrequest" 27 28 "github.com/kyma-incubator/compass/components/director/internal/model" 29 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 30 "github.com/stretchr/testify/assert" 31 ) 32 33 const ( 34 externalClientCertSecretName = "resource-name1" 35 extSvcClientCertSecretName = "resource-name2" 36 ) 37 38 var testErr = errors.New("test") 39 40 type RoundTripFunc func(req *http.Request) *http.Response 41 42 func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { 43 response := f(req) 44 if response.StatusCode == http.StatusBadRequest { 45 return nil, errors.New("error") 46 } 47 return response, nil 48 } 49 50 func NewTestClient(fn RoundTripFunc) *http.Client { 51 return &http.Client{ 52 Transport: fn, 53 } 54 } 55 56 func TestService_Update(t *testing.T) { 57 fetchReq := &model.FetchRequest{ 58 ID: "test", 59 Mode: model.FetchModeSingle, 60 } 61 62 testCases := []struct { 63 Name string 64 Context context.Context 65 FetchRequest *model.FetchRequest 66 FetchRequestRepoMock *automock.FetchRequestRepository 67 ExpectedError error 68 }{ 69 70 { 71 Name: "Success", 72 Context: tenant.SaveToContext(context.TODO(), tenantID, tenantID), 73 FetchRequest: fetchReq, 74 FetchRequestRepoMock: func() *automock.FetchRequestRepository { 75 fetchReqRepoMock := automock.FetchRequestRepository{} 76 fetchReqRepoMock.On("Update", mock.Anything, tenantID, fetchReq).Return(nil).Once() 77 return &fetchReqRepoMock 78 }(), 79 ExpectedError: nil, 80 }, 81 { 82 Name: "Fails when tenant is missing in context", 83 Context: context.TODO(), 84 FetchRequest: fetchReq, 85 FetchRequestRepoMock: &automock.FetchRequestRepository{}, 86 ExpectedError: apperrors.NewCannotReadTenantError(), 87 }, 88 { 89 Name: "Fails when repo update fails", 90 Context: tenant.SaveToContext(context.TODO(), tenantID, tenantID), 91 FetchRequest: fetchReq, 92 FetchRequestRepoMock: func() *automock.FetchRequestRepository { 93 fetchReqRepoMock := automock.FetchRequestRepository{} 94 fetchReqRepoMock.On("Update", mock.Anything, tenantID, fetchReq).Return(testErr).Once() 95 return &fetchReqRepoMock 96 }(), 97 ExpectedError: testErr, 98 }, 99 } 100 101 for _, testCase := range testCases { 102 t.Run(testCase.Name, func(t *testing.T) { 103 repo := testCase.FetchRequestRepoMock 104 svc := fetchrequest.NewService(repo, nil, nil) 105 resultErr := svc.Update(testCase.Context, testCase.FetchRequest) 106 assert.Equal(t, testCase.ExpectedError, resultErr) 107 108 repo.AssertExpectations(t) 109 }) 110 } 111 } 112 113 func TestService_UpdateGlobal(t *testing.T) { 114 fetchReq := &model.FetchRequest{ 115 ID: "test", 116 Mode: model.FetchModeSingle, 117 } 118 119 testCases := []struct { 120 Name string 121 FetchRequest *model.FetchRequest 122 FetchRequestRepoMock *automock.FetchRequestRepository 123 ExpectedError error 124 }{ 125 126 { 127 Name: "Success", 128 FetchRequest: fetchReq, 129 FetchRequestRepoMock: func() *automock.FetchRequestRepository { 130 fetchReqRepoMock := automock.FetchRequestRepository{} 131 fetchReqRepoMock.On("UpdateGlobal", mock.Anything, fetchReq).Return(nil).Once() 132 return &fetchReqRepoMock 133 }(), 134 ExpectedError: nil, 135 }, 136 { 137 Name: "Fails when repo update fails", 138 FetchRequest: fetchReq, 139 FetchRequestRepoMock: func() *automock.FetchRequestRepository { 140 fetchReqRepoMock := automock.FetchRequestRepository{} 141 fetchReqRepoMock.On("UpdateGlobal", mock.Anything, fetchReq).Return(testErr).Once() 142 return &fetchReqRepoMock 143 }(), 144 ExpectedError: testErr, 145 }, 146 } 147 148 for _, testCase := range testCases { 149 t.Run(testCase.Name, func(t *testing.T) { 150 repo := testCase.FetchRequestRepoMock 151 svc := fetchrequest.NewService(repo, nil, nil) 152 resultErr := svc.UpdateGlobal(context.TODO(), testCase.FetchRequest) 153 assert.Equal(t, testCase.ExpectedError, resultErr) 154 155 repo.AssertExpectations(t) 156 }) 157 } 158 } 159 160 func TestService_HandleSpec(t *testing.T) { 161 const username = "username" 162 const password = "password" 163 const clientID = "clId" 164 const secret = "clSecret" 165 const url = "mocked-url/oauth/token" 166 167 var testAccessStrategy = "testAccessStrategy" 168 169 mockSpec := "spec" 170 timestamp := time.Now() 171 172 modelInput := model.FetchRequest{ 173 ID: "test", 174 Mode: model.FetchModeSingle, 175 } 176 177 modelInputBundle := model.FetchRequest{ 178 ID: "test", 179 Mode: model.FetchModeBundle, 180 } 181 182 modelInputFilter := model.FetchRequest{ 183 ID: "test", 184 Mode: model.FetchModeSingle, 185 Filter: str.Ptr("filter"), 186 } 187 188 modelInputAccessStrategy := model.FetchRequest{ 189 ID: "test", 190 Mode: model.FetchModeSingle, 191 URL: "http://test.com", 192 Auth: &model.Auth{AccessStrategy: &testAccessStrategy}, 193 } 194 195 modelInputBasicCredentials := model.FetchRequest{ 196 ID: "test", 197 Auth: &model.Auth{ 198 Credential: model.CredentialData{ 199 Basic: &model.BasicCredentialData{ 200 Username: username, 201 Password: password, 202 }, 203 }, 204 }, 205 Mode: model.FetchModeSingle, 206 } 207 208 modelInputMissingCredentials := model.FetchRequest{ 209 ID: "test", 210 Auth: &model.Auth{ 211 Credential: model.CredentialData{ 212 Basic: nil, 213 Oauth: nil, 214 }, 215 }, 216 Mode: model.FetchModeSingle, 217 } 218 219 modelInputOauth := model.FetchRequest{ 220 ID: "test", 221 URL: "http://dummy.url.sth", 222 Auth: &model.Auth{ 223 Credential: model.CredentialData{ 224 Basic: nil, 225 Oauth: &model.OAuthCredentialData{ 226 ClientID: clientID, 227 ClientSecret: secret, 228 URL: url, 229 }, 230 }, 231 }, 232 Mode: model.FetchModeSingle, 233 } 234 235 testCases := []struct { 236 Name string 237 Client func(t *testing.T) *http.Client 238 localTenantID string 239 InputFr model.FetchRequest 240 ExecutorProviderFunc func() accessstrategy.ExecutorProvider 241 ExpectedResult *string 242 ExpectedStatus *model.FetchRequestStatus 243 }{ 244 245 { 246 Name: "Success without authentication", 247 Client: func(t *testing.T) *http.Client { 248 return NewTestClient(func(req *http.Request) *http.Response { 249 return &http.Response{ 250 StatusCode: http.StatusOK, 251 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 252 } 253 }) 254 }, 255 InputFr: modelInput, 256 localTenantID: localTenantID, 257 ExpectedResult: &mockSpec, 258 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp), 259 }, 260 { 261 Name: "Success when local tenant id is missing", 262 Client: func(t *testing.T) *http.Client { 263 return NewTestClient(func(req *http.Request) *http.Response { 264 return &http.Response{ 265 StatusCode: http.StatusOK, 266 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 267 } 268 }) 269 }, 270 InputFr: modelInput, 271 ExpectedResult: &mockSpec, 272 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp), 273 }, 274 { 275 Name: "Nil when fetch request validation fails due to mode Bundle", 276 Client: func(t *testing.T) *http.Client { 277 return NewTestClient(func(req *http.Request) *http.Response { 278 return &http.Response{} 279 }) 280 }, 281 282 InputFr: modelInputBundle, 283 localTenantID: localTenantID, 284 ExpectedResult: nil, 285 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionInitial, str.Ptr("Invalid data [reason=Unsupported fetch mode: BUNDLE]"), timestamp), 286 }, 287 { 288 Name: "Nil when fetch request validation fails due to provided filter", 289 Client: func(t *testing.T) *http.Client { 290 return NewTestClient(func(req *http.Request) *http.Response { 291 return &http.Response{} 292 }) 293 }, 294 295 InputFr: modelInputFilter, 296 localTenantID: localTenantID, 297 ExpectedResult: nil, 298 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionInitial, str.Ptr("Invalid data [reason=Filter for Fetch Request was provided, currently it's unsupported]"), timestamp), 299 }, 300 { 301 Name: "Success with access strategy", 302 ExecutorProviderFunc: func() accessstrategy.ExecutorProvider { 303 executor := &accessstrategyautomock.Executor{} 304 executor.On("Execute", mock.Anything, mock.Anything, modelInputAccessStrategy.URL, localTenantID).Return(&http.Response{ 305 StatusCode: http.StatusOK, 306 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 307 }, nil).Once() 308 309 executorProvider := &accessstrategyautomock.ExecutorProvider{} 310 executorProvider.On("Provide", accessstrategy.Type(testAccessStrategy)).Return(executor, nil).Once() 311 return executorProvider 312 }, 313 Client: func(t *testing.T) *http.Client { 314 return nil 315 }, 316 InputFr: modelInputAccessStrategy, 317 localTenantID: localTenantID, 318 ExpectedResult: &mockSpec, 319 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp), 320 }, 321 { 322 Name: "Fails when access strategy is unknown", 323 ExecutorProviderFunc: func() accessstrategy.ExecutorProvider { 324 executorProvider := &accessstrategyautomock.ExecutorProvider{} 325 executorProvider.On("Provide", accessstrategy.Type(testAccessStrategy)).Return(nil, testErr).Once() 326 return executorProvider 327 }, 328 Client: func(t *testing.T) *http.Client { 329 return nil 330 }, 331 InputFr: modelInputAccessStrategy, 332 localTenantID: localTenantID, 333 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr("While fetching Spec: test"), timestamp), 334 }, 335 { 336 Name: "Fails when access strategy execution fail", 337 ExecutorProviderFunc: func() accessstrategy.ExecutorProvider { 338 executor := &accessstrategyautomock.Executor{} 339 executor.On("Execute", mock.Anything, mock.Anything, modelInputAccessStrategy.URL, localTenantID).Return(nil, testErr).Once() 340 341 executorProvider := &accessstrategyautomock.ExecutorProvider{} 342 executorProvider.On("Provide", accessstrategy.Type(testAccessStrategy)).Return(executor, nil).Once() 343 return executorProvider 344 }, 345 Client: func(t *testing.T) *http.Client { 346 return nil 347 }, 348 InputFr: modelInputAccessStrategy, 349 localTenantID: localTenantID, 350 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr("While fetching Spec: test"), timestamp), 351 }, 352 { 353 Name: "Success with basic authentication", 354 Client: func(t *testing.T) *http.Client { 355 return NewTestClient(func(req *http.Request) *http.Response { 356 actualUsername, actualPassword, ok := req.BasicAuth() 357 assert.True(t, ok) 358 assert.Equal(t, username, actualUsername) 359 assert.Equal(t, password, actualPassword) 360 return &http.Response{ 361 StatusCode: http.StatusOK, 362 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 363 } 364 }) 365 }, 366 InputFr: modelInputBasicCredentials, 367 localTenantID: localTenantID, 368 ExpectedResult: &mockSpec, 369 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp), 370 }, 371 { 372 Name: "Fails to execute the request with basic authentication", 373 Client: func(t *testing.T) *http.Client { 374 return NewTestClient(func(req *http.Request) *http.Response { 375 actualUsername, actualPassword, ok := req.BasicAuth() 376 assert.True(t, ok) 377 assert.Equal(t, username, actualUsername) 378 assert.Equal(t, password, actualPassword) 379 return &http.Response{ 380 StatusCode: http.StatusInternalServerError, 381 } 382 }) 383 }, 384 InputFr: modelInputBasicCredentials, 385 localTenantID: localTenantID, 386 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr("While fetching Spec status code: 500"), timestamp), 387 }, 388 { 389 Name: "Nil when auth without credentials is provided", 390 Client: func(t *testing.T) *http.Client { 391 return NewTestClient(func(req *http.Request) *http.Response { 392 return &http.Response{} 393 }) 394 }, 395 396 InputFr: modelInputMissingCredentials, 397 localTenantID: localTenantID, 398 ExpectedResult: nil, 399 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr("While fetching Spec: Invalid data [reason=Credentials not provided]"), timestamp), 400 }, 401 { 402 Name: "Success with oauth authentication", 403 Client: func(t *testing.T) *http.Client { 404 return NewTestClient(func(req *http.Request) *http.Response { 405 if req.URL.String() == url { 406 actualClientID, actualSecret, ok := req.BasicAuth() 407 assert.True(t, ok) 408 assert.Equal(t, clientID, actualClientID) 409 assert.Equal(t, secret, actualSecret) 410 return &http.Response{ 411 StatusCode: http.StatusOK, 412 Body: io.NopCloser(bytes.NewBufferString(`{"access_token":"token"}`)), 413 } 414 } 415 416 return &http.Response{ 417 StatusCode: http.StatusOK, 418 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 419 } 420 }) 421 }, 422 InputFr: modelInputOauth, 423 localTenantID: localTenantID, 424 ExpectedResult: &mockSpec, 425 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp), 426 }, 427 { 428 Name: "Fails to fetch oauth token with oauth authentication", 429 Client: func(t *testing.T) *http.Client { 430 return NewTestClient(func(req *http.Request) *http.Response { 431 actualClientID, actualSecret, ok := req.BasicAuth() 432 if ok { 433 assert.Equal(t, clientID, actualClientID) 434 assert.Equal(t, secret, actualSecret) 435 } else { 436 credentials, err := io.ReadAll(req.Body) 437 assert.NoError(t, err) 438 assert.Contains(t, string(credentials), fmt.Sprintf("client_id=%s&client_secret=%s&grant_type=client_credentials", clientID, secret)) 439 } 440 return &http.Response{ 441 StatusCode: http.StatusInternalServerError, 442 } 443 }) 444 }, 445 InputFr: modelInputOauth, 446 localTenantID: localTenantID, 447 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr("While fetching Spec: Get \"http://dummy.url.sth\": oauth2: cannot fetch token: \nResponse: "), timestamp), 448 }, 449 { 450 Name: "Fails to execute the request with oauth authentication", 451 Client: func(t *testing.T) *http.Client { 452 return NewTestClient(func(req *http.Request) *http.Response { 453 if req.URL.String() == url { 454 actualClientID, actualSecret, ok := req.BasicAuth() 455 assert.True(t, ok) 456 assert.Equal(t, clientID, actualClientID) 457 assert.Equal(t, secret, actualSecret) 458 return &http.Response{ 459 StatusCode: http.StatusOK, 460 Body: io.NopCloser(bytes.NewBufferString(`{"access_token":"token"}`)), 461 } 462 } 463 464 return &http.Response{ 465 StatusCode: http.StatusInternalServerError, 466 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 467 } 468 }) 469 }, 470 InputFr: modelInputOauth, 471 localTenantID: localTenantID, 472 ExpectedStatus: fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr("While fetching Spec status code: 500"), timestamp), 473 }, 474 } 475 476 for _, testCase := range testCases { 477 t.Run(testCase.Name, func(t *testing.T) { 478 certCache := certloader.NewCertificateCache() 479 var executorProviderMock accessstrategy.ExecutorProvider = accessstrategy.NewDefaultExecutorProvider(certCache, externalClientCertSecretName, extSvcClientCertSecretName) 480 if testCase.ExecutorProviderFunc != nil { 481 executorProviderMock = testCase.ExecutorProviderFunc() 482 } 483 484 ctx := context.TODO() 485 ctx = tenant.SaveToContext(ctx, tenantID, tenantID) 486 ctx = tenant.SaveLocalTenantIDToContext(ctx, testCase.localTenantID) 487 488 frRepo := &automock.FetchRequestRepository{} 489 frRepo.On("Update", ctx, tenantID, mock.Anything).Return(nil).Once() 490 491 svc := fetchrequest.NewService(frRepo, testCase.Client(t), executorProviderMock) 492 svc.SetTimestampGen(func() time.Time { return timestamp }) 493 494 result := svc.HandleSpec(ctx, &testCase.InputFr) 495 496 assert.Equal(t, testCase.ExpectedStatus, testCase.InputFr.Status) 497 assert.Equal(t, testCase.ExpectedResult, result) 498 499 if testCase.ExecutorProviderFunc != nil { 500 mock.AssertExpectationsForObjects(t, executorProviderMock) 501 } 502 }) 503 } 504 } 505 506 func TestService_HandleSpec_FailedToUpdateStatusAfterFetching(t *testing.T) { 507 ctx := context.TODO() 508 ctx = tenant.SaveToContext(ctx, tenantID, tenantID) 509 ctx = tenant.SaveLocalTenantIDToContext(ctx, localTenantID) 510 511 timestamp := time.Now() 512 frRepo := &automock.FetchRequestRepository{} 513 frRepo.On("Update", ctx, tenantID, mock.Anything).Return(errors.New("error")).Once() 514 515 certCache := certloader.NewCertificateCache() 516 svc := fetchrequest.NewService(frRepo, NewTestClient(func(req *http.Request) *http.Response { 517 return &http.Response{ 518 StatusCode: http.StatusOK, 519 Body: io.NopCloser(bytes.NewBufferString("spec")), 520 } 521 }), accessstrategy.NewDefaultExecutorProvider(certCache, externalClientCertSecretName, extSvcClientCertSecretName)) 522 svc.SetTimestampGen(func() time.Time { return timestamp }) 523 524 modelInput := &model.FetchRequest{ 525 ID: "test", 526 Mode: model.FetchModeSingle, 527 } 528 529 result := svc.HandleSpec(ctx, modelInput) 530 expectedStatus := fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp) 531 532 assert.Equal(t, expectedStatus, modelInput.Status) 533 assert.Nil(t, result) 534 } 535 536 func TestService_HandleSpec_SucceedsAfterRetryMechanismIsLeveraged(t *testing.T) { 537 ctx := context.TODO() 538 ctx = tenant.SaveToContext(ctx, tenantID, tenantID) 539 ctx = tenant.SaveLocalTenantIDToContext(ctx, localTenantID) 540 541 timestamp := time.Now() 542 frRepo := &automock.FetchRequestRepository{} 543 frRepo.On("Update", ctx, tenantID, mock.Anything).Return(nil).Once() 544 545 certCache := certloader.NewCertificateCache() 546 retryConfig := &retry.Config{ 547 Attempts: 3, 548 Delay: 100 * time.Millisecond, 549 } 550 551 mockSpec := "spec" 552 553 invocations := 0 554 svc := fetchrequest.NewServiceWithRetry(frRepo, NewTestClient(func(req *http.Request) *http.Response { 555 defer func() { 556 invocations++ 557 }() 558 559 if invocations != int(retryConfig.Attempts)-1 { 560 return &http.Response{StatusCode: http.StatusInternalServerError} 561 } 562 563 return &http.Response{ 564 StatusCode: http.StatusOK, 565 Body: io.NopCloser(bytes.NewBufferString(mockSpec)), 566 } 567 }), accessstrategy.NewDefaultExecutorProvider(certCache, externalClientCertSecretName, extSvcClientCertSecretName), retry.NewHTTPExecutor(retryConfig)) 568 svc.SetTimestampGen(func() time.Time { return timestamp }) 569 570 modelInput := &model.FetchRequest{ 571 ID: "test", 572 Mode: model.FetchModeSingle, 573 } 574 575 result := svc.HandleSpec(ctx, modelInput) 576 expectedStatus := fetchrequest.FixStatus(model.FetchRequestStatusConditionSucceeded, nil, timestamp) 577 578 assert.Equal(t, expectedStatus, modelInput.Status) 579 assert.Equal(t, mockSpec, *result) 580 assert.Equal(t, int(retryConfig.Attempts), invocations) 581 } 582 583 func TestService_HandleSpec_FailsAfterRetryMechanismIsExhausted(t *testing.T) { 584 ctx := context.TODO() 585 ctx = tenant.SaveToContext(ctx, tenantID, tenantID) 586 ctx = tenant.SaveLocalTenantIDToContext(ctx, localTenantID) 587 588 timestamp := time.Now() 589 frRepo := &automock.FetchRequestRepository{} 590 frRepo.On("Update", ctx, tenantID, mock.Anything).Return(nil).Once() 591 592 certCache := certloader.NewCertificateCache() 593 retryConfig := &retry.Config{ 594 Attempts: 3, 595 Delay: 100 * time.Millisecond, 596 } 597 598 invocations := 0 599 svc := fetchrequest.NewServiceWithRetry(frRepo, NewTestClient(func(req *http.Request) *http.Response { 600 defer func() { 601 invocations++ 602 }() 603 604 return &http.Response{StatusCode: http.StatusInternalServerError} 605 }), accessstrategy.NewDefaultExecutorProvider(certCache, externalClientCertSecretName, extSvcClientCertSecretName), retry.NewHTTPExecutor(retryConfig)) 606 svc.SetTimestampGen(func() time.Time { return timestamp }) 607 608 modelInput := &model.FetchRequest{ 609 ID: "test", 610 Mode: model.FetchModeSingle, 611 } 612 613 result := svc.HandleSpec(ctx, modelInput) 614 respStatusCodeErr := fmt.Sprintf("unexpected status code: %d", http.StatusInternalServerError) 615 expectedErr := fmt.Sprintf("All attempts fail:\n#1: %s\n#2: %s\n#3: %s", respStatusCodeErr, respStatusCodeErr, respStatusCodeErr) 616 expectedStatus := fetchrequest.FixStatus(model.FetchRequestStatusConditionFailed, str.Ptr(fmt.Sprintf("While fetching Spec: %s", expectedErr)), timestamp) 617 618 assert.Equal(t, expectedStatus, modelInput.Status) 619 assert.Nil(t, result) 620 assert.Equal(t, int(retryConfig.Attempts), invocations) 621 }