github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/healthz/health_test.go (about) 1 package healthz_test 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 "net/http/httptest" 8 "testing" 9 "time" 10 11 "github.com/kyma-incubator/compass/components/director/internal/healthz" 12 "github.com/kyma-incubator/compass/components/director/internal/healthz/automock" 13 "github.com/stretchr/testify/require" 14 ) 15 16 var ( 17 firstConfig = healthz.IndicatorConfig{ 18 Name: "First", 19 Interval: time.Second, 20 Timeout: time.Second, 21 InitialDelay: time.Second, 22 Threshold: 1, 23 } 24 25 secondConfig = healthz.IndicatorConfig{ 26 Name: "Second", 27 Interval: time.Second, 28 Timeout: time.Second, 29 InitialDelay: time.Second, 30 Threshold: 1, 31 } 32 ) 33 34 func TestNew(t *testing.T) { 35 t.Run("should return not nil health", func(t *testing.T) { 36 // GIVEN 37 ctx := context.TODO() 38 // WHEN 39 health, err := healthz.New(ctx, healthz.Config{}) 40 // THEN 41 require.NotNil(t, health) 42 require.NoError(t, err) 43 }) 44 t.Run("should return error on invalid interval", func(t *testing.T) { 45 // GIVEN 46 ctx := context.TODO() 47 // WHEN 48 health, err := healthz.New(ctx, healthz.Config{Indicators: []healthz.IndicatorConfig{{ 49 Interval: 0, 50 }}}) 51 // THEN 52 require.Nil(t, health) 53 require.Error(t, err) 54 require.Contains(t, err.Error(), "interval") 55 }) 56 t.Run("should return error on invalid timeout", func(t *testing.T) { 57 // GIVEN 58 ctx := context.TODO() 59 // WHEN 60 health, err := healthz.New(ctx, healthz.Config{Indicators: []healthz.IndicatorConfig{{ 61 Interval: time.Second, 62 Timeout: 0, 63 }}}) 64 // THEN 65 require.Nil(t, health) 66 require.Error(t, err) 67 require.Contains(t, err.Error(), "timeout") 68 }) 69 t.Run("should return error on invalid initial delay", func(t *testing.T) { 70 // GIVEN 71 ctx := context.TODO() 72 // WHEN 73 health, err := healthz.New(ctx, healthz.Config{Indicators: []healthz.IndicatorConfig{{ 74 Interval: time.Second, 75 Timeout: time.Second, 76 InitialDelay: -1, 77 }}}) 78 // THEN 79 require.Nil(t, health) 80 require.Error(t, err) 81 require.Contains(t, err.Error(), "initial delay ") 82 }) 83 t.Run("should return error on invalid threshold", func(t *testing.T) { 84 // GIVEN 85 ctx := context.TODO() 86 // WHEN 87 health, err := healthz.New(ctx, healthz.Config{Indicators: []healthz.IndicatorConfig{{ 88 Interval: time.Second, 89 Timeout: time.Second, 90 InitialDelay: time.Second, 91 Threshold: -1, 92 }}}) 93 // THEN 94 require.Nil(t, health) 95 require.Error(t, err) 96 require.Contains(t, err.Error(), "threshold") 97 }) 98 } 99 100 func TestFullFlow(t *testing.T) { 101 t.Run("should configure properly(first config exists) and return UP when both indicators succeed", func(t *testing.T) { 102 // GIVEN 103 ctx, cancel := context.WithCancel(context.TODO()) 104 defer cancel() 105 106 healthCfg := healthz.Config{Indicators: []healthz.IndicatorConfig{firstConfig}} 107 108 firstStatus := &automock.Status{} 109 defer firstStatus.AssertExpectations(t) 110 firstStatus.On("Error").Return(nil) 111 112 firstInd := &automock.Indicator{} 113 defer firstInd.AssertExpectations(t) 114 firstInd.On("Name").Return("First") 115 firstInd.On("Run", ctx).Return(nil) 116 firstInd.On("Status").Return(firstStatus) 117 firstInd.On("Configure", firstConfig).Return() 118 119 secondStatus := &automock.Status{} 120 defer secondStatus.AssertExpectations(t) 121 secondStatus.On("Error").Return(nil) 122 123 secondInd := &automock.Indicator{} 124 defer secondInd.AssertExpectations(t) 125 secondInd.On("Name").Return("Second") 126 secondInd.On("Run", ctx).Return(nil) 127 secondInd.On("Status").Return(secondStatus) 128 secondInd.On("Configure", healthz.NewDefaultConfig()).Return() 129 130 // WHEN 131 health, err := healthz.New(ctx, healthCfg) 132 require.NoError(t, err) 133 134 health.RegisterIndicator(firstInd). 135 RegisterIndicator(secondInd). 136 Start() 137 status := health.ReportStatus() 138 139 // THEN 140 require.Equal(t, status, healthz.UP) 141 AssertHandlerStatusCodeForHealth(t, health, http.StatusOK, healthz.UP) 142 }) 143 144 t.Run("should configure properly(both config exist) and return DOWN when one indicator fails", func(t *testing.T) { 145 // GIVEN 146 ctx, cancel := context.WithCancel(context.TODO()) 147 defer cancel() 148 149 healthCfg := healthz.Config{Indicators: []healthz.IndicatorConfig{firstConfig, secondConfig}} 150 151 firstStatus := &automock.Status{} 152 defer firstStatus.AssertExpectations(t) 153 firstStatus.On("Error").Return(errors.New("some error")) 154 firstStatus.On("Details").Return("some details") 155 156 firstInd := &automock.Indicator{} 157 defer firstInd.AssertExpectations(t) 158 firstInd.On("Name").Return("First").Times(3) 159 firstInd.On("Run", ctx).Return(nil) 160 firstInd.On("Status").Return(firstStatus) 161 firstInd.On("Configure", firstConfig).Return() 162 163 secondStatus := &automock.Status{} 164 defer secondStatus.AssertExpectations(t) 165 secondStatus.On("Error").Return(nil) 166 167 secondInd := &automock.Indicator{} 168 defer secondInd.AssertExpectations(t) 169 secondInd.On("Name").Return("Second") 170 secondInd.On("Run", ctx).Return(nil) 171 secondInd.On("Status").Return(secondStatus) 172 secondInd.On("Configure", secondConfig).Return() 173 174 // WHEN 175 health, err := healthz.New(ctx, healthCfg) 176 require.NoError(t, err) 177 178 health.RegisterIndicator(firstInd). 179 RegisterIndicator(secondInd). 180 Start() 181 182 status := health.ReportStatus() 183 184 // THEN 185 require.Equal(t, status, healthz.DOWN) 186 AssertHandlerStatusCodeForHealth(t, health, http.StatusInternalServerError, healthz.DOWN) 187 }) 188 189 t.Run("should configure properly(neither config exist) and return DOWN when all indicators fail", func(t *testing.T) { 190 // GIVEN 191 ctx, cancel := context.WithCancel(context.TODO()) 192 defer cancel() 193 194 healthCfg := healthz.Config{Indicators: []healthz.IndicatorConfig{}} 195 196 firstStatus := &automock.Status{} 197 defer firstStatus.AssertExpectations(t) 198 firstStatus.On("Error").Return(errors.New("some error")) 199 firstStatus.On("Details").Return("some details") 200 201 firstInd := &automock.Indicator{} 202 defer firstInd.AssertExpectations(t) 203 firstInd.On("Name").Return("First").Times(3) 204 firstInd.On("Run", ctx).Return(nil) 205 firstInd.On("Status").Return(firstStatus) 206 firstInd.On("Configure", healthz.NewDefaultConfig()).Return() 207 208 secondStatus := &automock.Status{} 209 defer secondStatus.AssertExpectations(t) 210 secondStatus.On("Error").Return(errors.New("some error 2")) 211 secondStatus.On("Details").Return("some details 2") 212 213 secondInd := &automock.Indicator{} 214 defer secondInd.AssertExpectations(t) 215 secondInd.On("Name").Return("Second").Times(3) 216 secondInd.On("Run", ctx).Return(nil) 217 secondInd.On("Status").Return(secondStatus) 218 secondInd.On("Configure", healthz.NewDefaultConfig()).Return() 219 220 // WHEN 221 health, err := healthz.New(ctx, healthCfg) 222 require.NoError(t, err) 223 224 health.RegisterIndicator(firstInd). 225 RegisterIndicator(secondInd). 226 Start() 227 228 status := health.ReportStatus() 229 230 // THEN 231 require.Equal(t, status, healthz.DOWN) 232 AssertHandlerStatusCodeForHealth(t, health, http.StatusInternalServerError, healthz.DOWN) 233 }) 234 } 235 236 func AssertHandlerStatusCodeForHealth(t *testing.T, h *healthz.Health, expectedCode int, expectedBody string) { 237 req, err := http.NewRequest("GET", "/health", nil) 238 require.NoError(t, err) 239 240 rr := httptest.NewRecorder() 241 handler := http.HandlerFunc(healthz.NewHealthHandler(h)) 242 // WHEN 243 handler.ServeHTTP(rr, req) 244 // THEN 245 require.Equal(t, expectedCode, rr.Code) 246 require.Equal(t, expectedBody, rr.Body.String()) 247 }