github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/health/health_test.go (about)

     1  package health
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  const (
    17  	checkErr = "failed during RabbitMQ health check"
    18  )
    19  
    20  func TestRegisterWithNoName(t *testing.T) {
    21  	h, err := New()
    22  	require.NoError(t, err)
    23  
    24  	err = h.Register(Config{
    25  		Name: "",
    26  		Check: func(context.Context) error {
    27  			return nil
    28  		},
    29  	})
    30  	require.Error(t, err, "health check registration with empty name should return an error")
    31  }
    32  
    33  func TestDoubleRegister(t *testing.T) {
    34  	h, err := New()
    35  	require.NoError(t, err)
    36  
    37  	healthCheckName := "health-check"
    38  
    39  	conf := Config{
    40  		Name: healthCheckName,
    41  		Check: func(context.Context) error {
    42  			return nil
    43  		},
    44  	}
    45  
    46  	err = h.Register(conf)
    47  	require.NoError(t, err, "the first registration of a health check should not return an error, but got one")
    48  
    49  	err = h.Register(conf)
    50  	assert.Error(t, err, "the second registration of a health check config should return an error, but did not")
    51  
    52  	err = h.Register(Config{
    53  		Name: healthCheckName,
    54  		Check: func(context.Context) error {
    55  			return errors.New("health checks registered")
    56  		},
    57  	})
    58  	assert.Error(t, err, "registration with same name, but different details should still return an error, but did not")
    59  }
    60  
    61  func TestHealthHandler(t *testing.T) {
    62  	h, err := New()
    63  	require.NoError(t, err)
    64  
    65  	res := httptest.NewRecorder()
    66  	req, err := http.NewRequest("GET", "http://localhost/status", nil)
    67  	require.NoError(t, err)
    68  
    69  	err = h.Register(Config{
    70  		Name:      "rabbitmq",
    71  		SkipOnErr: true,
    72  		Check:     func(context.Context) error { return errors.New(checkErr) },
    73  	})
    74  	require.NoError(t, err)
    75  
    76  	err = h.Register(Config{
    77  		Name:  "mongodb",
    78  		Check: func(context.Context) error { return nil },
    79  	})
    80  	require.NoError(t, err)
    81  
    82  	err = h.Register(Config{
    83  		Name:      "snail-service",
    84  		SkipOnErr: true,
    85  		Timeout:   time.Second * 1,
    86  		Check: func(context.Context) error {
    87  			time.Sleep(time.Second * 2)
    88  			return nil
    89  		},
    90  	})
    91  	require.NoError(t, err)
    92  
    93  	handler := h.Handler()
    94  	handler.ServeHTTP(res, req)
    95  
    96  	assert.Equal(t, http.StatusOK, res.Code, "status handler returned wrong status code")
    97  
    98  	body := make(map[string]interface{})
    99  	err = json.NewDecoder(res.Body).Decode(&body)
   100  	require.NoError(t, err)
   101  
   102  	assert.Equal(t, string(StatusPartiallyAvailable), body["status"], "body returned wrong status")
   103  
   104  	failure, ok := body["failures"]
   105  	assert.True(t, ok, "body returned nil failures field")
   106  
   107  	f, ok := failure.(map[string]interface{})
   108  	assert.True(t, ok, "body returned nil failures.rabbitmq field")
   109  
   110  	assert.Equal(t, checkErr, f["rabbitmq"], "body returned wrong status for rabbitmq")
   111  	assert.Equal(t, string(StatusTimeout), f["snail-service"], "body returned wrong status for snail-service")
   112  }
   113  
   114  func TestHealth_Measure(t *testing.T) {
   115  	h, err := New(WithChecks(Config{
   116  		Name:      "check1",
   117  		Timeout:   time.Second,
   118  		SkipOnErr: false,
   119  		Check: func(context.Context) error {
   120  			time.Sleep(time.Second * 10)
   121  			return errors.New("check1")
   122  		},
   123  	}, Config{
   124  		Name:      "check2",
   125  		Timeout:   time.Second * 2,
   126  		SkipOnErr: false,
   127  		Check: func(context.Context) error {
   128  			time.Sleep(time.Second * 10)
   129  			return errors.New("check2")
   130  		},
   131  	}), WithMaxConcurrent(2))
   132  	require.NoError(t, err)
   133  
   134  	startedAt := time.Now()
   135  	result := h.Measure(context.Background())
   136  	elapsed := time.Since(startedAt)
   137  
   138  	// both checks should run concurrently and should fail with timeout,
   139  	// so should take not less than 2 sec, but less than 5 that is sequential check time
   140  	require.GreaterOrEqual(t, elapsed.Milliseconds(), (time.Second * 2).Milliseconds())
   141  	require.Less(t, elapsed.Milliseconds(), (time.Second * 5).Milliseconds())
   142  
   143  	assert.Equal(t, StatusUnavailable, result.Status)
   144  	assert.Equal(t, string(StatusTimeout), result.Failures["check1"])
   145  	assert.Equal(t, string(StatusTimeout), result.Failures["check2"])
   146  	assert.Nil(t, result.System)
   147  
   148  	h, err = New(WithSystemInfo())
   149  	require.NoError(t, err)
   150  	result = h.Measure(context.Background())
   151  
   152  	assert.NotNil(t, result.System)
   153  }