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  }