github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/destinationfetchersvc/handler_test.go (about) 1 package destinationfetchersvc_test 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 11 "github.com/kyma-incubator/compass/components/director/internal/destinationfetchersvc" 12 "github.com/kyma-incubator/compass/components/director/internal/destinationfetchersvc/automock" 13 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 14 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 15 "github.com/kyma-incubator/compass/components/director/pkg/resource" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/mock" 18 ) 19 20 const expectedTenantID = "f09ba084-0e82-49ab-ab2e-b7ecc988312d" 21 22 func TestHandler_SyncDestinations(t *testing.T) { 23 target := "/v1/fetch" 24 25 validHandlerConfig := destinationfetchersvc.HandlerConfig{ 26 SyncDestinationsEndpoint: "/v1/fetch", 27 DestinationsSensitiveEndpoint: "/v1/info", 28 } 29 30 reqWithUserContext := httptest.NewRequest(http.MethodPut, target, nil) 31 reqWithUserContext = reqWithUserContext.WithContext( 32 tenant.SaveToContext(reqWithUserContext.Context(), expectedTenantID, "")) 33 34 testCases := []struct { 35 Name string 36 Request *http.Request 37 DestinationManager func() *automock.DestinationManager 38 ExpectedErrorOutput string 39 ExpectedStatusCode int 40 }{ 41 { 42 Name: "Successful fetch on-demand", 43 Request: reqWithUserContext, 44 DestinationManager: func() *automock.DestinationManager { 45 svc := &automock.DestinationManager{} 46 svc.On("SyncTenantDestinations", mock.Anything, expectedTenantID).Return(nil) 47 return svc 48 }, 49 ExpectedStatusCode: http.StatusOK, 50 }, 51 { 52 Name: "Missing tenant header", 53 Request: httptest.NewRequest(http.MethodPut, target, nil), 54 DestinationManager: func() *automock.DestinationManager { 55 return &automock.DestinationManager{} 56 }, 57 ExpectedStatusCode: http.StatusBadRequest, 58 }, 59 { 60 Name: "Tenant not found", 61 Request: reqWithUserContext, 62 DestinationManager: func() *automock.DestinationManager { 63 svc := &automock.DestinationManager{} 64 err := apperrors.NewNotFoundErrorWithMessage(resource.Label, 65 expectedTenantID, fmt.Sprintf("tenant %s not found", expectedTenantID)) 66 svc.On("SyncTenantDestinations", mock.Anything, expectedTenantID).Return(err) 67 return svc 68 }, 69 ExpectedErrorOutput: fmt.Sprintf("tenant %s not found", expectedTenantID), 70 ExpectedStatusCode: http.StatusBadRequest, 71 }, 72 { 73 Name: "Internal Server Error", 74 Request: reqWithUserContext, 75 DestinationManager: func() *automock.DestinationManager { 76 svc := &automock.DestinationManager{} 77 err := fmt.Errorf("random error") 78 svc.On("SyncTenantDestinations", mock.Anything, expectedTenantID).Return(err) 79 return svc 80 }, 81 ExpectedErrorOutput: fmt.Sprintf("Failed to sync destinations for tenant %s", expectedTenantID), 82 ExpectedStatusCode: http.StatusInternalServerError, 83 }, 84 } 85 for _, testCase := range testCases { 86 t.Run(testCase.Name, func(t *testing.T) { 87 tf := testCase.DestinationManager() 88 defer mock.AssertExpectationsForObjects(t, tf) 89 90 handler := destinationfetchersvc.NewDestinationsHTTPHandler(tf, validHandlerConfig) 91 req := testCase.Request 92 w := httptest.NewRecorder() 93 94 // WHEN 95 handler.SyncTenantDestinations(w, req) 96 97 // THEN 98 resp := w.Result() 99 body, err := io.ReadAll(resp.Body) 100 assert.NoError(t, err) 101 102 if len(testCase.ExpectedErrorOutput) > 0 { 103 assert.Contains(t, string(body), testCase.ExpectedErrorOutput) 104 } else { 105 assert.NoError(t, err) 106 } 107 108 assert.Equal(t, testCase.ExpectedStatusCode, resp.StatusCode) 109 }) 110 } 111 } 112 113 func TestHandler_FetchDestinationsSensitiveData(t *testing.T) { 114 const destQueryParameter = "name" 115 116 json := []byte("{}") 117 118 target := "/v1/info" 119 120 validHandlerConfig := destinationfetchersvc.HandlerConfig{ 121 SyncDestinationsEndpoint: "/v1/fetch", 122 DestinationsSensitiveEndpoint: "/v1/info", 123 DestinationsQueryParameter: destQueryParameter, 124 } 125 126 namesQueryRaw := "name=Rand&name=Mat" 127 names := []string{"Rand", "Mat"} 128 reqWithUserContext := httptest.NewRequest(http.MethodPut, target, nil) 129 reqWithUserContext = reqWithUserContext.WithContext( 130 tenant.SaveToContext(reqWithUserContext.Context(), expectedTenantID, "")) 131 132 testCases := []struct { 133 Name string 134 Request *http.Request 135 DestQuery string 136 DestinationFetcherSvc func() *automock.DestinationManager 137 ExpectedErrorOutput string 138 ExpectedStatusCode int 139 }{ 140 { 141 Name: "Successful fetch data fetch", 142 Request: reqWithUserContext, 143 DestQuery: namesQueryRaw, 144 DestinationFetcherSvc: func() *automock.DestinationManager { 145 svc := &automock.DestinationManager{} 146 svc.On("FetchDestinationsSensitiveData", mock.Anything, expectedTenantID, names). 147 Return( 148 func(ctx context.Context, tenantID string, destNames []string) []byte { 149 return json 150 }, 151 func(ctx context.Context, tenantID string, destNames []string) error { 152 return nil 153 }, 154 ) 155 return svc 156 }, 157 ExpectedStatusCode: http.StatusOK, 158 }, 159 { 160 Name: "Missing tenant header", 161 Request: httptest.NewRequest(http.MethodPut, target, nil), 162 DestinationFetcherSvc: func() *automock.DestinationManager { 163 return &automock.DestinationManager{} 164 }, 165 ExpectedStatusCode: http.StatusBadRequest, 166 }, 167 { 168 Name: "Missing destination query parameter.", 169 Request: reqWithUserContext, 170 DestinationFetcherSvc: func() *automock.DestinationManager { 171 return &automock.DestinationManager{} 172 }, 173 ExpectedStatusCode: http.StatusBadRequest, 174 }, 175 } 176 for _, testCase := range testCases { 177 t.Run(testCase.Name, func(t *testing.T) { 178 tf := testCase.DestinationFetcherSvc() 179 defer mock.AssertExpectationsForObjects(t, tf) 180 181 handler := destinationfetchersvc.NewDestinationsHTTPHandler(tf, validHandlerConfig) 182 req := testCase.Request 183 //req is a pointer and the changes on the previous test are kept 184 req.URL.RawQuery = "" 185 if len(testCase.DestQuery) > 0 { 186 req.URL.RawQuery = testCase.DestQuery 187 } 188 189 w := httptest.NewRecorder() 190 191 // WHEN 192 handler.FetchDestinationsSensitiveData(w, req) 193 194 // THEN 195 resp := w.Result() 196 body, err := io.ReadAll(resp.Body) 197 assert.NoError(t, err) 198 199 if len(testCase.ExpectedErrorOutput) > 0 { 200 assert.Contains(t, string(body), testCase.ExpectedErrorOutput) 201 } else { 202 assert.NoError(t, err) 203 } 204 205 assert.Equal(t, testCase.ExpectedStatusCode, resp.StatusCode) 206 }) 207 } 208 }