flamingo.me/flamingo-commerce/v3@v3.11.0/checkout/infrastructure/locker/redis_test.go (about)

     1  package locker_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os/exec"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/stvp/tempredis"
    13  	"github.com/testcontainers/testcontainers-go"
    14  	"github.com/testcontainers/testcontainers-go/wait"
    15  	"go.uber.org/goleak"
    16  
    17  	"flamingo.me/flamingo-commerce/v3/checkout/infrastructure/locker"
    18  )
    19  
    20  func startUp(t *testing.T) *tempredis.Server {
    21  	t.Helper()
    22  	server, err := tempredis.Start(tempredis.Config{})
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  
    27  	return server
    28  }
    29  
    30  func getRedisLocker(network, address string) *locker.Redis {
    31  	redis := locker.NewRedis(&struct {
    32  		MaxIdle                 int    `inject:"config:commerce.checkout.placeorder.lock.redis.maxIdle"`
    33  		IdleTimeoutMilliseconds int    `inject:"config:commerce.checkout.placeorder.lock.redis.idleTimeoutMilliseconds"`
    34  		Network                 string `inject:"config:commerce.checkout.placeorder.lock.redis.network"`
    35  		Address                 string `inject:"config:commerce.checkout.placeorder.lock.redis.address"`
    36  		Database                int    `inject:"config:commerce.checkout.placeorder.lock.redis.database"`
    37  	}{MaxIdle: 3, IdleTimeoutMilliseconds: 240000, Network: network, Address: address, Database: 0})
    38  	return redis
    39  }
    40  
    41  func TestRedis_TryLockDocker(t *testing.T) {
    42  	ctx := context.Background()
    43  	req := testcontainers.ContainerRequest{
    44  		Image:        "redis:latest",
    45  		ExposedPorts: []string{"6379/tcp"},
    46  		WaitingFor:   wait.ForLog("Ready to accept connections"),
    47  	}
    48  	redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
    49  		ContainerRequest: req,
    50  		Started:          true,
    51  	})
    52  	require.NoError(t, err)
    53  	defer func() { _ = redisC.Terminate(ctx) }()
    54  	defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
    55  
    56  	port, err := redisC.MappedPort(ctx, "6379")
    57  	require.NoError(t, err)
    58  
    59  	host, err := redisC.Host(ctx)
    60  	require.NoError(t, err)
    61  	address := fmt.Sprintf("%s:%s", host, port.Port())
    62  
    63  	redisLocker := getRedisLocker("tcp", address)
    64  	runTestCases(t, redisLocker)
    65  
    66  }
    67  
    68  func TestRedis_TryLock(t *testing.T) {
    69  	defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
    70  
    71  	if _, err := exec.LookPath("redis-server"); err != nil {
    72  		t.Skip("redis-server not installed")
    73  	}
    74  
    75  	server := startUp(t)
    76  	defer func() { _ = server.Term() }()
    77  	redisLocker := getRedisLocker("unix", server.Socket())
    78  
    79  	runTestCases(t, redisLocker)
    80  
    81  }
    82  
    83  func runTestCases(t *testing.T, redisLocker *locker.Redis) {
    84  	t.Run("really locked", func(t *testing.T) {
    85  		key := "test"
    86  		start := time.Now()
    87  		// get a long lock
    88  		unlock, err := redisLocker.TryLock(context.Background(), key, time.Minute)
    89  		require.NoError(t, err)
    90  
    91  		// try to get same lock
    92  		_, err = redisLocker.TryLock(context.Background(), key, time.Second)
    93  		assert.Error(t, err)
    94  		// assert if we were really in the lock period
    95  		assert.True(t, time.Now().Before(start.Add(time.Minute)))
    96  
    97  		// unlock
    98  		require.NoError(t, unlock())
    99  
   100  		// get the lock successfully again after unlock
   101  		unlock, err = redisLocker.TryLock(context.Background(), key, time.Minute)
   102  		require.NoError(t, err)
   103  		require.NoError(t, unlock())
   104  	})
   105  
   106  	t.Run("lock should be expanded", func(t *testing.T) {
   107  		key := "test_expanded"
   108  
   109  		unlock, err := redisLocker.TryLock(context.Background(), key, 100*time.Millisecond)
   110  		require.NoError(t, err)
   111  		defer func() { _ = unlock() }()
   112  
   113  		time.Sleep(200 * time.Millisecond)
   114  		// try to get same lock
   115  		_, err = redisLocker.TryLock(context.Background(), key, time.Second)
   116  		assert.Error(t, err)
   117  	})
   118  }
   119  
   120  func TestRedis_StatusDocker(t *testing.T) {
   121  	t.Run("redis not ready", func(t *testing.T) {
   122  		redisLocker := getRedisLocker("tcp", "127.0.0.1:80")
   123  		alive, _ := redisLocker.Status()
   124  
   125  		assert.False(t, alive)
   126  	})
   127  
   128  	t.Run("redis is there", func(t *testing.T) {
   129  		ctx := context.Background()
   130  		req := testcontainers.ContainerRequest{
   131  			Image:        "redis:latest",
   132  			ExposedPorts: []string{"6379/tcp"},
   133  			WaitingFor:   wait.ForLog("Ready to accept connections"),
   134  		}
   135  		redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
   136  			ContainerRequest: req,
   137  			Started:          true,
   138  		})
   139  		require.NoError(t, err)
   140  		defer func() { _ = redisC.Terminate(ctx) }()
   141  		defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
   142  
   143  		port, err := redisC.MappedPort(ctx, "6379")
   144  		require.NoError(t, err)
   145  
   146  		host, err := redisC.Host(ctx)
   147  		require.NoError(t, err)
   148  		address := fmt.Sprintf("%s:%s", host, port.Port())
   149  
   150  		redisLocker := getRedisLocker("tcp", address)
   151  		alive, _ := redisLocker.Status()
   152  
   153  		assert.True(t, alive)
   154  	})
   155  }