github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/statusupdate/handler_test.go (about) 1 package statusupdate_test 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 11 "github.com/kyma-incubator/compass/components/hydrator/pkg/oathkeeper" 12 13 "github.com/kyma-incubator/compass/components/director/pkg/str" 14 logrustest "github.com/sirupsen/logrus/hooks/test" 15 16 "github.com/kyma-incubator/compass/components/director/pkg/log" 17 18 "github.com/sirupsen/logrus" 19 20 "github.com/stretchr/testify/mock" 21 22 "github.com/stretchr/testify/assert" 23 24 "github.com/kyma-incubator/compass/components/director/internal/statusupdate" 25 26 "github.com/stretchr/testify/require" 27 28 "github.com/kyma-incubator/compass/components/director/internal/statusupdate/automock" 29 persistenceautomock "github.com/kyma-incubator/compass/components/director/pkg/persistence/automock" 30 31 "github.com/kyma-incubator/compass/components/director/pkg/persistence/txtest" 32 33 "github.com/kyma-incubator/compass/components/director/pkg/consumer" 34 ) 35 36 func TestUpdate_Handler(t *testing.T) { 37 // GIVEN 38 testErr := errors.New("test") 39 txGen := txtest.NewTransactionContextGenerator(testErr) 40 41 testCases := []struct { 42 Name string 43 TxFn func() (*persistenceautomock.PersistenceTx, *persistenceautomock.Transactioner) 44 RepoFn func() *automock.StatusUpdateRepository 45 Request *http.Request 46 ExpectedStatus int 47 ExpectedResponse string 48 ExpectedErrorMessage *string 49 ExpectedError *string 50 MockNextHandler http.Handler 51 }{ 52 { 53 Name: "In case of Integration System do nothing and execute next handler", 54 TxFn: txGen.ThatDoesntStartTransaction, 55 RepoFn: func() *automock.StatusUpdateRepository { 56 return nil 57 }, 58 Request: createRequestWithClaims(testID, consumer.IntegrationSystem, oathkeeper.OAuth2Flow), 59 ExpectedStatus: http.StatusOK, 60 ExpectedResponse: "OK", 61 MockNextHandler: fixNextHandler(t), 62 }, 63 { 64 Name: "In case of Static User do nothing and execute next handler", 65 TxFn: txGen.ThatDoesntStartTransaction, 66 RepoFn: func() *automock.StatusUpdateRepository { 67 return nil 68 }, 69 Request: createRequestWithClaims(testID, consumer.User, oathkeeper.JWTAuthFlow), 70 ExpectedStatus: http.StatusOK, 71 ExpectedResponse: "OK", 72 MockNextHandler: fixNextHandler(t), 73 }, 74 { 75 Name: "In case of Application and Certificate flow update status and execute next handler", 76 TxFn: txGen.ThatSucceeds, 77 RepoFn: func() *automock.StatusUpdateRepository { 78 repo := automock.StatusUpdateRepository{} 79 repo.On("IsConnected", txtest.CtxWithDBMatcher(), testID, statusupdate.Applications).Return(false, nil) 80 repo.On("UpdateStatus", txtest.CtxWithDBMatcher(), testID, statusupdate.Applications).Return(nil) 81 return &repo 82 }, 83 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.CertificateFlow), 84 ExpectedStatus: http.StatusOK, 85 ExpectedResponse: "OK", 86 MockNextHandler: fixNextHandler(t), 87 }, 88 { 89 Name: "In case of Application and OneTimeToken flow do nothing and execute next handler", 90 TxFn: txGen.ThatDoesntStartTransaction, 91 RepoFn: func() *automock.StatusUpdateRepository { 92 return nil 93 }, 94 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.OneTimeTokenFlow), 95 ExpectedStatus: http.StatusOK, 96 ExpectedResponse: "OK", 97 MockNextHandler: fixNextHandler(t), 98 }, 99 { 100 Name: "In case of Runtime and Certificate flow update status and execute next handler", 101 TxFn: txGen.ThatSucceeds, 102 RepoFn: func() *automock.StatusUpdateRepository { 103 repo := automock.StatusUpdateRepository{} 104 repo.On("IsConnected", txtest.CtxWithDBMatcher(), testID, statusupdate.Runtimes).Return(false, nil) 105 repo.On("UpdateStatus", txtest.CtxWithDBMatcher(), testID, statusupdate.Runtimes).Return(nil) 106 return &repo 107 }, 108 Request: createRequestWithClaims(testID, consumer.Runtime, oathkeeper.CertificateFlow), 109 ExpectedStatus: http.StatusOK, 110 ExpectedResponse: "OK", 111 MockNextHandler: fixNextHandler(t), 112 }, 113 { 114 Name: "In case of Runtime and OneTimeToken flow do nothing and execute next handler", 115 TxFn: txGen.ThatDoesntStartTransaction, 116 RepoFn: func() *automock.StatusUpdateRepository { 117 return nil 118 }, 119 Request: createRequestWithClaims(testID, consumer.Runtime, oathkeeper.OneTimeTokenFlow), 120 ExpectedStatus: http.StatusOK, 121 ExpectedResponse: "OK", 122 MockNextHandler: fixNextHandler(t), 123 }, 124 { 125 Name: "In case of application already connected do nothing and execute next handler", 126 TxFn: txGen.ThatSucceeds, 127 RepoFn: func() *automock.StatusUpdateRepository { 128 repo := automock.StatusUpdateRepository{} 129 repo.On("IsConnected", mock.Anything, testID, statusupdate.Applications).Return(true, nil) 130 return &repo 131 }, 132 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.CertificateFlow), 133 ExpectedStatus: http.StatusOK, 134 ExpectedResponse: "OK", 135 MockNextHandler: fixNextHandler(t), 136 }, 137 { 138 Name: "In case of runtime already connected do nothing and execute next handler", 139 TxFn: txGen.ThatSucceeds, 140 RepoFn: func() *automock.StatusUpdateRepository { 141 repo := automock.StatusUpdateRepository{} 142 repo.On("IsConnected", txtest.CtxWithDBMatcher(), testID, statusupdate.Runtimes).Return(true, nil) 143 return &repo 144 }, 145 Request: createRequestWithClaims(testID, consumer.Runtime, oathkeeper.CertificateFlow), 146 ExpectedStatus: http.StatusOK, 147 ExpectedResponse: "OK", 148 MockNextHandler: fixNextHandler(t), 149 }, 150 { 151 Name: "Error when no consumer info in context", 152 TxFn: txGen.ThatDoesntStartTransaction, 153 RepoFn: func() *automock.StatusUpdateRepository { 154 repo := automock.StatusUpdateRepository{} 155 return &repo 156 }, 157 Request: &http.Request{}, 158 ExpectedStatus: http.StatusOK, 159 ExpectedErrorMessage: str.Ptr("An error has occurred while fetching consumer info from context:"), 160 ExpectedError: str.Ptr("Internal Server Error: cannot read consumer from context"), 161 MockNextHandler: fixNextHandler(t), 162 }, 163 { 164 Name: "Error when starting transaction", 165 TxFn: txGen.ThatFailsOnBegin, 166 RepoFn: func() *automock.StatusUpdateRepository { 167 repo := automock.StatusUpdateRepository{} 168 return &repo 169 }, 170 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.CertificateFlow), 171 ExpectedStatus: http.StatusOK, 172 ExpectedErrorMessage: str.Ptr("An error has occurred while opening transaction:"), 173 ExpectedError: str.Ptr("test"), 174 MockNextHandler: fixNextHandler(t), 175 }, 176 { 177 Name: "Error when checking if already connected", 178 TxFn: txGen.ThatDoesntExpectCommit, 179 RepoFn: func() *automock.StatusUpdateRepository { 180 repo := automock.StatusUpdateRepository{} 181 repo.On("IsConnected", txtest.CtxWithDBMatcher(), testID, statusupdate.Applications).Return(false, testErr) 182 return &repo 183 }, 184 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.CertificateFlow), 185 ExpectedStatus: http.StatusOK, 186 ExpectedErrorMessage: str.Ptr("An error has occurred while checking repository status for applications with ID foo:"), 187 ExpectedError: str.Ptr("test"), 188 MockNextHandler: fixNextHandler(t), 189 }, 190 { 191 Name: "Error when failing on commit", 192 TxFn: txGen.ThatFailsOnCommit, 193 RepoFn: func() *automock.StatusUpdateRepository { 194 repo := automock.StatusUpdateRepository{} 195 repo.On("IsConnected", txtest.CtxWithDBMatcher(), testID, statusupdate.Applications).Return(true, nil) 196 return &repo 197 }, 198 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.CertificateFlow), 199 ExpectedStatus: http.StatusOK, 200 ExpectedErrorMessage: str.Ptr("An error has occurred while committing transaction:"), 201 ExpectedError: str.Ptr("test"), 202 MockNextHandler: fixNextHandler(t), 203 }, 204 { 205 Name: "Error when updating status", 206 TxFn: txGen.ThatDoesntExpectCommit, 207 RepoFn: func() *automock.StatusUpdateRepository { 208 repo := automock.StatusUpdateRepository{} 209 repo.On("IsConnected", txtest.CtxWithDBMatcher(), testID, statusupdate.Applications).Return(false, nil) 210 repo.On("UpdateStatus", txtest.CtxWithDBMatcher(), testID, statusupdate.Applications).Return(testErr) 211 return &repo 212 }, 213 Request: createRequestWithClaims(testID, consumer.Application, oathkeeper.CertificateFlow), 214 ExpectedStatus: http.StatusOK, 215 ExpectedErrorMessage: str.Ptr("An error has occurred while updating repository status for applications with ID foo:"), 216 ExpectedError: str.Ptr("test"), 217 MockNextHandler: fixNextHandler(t), 218 }, 219 } 220 for _, testCase := range testCases { 221 t.Run(testCase.Name, func(t *testing.T) { 222 repo := testCase.RepoFn() 223 persist, transact := testCase.TxFn() 224 var actualLog bytes.Buffer 225 logger, hook := logrustest.NewNullLogger() 226 logger.SetFormatter(&logrus.TextFormatter{ 227 DisableTimestamp: true, 228 }) 229 logger.SetOutput(&actualLog) 230 ctx := log.ContextWithLogger(testCase.Request.Context(), logrus.NewEntry(logger)) 231 update := statusupdate.New(transact, repo) 232 req := testCase.Request.WithContext(ctx) 233 // WHEN 234 rr := httptest.NewRecorder() 235 updateHandler := update.Handler() 236 updateHandler(testCase.MockNextHandler).ServeHTTP(rr, req) 237 238 // THEN 239 response := rr.Body.String() 240 assert.Equal(t, testCase.ExpectedStatus, rr.Code) 241 if testCase.ExpectedResponse == "OK" { 242 assert.Equal(t, testCase.ExpectedResponse, response) 243 } 244 if testCase.ExpectedErrorMessage != nil { 245 assert.Equal(t, *testCase.ExpectedErrorMessage+" "+*testCase.ExpectedError, hook.LastEntry().Message) 246 } 247 if testCase.ExpectedError != nil { 248 assert.Equal(t, *testCase.ExpectedError, hook.LastEntry().Data["error"].(error).Error()) 249 } 250 persist.AssertExpectations(t) 251 transact.AssertExpectations(t) 252 }) 253 } 254 } 255 256 func createRequestWithClaims(id string, consumerType consumer.ConsumerType, flow oathkeeper.AuthFlow) *http.Request { 257 req := http.Request{} 258 apiConsumer := consumer.Consumer{ConsumerID: id, ConsumerType: consumerType, Flow: flow} 259 ctxWithConsumerInfo := consumer.SaveToContext(context.TODO(), apiConsumer) 260 return req.WithContext(ctxWithConsumerInfo) 261 } 262 263 func fixNextHandler(t *testing.T) http.HandlerFunc { 264 return func(w http.ResponseWriter, r *http.Request) { 265 _, err := w.Write([]byte("OK")) 266 require.NoError(t, err) 267 } 268 }