github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/healthz/indicator_test.go (about)

     1  package healthz_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/healthz/automock"
    11  
    12  	"github.com/kyma-incubator/compass/components/director/internal/healthz"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestNewIndicator(t *testing.T) {
    17  	t.Run("should return not nil indicator", func(t *testing.T) {
    18  		// GIVEN
    19  		indicatorFunc := dummyIndicatorFunc(nil)
    20  
    21  		// WHEN
    22  		indicator := healthz.NewIndicator("test", indicatorFunc)
    23  
    24  		// THEN
    25  		require.NotNil(t, indicator)
    26  		require.Equal(t, "test", indicator.Name())
    27  		require.NotNil(t, indicator.Status())
    28  		require.NoError(t, indicator.Status().Error())
    29  		require.Equal(t, "", indicator.Status().Details())
    30  	})
    31  }
    32  
    33  func TestRun(t *testing.T) {
    34  	t.Run("should return context timeout when timeout is reached", func(t *testing.T) {
    35  		// GIVEN
    36  		ctx, cancel := context.WithCancel(context.TODO())
    37  		defer cancel()
    38  
    39  		indicatorFunc := timeOutIndicatorFunc()
    40  		cfg := healthz.IndicatorConfig{
    41  			Name:         "First",
    42  			Interval:     time.Minute,
    43  			Timeout:      time.Nanosecond,
    44  			InitialDelay: 0,
    45  			Threshold:    0,
    46  		}
    47  
    48  		// WHEN
    49  		indicator := healthz.NewIndicator("test", indicatorFunc)
    50  		indicator.Configure(cfg)
    51  		indicator.Run(ctx)
    52  
    53  		// THEN
    54  		require.Eventually(t, func() bool {
    55  			return indicator.Status().Error() != nil
    56  		}, time.Second, time.Second/2)
    57  		require.NotNil(t, indicator)
    58  		require.NotNil(t, indicator.Status())
    59  		require.Error(t, indicator.Status().Error())
    60  		require.Contains(t, indicator.Status().Error().Error(), "timeout")
    61  		require.Contains(t, indicator.Status().Details(), "timeout")
    62  	})
    63  	t.Run("should call function on interval time", func(t *testing.T) {
    64  		// GIVEN
    65  		var counter uint64
    66  
    67  		ctx, cancel := context.WithCancel(context.TODO())
    68  		defer cancel()
    69  
    70  		cfg := healthz.IndicatorConfig{
    71  			Name:         "First",
    72  			Interval:     10 * time.Millisecond,
    73  			Timeout:      time.Second,
    74  			InitialDelay: 0,
    75  			Threshold:    0,
    76  		}
    77  		status := &automock.Status{}
    78  		status.On("Error").Return(nil)
    79  
    80  		// WHEN
    81  		indicator := healthz.NewIndicator("test", func(ctx context.Context) healthz.Status {
    82  			atomic.AddUint64(&counter, 1)
    83  			return status
    84  		})
    85  
    86  		indicator.Configure(cfg)
    87  		indicator.Run(ctx)
    88  
    89  		// THEN
    90  		require.Eventually(t, func() bool {
    91  			return atomic.LoadUint64(&counter) >= 4
    92  		}, 50*time.Millisecond, 10*time.Millisecond)
    93  		require.NotNil(t, indicator)
    94  	})
    95  	t.Run("should respect the threshold", func(t *testing.T) {
    96  		// GIVEN
    97  		ctx, cancel := context.WithCancel(context.TODO())
    98  		defer cancel()
    99  
   100  		cfg := healthz.IndicatorConfig{
   101  			Name:         "First",
   102  			Interval:     10 * time.Millisecond,
   103  			Timeout:      time.Second,
   104  			InitialDelay: 0,
   105  			Threshold:    3,
   106  		}
   107  		status := &automock.Status{}
   108  		status.On("Error").Return(errors.New("some error"))
   109  		status.On("Details").Return("some details")
   110  		// WHEN
   111  		indicator := healthz.NewIndicator("test", func(ctx context.Context) healthz.Status {
   112  			return status
   113  		})
   114  		indicator.Configure(cfg)
   115  		indicator.Run(ctx)
   116  
   117  		// THEN
   118  		require.NotNil(t, indicator)
   119  
   120  		require.NoError(t, indicator.Status().Error())
   121  		require.Eventually(t, func() bool {
   122  			return indicator.Status().Error() != nil
   123  		}, 50*time.Millisecond, 10*time.Millisecond)
   124  	})
   125  }
   126  
   127  func dummyIndicatorFunc(status *automock.Status) func(ctx context.Context) healthz.Status {
   128  	return func(ctx context.Context) healthz.Status {
   129  		return status
   130  	}
   131  }
   132  
   133  func timeOutIndicatorFunc() func(ctx context.Context) healthz.Status {
   134  	status := &automock.Status{}
   135  	status.On("Error").Return(errors.New("timeout")).Times(6)
   136  	status.On("Details").Return("some timeout details").Times(2)
   137  	return func(ctx context.Context) healthz.Status {
   138  		select {
   139  		case <-ctx.Done():
   140  			return status
   141  		case <-time.After(time.Second):
   142  		}
   143  		return nil
   144  	}
   145  }