github.com/argoproj/argo-cd/v3@v3.2.1/util/cache/redis_test.go (about)

     1  package cache
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"io"
     7  	"strconv"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/alicebob/miniredis/v2"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	promcm "github.com/prometheus/client_model/go"
    14  	"github.com/redis/go-redis/v9"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  var (
    20  	redisRequestCounter = prometheus.NewCounterVec(
    21  		prometheus.CounterOpts{
    22  			Name: "argocd_redis_request_total",
    23  		},
    24  		[]string{"initiator", "failed"},
    25  	)
    26  	redisRequestHistogram = prometheus.NewHistogramVec(
    27  		prometheus.HistogramOpts{
    28  			Name:    "argocd_redis_request_duration",
    29  			Buckets: []float64{0.1, 0.25, .5, 1, 2},
    30  		},
    31  		[]string{"initiator"},
    32  	)
    33  )
    34  
    35  type MockMetricsServer struct {
    36  	redisRequestCounter   *prometheus.CounterVec
    37  	redisRequestHistogram *prometheus.HistogramVec
    38  }
    39  
    40  func NewMockMetricsServer() *MockMetricsServer {
    41  	registry := prometheus.NewRegistry()
    42  	registry.MustRegister(redisRequestCounter)
    43  	registry.MustRegister(redisRequestHistogram)
    44  	return &MockMetricsServer{
    45  		redisRequestCounter:   redisRequestCounter,
    46  		redisRequestHistogram: redisRequestHistogram,
    47  	}
    48  }
    49  
    50  func (m *MockMetricsServer) IncRedisRequest(failed bool) {
    51  	m.redisRequestCounter.WithLabelValues("mock", strconv.FormatBool(failed)).Inc()
    52  }
    53  
    54  func (m *MockMetricsServer) ObserveRedisRequestDuration(duration time.Duration) {
    55  	m.redisRequestHistogram.WithLabelValues("mock").Observe(duration.Seconds())
    56  }
    57  
    58  func TestRedisSetCache(t *testing.T) {
    59  	mr, err := miniredis.Run()
    60  	if err != nil {
    61  		panic(err)
    62  	}
    63  	defer mr.Close()
    64  	assert.NotNil(t, mr)
    65  
    66  	t.Run("Successful set", func(t *testing.T) {
    67  		client := NewRedisCache(redis.NewClient(&redis.Options{Addr: mr.Addr()}), 60*time.Second, RedisCompressionNone)
    68  		err = client.Set(&Item{Key: "foo", Object: "bar"})
    69  		require.NoError(t, err)
    70  	})
    71  
    72  	t.Run("Successful get", func(t *testing.T) {
    73  		var res string
    74  		client := NewRedisCache(redis.NewClient(&redis.Options{Addr: mr.Addr()}), 10*time.Second, RedisCompressionNone)
    75  		err = client.Get("foo", &res)
    76  		require.NoError(t, err)
    77  		assert.Equal(t, "bar", res)
    78  	})
    79  
    80  	t.Run("Successful delete", func(t *testing.T) {
    81  		client := NewRedisCache(redis.NewClient(&redis.Options{Addr: mr.Addr()}), 10*time.Second, RedisCompressionNone)
    82  		err = client.Delete("foo")
    83  		require.NoError(t, err)
    84  	})
    85  
    86  	t.Run("Cache miss", func(t *testing.T) {
    87  		var res string
    88  		client := NewRedisCache(redis.NewClient(&redis.Options{Addr: mr.Addr()}), 10*time.Second, RedisCompressionNone)
    89  		err = client.Get("foo", &res)
    90  		assert.ErrorContains(t, err, "cache: key is missing")
    91  	})
    92  }
    93  
    94  func TestRedisSetCacheCompressed(t *testing.T) {
    95  	mr, err := miniredis.Run()
    96  	if err != nil {
    97  		panic(err)
    98  	}
    99  	defer mr.Close()
   100  	assert.NotNil(t, mr)
   101  
   102  	redisClient := redis.NewClient(&redis.Options{Addr: mr.Addr()})
   103  
   104  	client := NewRedisCache(redisClient, 10*time.Second, RedisCompressionGZip)
   105  	testValue := "my-value"
   106  	require.NoError(t, client.Set(&Item{Key: "my-key", Object: testValue}))
   107  
   108  	compressedData, err := redisClient.Get(t.Context(), "my-key.gz").Bytes()
   109  	require.NoError(t, err)
   110  
   111  	assert.Greater(t, len(compressedData), len([]byte(testValue)), "compressed data is bigger than uncompressed")
   112  
   113  	// trying to unzip compressed data
   114  	gzipReader, err := gzip.NewReader(bytes.NewBuffer(compressedData))
   115  	require.NoError(t, err)
   116  	_, err = io.ReadAll(gzipReader)
   117  	require.NoError(t, err)
   118  
   119  	var result string
   120  	require.NoError(t, client.Get("my-key", &result))
   121  
   122  	assert.Equal(t, testValue, result)
   123  }
   124  
   125  func TestRedisMetrics(t *testing.T) {
   126  	mr, err := miniredis.Run()
   127  	if err != nil {
   128  		panic(err)
   129  	}
   130  	defer mr.Close()
   131  
   132  	metric := &promcm.Metric{}
   133  	ms := NewMockMetricsServer()
   134  	redisClient := redis.NewClient(&redis.Options{Addr: mr.Addr()})
   135  	faultyRedisClient := redis.NewClient(&redis.Options{Addr: "invalidredishost.invalid:12345"})
   136  	CollectMetrics(redisClient, ms, nil)
   137  	CollectMetrics(faultyRedisClient, ms, nil)
   138  
   139  	client := NewRedisCache(redisClient, 60*time.Second, RedisCompressionNone)
   140  	faultyClient := NewRedisCache(faultyRedisClient, 60*time.Second, RedisCompressionNone)
   141  	var res string
   142  
   143  	// client successful request
   144  	err = client.Set(&Item{Key: "foo", Object: "bar"})
   145  	require.NoError(t, err)
   146  	err = client.Get("foo", &res)
   147  	require.NoError(t, err)
   148  
   149  	c, err := ms.redisRequestCounter.GetMetricWithLabelValues("mock", "false")
   150  	require.NoError(t, err)
   151  	err = c.Write(metric)
   152  	require.NoError(t, err)
   153  	assert.InEpsilon(t, float64(2), metric.Counter.GetValue(), 0.0001)
   154  
   155  	// faulty client failed request
   156  	err = faultyClient.Get("foo", &res)
   157  	require.Error(t, err)
   158  	c, err = ms.redisRequestCounter.GetMetricWithLabelValues("mock", "true")
   159  	require.NoError(t, err)
   160  	err = c.Write(metric)
   161  	require.NoError(t, err)
   162  	assert.InEpsilon(t, float64(1), metric.Counter.GetValue(), 0.0001)
   163  
   164  	// both clients histogram count
   165  	o, err := ms.redisRequestHistogram.GetMetricWithLabelValues("mock")
   166  	require.NoError(t, err)
   167  	err = o.(prometheus.Metric).Write(metric)
   168  	require.NoError(t, err)
   169  	assert.Equal(t, 3, int(metric.Histogram.GetSampleCount()))
   170  }