github.com/xmidt-org/webpa-common@v1.11.9/health/health_test.go (about) 1 package health 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/mock" 14 "github.com/xmidt-org/webpa-common/logging" 15 ) 16 17 // setupHealth supplies a Health object with useful test configuration 18 func setupHealth(t *testing.T) *Health { 19 return New( 20 time.Duration(69)*time.Second, 21 logging.NewTestLogger(nil, t), 22 ) 23 } 24 25 func TestLifecycle(t *testing.T) { 26 var ( 27 assert = assert.New(t) 28 h = setupHealth(t) 29 30 healthWaitGroup = &sync.WaitGroup{} 31 shutdown = make(chan struct{}) 32 ) 33 34 h.Run(healthWaitGroup, shutdown) 35 36 // verify initial state 37 var initialListenerCount int 38 testWaitGroup := &sync.WaitGroup{} 39 testWaitGroup.Add(1) 40 h.SendEvent(func(stats Stats) { 41 defer testWaitGroup.Done() 42 initialListenerCount = len(h.statsListeners) 43 assert.Equal(NewStats(nil), stats) 44 assert.Equal(stats, h.stats) 45 }) 46 47 h.AddStatsListener(StatsListenerFunc(func(Stats) {})) 48 49 testWaitGroup.Add(1) 50 h.SendEvent(func(Stats) { 51 defer testWaitGroup.Done() 52 t.Log("verifying AddStatsListener") 53 if len(h.statsListeners) != (initialListenerCount + 1) { 54 t.Errorf("Listeners were not updated properly") 55 } 56 }) 57 58 done := make(chan struct{}) 59 timer := time.NewTimer(time.Second * 10) 60 61 go func() { 62 testWaitGroup.Wait() 63 close(done) 64 }() 65 66 select { 67 case <-done: 68 t.Log("Initial state verified") 69 case <-timer.C: 70 t.Errorf("Failed to verify initial state within the timeout") 71 close(done) 72 } 73 74 close(shutdown) 75 76 done = make(chan struct{}) 77 timer.Stop() 78 timer = time.NewTimer(time.Second * 10) 79 defer timer.Stop() 80 go func() { 81 healthWaitGroup.Wait() 82 close(done) 83 }() 84 85 select { 86 case <-done: 87 t.Log("Final state verified") 88 case <-timer.C: 89 t.Errorf("Failed to verify final state within the timeout") 90 close(done) 91 } 92 } 93 94 func TestServeHTTP(t *testing.T) { 95 var ( 96 assert = assert.New(t) 97 h = setupHealth(t) 98 shutdown = make(chan struct{}) 99 100 request = httptest.NewRequest("GET", "http://something.net", nil) 101 response = httptest.NewRecorder() 102 ) 103 104 h.Run(&sync.WaitGroup{}, shutdown) 105 defer close(shutdown) 106 107 h.ServeHTTP(response, request) 108 109 done := make(chan struct{}) 110 timer := time.NewTimer(time.Second * 15) 111 defer timer.Stop() 112 h.SendEvent(func(stats Stats) { 113 close(done) 114 }) 115 116 select { 117 case <-done: 118 case <-timer.C: 119 close(done) 120 t.Fatalf("Did not receive next event after ServeHTTP in the allotted time") 121 } 122 123 assert.Equal(200, response.Code) 124 125 var result Stats 126 assert.NoError(json.Unmarshal(response.Body.Bytes(), &result)) 127 128 // each key in commonStats should be present in the output 129 for _, stat := range memoryStats { 130 _, ok := result[stat.(Stat)] 131 assert.True(ok) 132 } 133 } 134 135 func TestHealthRequestTracker(t *testing.T) { 136 var ( 137 assert = assert.New(t) 138 testData = []struct { 139 expectedStatusCode int 140 expectedStats Stats 141 }{ 142 // success codes 143 {0, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 144 {100, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 145 {200, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 146 {201, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 147 {202, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 148 {300, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 149 {307, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 1, TotalRequestsDenied: 0}}, 150 151 // failure codes 152 {400, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 0, TotalRequestsDenied: 1}}, 153 {404, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 0, TotalRequestsDenied: 1}}, 154 {500, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 0, TotalRequestsDenied: 1}}, 155 {523, Stats{TotalRequestsReceived: 1, TotalRequestsSuccessfullyServiced: 0, TotalRequestsDenied: 1}}, 156 } 157 ) 158 159 for _, record := range testData { 160 t.Logf("%#v", record) 161 162 var ( 163 monitor = setupHealth(t) 164 shutdown = make(chan struct{}) 165 166 handler = new(mockHandler) 167 request = httptest.NewRequest("GET", "http://something.com", nil) 168 response = httptest.NewRecorder() 169 ) 170 171 monitor.Run(&sync.WaitGroup{}, shutdown) 172 defer close(shutdown) 173 174 handler.On("ServeHTTP", mock.MatchedBy(func(*ResponseWriter) bool { return true }), request). 175 Once(). 176 Run(func(arguments mock.Arguments) { 177 arguments.Get(0).(http.ResponseWriter).WriteHeader(record.expectedStatusCode) 178 }) 179 180 compositeHandler := monitor.RequestTracker(handler) 181 compositeHandler.ServeHTTP(response, request) 182 assert.Equal(record.expectedStatusCode, response.Code) 183 184 assertionWaitGroup := new(sync.WaitGroup) 185 assertionWaitGroup.Add(1) 186 monitor.SendEvent( 187 func(actualStats Stats) { 188 defer assertionWaitGroup.Done() 189 t.Logf("actual stats: %v", actualStats) 190 for stat, value := range record.expectedStats { 191 assert.Equal(value, actualStats[stat], fmt.Sprintf("%s should have been %d", stat, value)) 192 } 193 }, 194 ) 195 196 assertionWaitGroup.Wait() 197 handler.AssertExpectations(t) 198 } 199 } 200 201 func TestHealthRequestTrackerDelegatePanic(t *testing.T) { 202 var ( 203 assert = assert.New(t) 204 monitor = setupHealth(t) 205 shutdown = make(chan struct{}) 206 207 handler = new(mockHandler) 208 request = httptest.NewRequest("GET", "http://something.com", nil) 209 response = httptest.NewRecorder() 210 ) 211 212 monitor.Run(&sync.WaitGroup{}, shutdown) 213 defer close(shutdown) 214 215 handler.On("ServeHTTP", mock.MatchedBy(func(*ResponseWriter) bool { return true }), request). 216 Once(). 217 Run(func(mock.Arguments) { 218 panic("expected") 219 }) 220 221 compositeHandler := monitor.RequestTracker(handler) 222 compositeHandler.ServeHTTP(response, request) 223 assert.Equal(http.StatusInternalServerError, response.Code) 224 225 assertionWaitGroup := new(sync.WaitGroup) 226 assertionWaitGroup.Add(1) 227 monitor.SendEvent( 228 func(actualStats Stats) { 229 defer assertionWaitGroup.Done() 230 assert.Equal(1, actualStats[TotalRequestsReceived]) 231 assert.Equal(0, actualStats[TotalRequestsSuccessfullyServiced]) 232 assert.Equal(1, actualStats[TotalRequestsDenied]) 233 }, 234 ) 235 236 assertionWaitGroup.Wait() 237 handler.AssertExpectations(t) 238 }