2023-12-12 06:05:08 +05:30
|
|
|
//go:build redis
|
2020-04-21 04:50:45 +05:30
|
|
|
|
|
|
|
package redis
|
|
|
|
|
|
|
|
import (
|
2023-12-14 06:45:59 +05:30
|
|
|
"context"
|
2024-01-30 13:35:04 +05:30
|
|
|
"errors"
|
2020-04-21 04:50:45 +05:30
|
|
|
"fmt"
|
2023-12-12 06:05:08 +05:30
|
|
|
"os"
|
|
|
|
"strconv"
|
2020-04-21 04:50:45 +05:30
|
|
|
"testing"
|
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
"github.com/mediocregopher/radix/v4"
|
2024-01-30 13:35:04 +05:30
|
|
|
"github.com/stretchr/testify/mock"
|
2020-04-21 04:50:45 +05:30
|
|
|
assert "github.com/stretchr/testify/require"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
|
2024-02-01 12:42:34 +05:30
|
|
|
"ely.by/chrly/internal/db"
|
2020-04-21 04:50:45 +05:30
|
|
|
)
|
|
|
|
|
2023-12-12 06:05:08 +05:30
|
|
|
var redisAddr string
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
host := "localhost"
|
|
|
|
port := 6379
|
|
|
|
if os.Getenv("STORAGE_REDIS_HOST") != "" {
|
|
|
|
host = os.Getenv("STORAGE_REDIS_HOST")
|
|
|
|
}
|
|
|
|
|
|
|
|
if os.Getenv("STORAGE_REDIS_PORT") != "" {
|
|
|
|
port, _ = strconv.Atoi(os.Getenv("STORAGE_REDIS_PORT"))
|
|
|
|
}
|
|
|
|
|
|
|
|
redisAddr = fmt.Sprintf("%s:%d", host, port)
|
|
|
|
}
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
type MockProfileSerializer struct {
|
|
|
|
mock.Mock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockProfileSerializer) Serialize(profile *db.Profile) ([]byte, error) {
|
|
|
|
args := m.Called(profile)
|
|
|
|
|
|
|
|
return []byte(args.String(0)), args.Error(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockProfileSerializer) Deserialize(value []byte) (*db.Profile, error) {
|
|
|
|
args := m.Called(value)
|
|
|
|
var result *db.Profile
|
|
|
|
if casted, ok := args.Get(0).(*db.Profile); ok {
|
|
|
|
result = casted
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, args.Error(1)
|
|
|
|
}
|
|
|
|
|
2020-04-21 04:50:45 +05:30
|
|
|
func TestNew(t *testing.T) {
|
|
|
|
t.Run("should connect", func(t *testing.T) {
|
2024-01-30 13:35:04 +05:30
|
|
|
conn, err := New(context.Background(), &MockProfileSerializer{}, redisAddr, 12)
|
2020-04-21 04:50:45 +05:30
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.NotNil(t, conn)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("should return error", func(t *testing.T) {
|
2024-01-30 13:35:04 +05:30
|
|
|
conn, err := New(context.Background(), &MockProfileSerializer{}, "localhost:12345", 12) // Use localhost to avoid DNS resolution
|
2020-04-21 04:50:45 +05:30
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, conn)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type redisTestSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
Redis *Redis
|
|
|
|
Serializer *MockProfileSerializer
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
cmd func(cmd string, args ...interface{}) string
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) SetupSuite() {
|
|
|
|
s.Serializer = &MockProfileSerializer{}
|
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
ctx := context.Background()
|
2024-01-30 13:35:04 +05:30
|
|
|
conn, err := New(ctx, s.Serializer, redisAddr, 10)
|
2020-04-21 04:50:45 +05:30
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("cannot establish connection to redis: %w", err))
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Redis = conn
|
|
|
|
s.cmd = func(cmd string, args ...interface{}) string {
|
2023-12-14 06:45:59 +05:30
|
|
|
var result string
|
2024-01-30 13:35:04 +05:30
|
|
|
err := s.Redis.client.Do(ctx, radix.FlatCmd(&result, cmd, args...))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) SetupSubTest() {
|
2023-12-14 06:45:59 +05:30
|
|
|
// Cleanup database before each test
|
2024-01-30 13:35:04 +05:30
|
|
|
s.cmd("FLUSHALL")
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TearDownSubTest() {
|
|
|
|
s.Serializer.AssertExpectations(s.T())
|
|
|
|
for _, call := range s.Serializer.ExpectedCalls {
|
|
|
|
call.Unset()
|
|
|
|
}
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
func TestRedis(t *testing.T) {
|
|
|
|
suite.Run(t, new(redisTestSuite))
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestFindProfileByUsername() {
|
|
|
|
s.Run("exists record", func() {
|
|
|
|
serializedData := []byte("mock.exists.profile")
|
|
|
|
expectedProfile := &db.Profile{}
|
|
|
|
s.cmd("HSET", usernameToProfileKey, "mock", serializedData)
|
|
|
|
s.Serializer.On("Deserialize", serializedData).Return(expectedProfile, nil)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
profile, err := s.Redis.FindProfileByUsername("Mock")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Same(expectedProfile, profile)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("not exists record", func() {
|
|
|
|
profile, err := s.Redis.FindProfileByUsername("Mock")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Nil(profile)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("an error from serializer implementation", func() {
|
|
|
|
expectedError := errors.New("mock error")
|
|
|
|
s.cmd("HSET", usernameToProfileKey, "mock", "some-invalid-mock-data")
|
|
|
|
s.Serializer.On("Deserialize", mock.Anything).Return(nil, expectedError)
|
|
|
|
|
|
|
|
profile, err := s.Redis.FindProfileByUsername("Mock")
|
|
|
|
s.Require().Nil(profile)
|
|
|
|
s.Require().ErrorIs(err, expectedError)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestFindProfileByUuid() {
|
|
|
|
s.Run("exists record", func() {
|
|
|
|
serializedData := []byte("mock.exists.profile")
|
|
|
|
expectedProfile := &db.Profile{Username: "Mock"}
|
|
|
|
s.cmd("HSET", usernameToProfileKey, "mock", serializedData)
|
|
|
|
s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock")
|
|
|
|
s.Serializer.On("Deserialize", serializedData).Return(expectedProfile, nil)
|
|
|
|
|
|
|
|
profile, err := s.Redis.FindProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Same(expectedProfile, profile)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("not exists record", func() {
|
|
|
|
profile, err := s.Redis.FindProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Nil(profile)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("exists uuid record, but related profile not exists", func() {
|
|
|
|
s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock")
|
|
|
|
profile, err := s.Redis.FindProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Nil(profile)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestSaveProfile() {
|
|
|
|
s.Run("save new entity", func() {
|
|
|
|
profile := &db.Profile{
|
|
|
|
Uuid: "f57f36d5-4f50-4728-948a-42d5d80b18f3",
|
|
|
|
Username: "Mock",
|
|
|
|
}
|
|
|
|
serializedProfile := "serialized-profile"
|
|
|
|
s.Serializer.On("Serialize", profile).Return(serializedProfile, nil)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.cmd("HSET", usernameToProfileKey, "mock", serializedProfile)
|
|
|
|
s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock")
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err := s.Redis.SaveProfile(profile)
|
|
|
|
s.Require().NoError(err)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3")
|
|
|
|
s.Require().Equal("mock", uuidResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
profileResp := s.cmd("HGET", usernameToProfileKey, "mock")
|
|
|
|
s.Require().Equal(serializedProfile, profileResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("update exists record with changed username", func() {
|
|
|
|
newProfile := &db.Profile{
|
|
|
|
Uuid: "f57f36d5-4f50-4728-948a-42d5d80b18f3",
|
|
|
|
Username: "NewMock",
|
|
|
|
}
|
|
|
|
serializedNewProfile := "serialized-new-profile"
|
|
|
|
s.Serializer.On("Serialize", newProfile).Return(serializedNewProfile, nil)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.cmd("HSET", usernameToProfileKey, "mock", "serialized-old-profile")
|
|
|
|
s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock")
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err := s.Redis.SaveProfile(newProfile)
|
|
|
|
s.Require().NoError(err)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3")
|
|
|
|
s.Require().Equal("newmock", uuidResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
newProfileResp := s.cmd("HGET", usernameToProfileKey, "newmock")
|
|
|
|
s.Require().Equal(serializedNewProfile, newProfileResp)
|
|
|
|
|
|
|
|
oldProfileResp := s.cmd("HGET", usernameToProfileKey, "mock")
|
|
|
|
s.Require().Empty(oldProfileResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestRemoveProfileByUuid() {
|
|
|
|
s.Run("exists record", func() {
|
|
|
|
s.cmd("HSET", usernameToProfileKey, "mock", "serialized-profile")
|
|
|
|
s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock")
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err := s.Redis.RemoveProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3")
|
|
|
|
s.Require().NoError(err)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3")
|
|
|
|
s.Require().Empty(uuidResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
profileResp := s.cmd("HGET", usernameToProfileKey, "mock")
|
|
|
|
s.Require().Empty(profileResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("uuid exists, username is missing", func() {
|
|
|
|
s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock")
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err := s.Redis.RemoveProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3")
|
|
|
|
s.Require().NoError(err)
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3")
|
|
|
|
s.Require().Empty(uuidResp)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("uuid not exists", func() {
|
|
|
|
err := s.Redis.RemoveProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3")
|
|
|
|
s.Require().NoError(err)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestGetUuidForMojangUsername() {
|
|
|
|
s.Run("exists record", func() {
|
|
|
|
s.cmd("SET", "mojang:uuid:mock", "MoCk:d3ca513eb3e14946b58047f2bd3530fd")
|
2024-01-10 06:12:10 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
uuid, username, err := s.Redis.GetUuidForMojangUsername("Mock")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal("MoCk", username)
|
|
|
|
s.Require().Equal("d3ca513eb3e14946b58047f2bd3530fd", uuid)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("exists record with empty uuid value", func() {
|
|
|
|
s.cmd("SET", "mojang:uuid:mock", "MoCk:")
|
2020-04-28 20:27:51 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
uuid, username, err := s.Redis.GetUuidForMojangUsername("Mock")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal("MoCk", username)
|
|
|
|
s.Require().Empty(uuid)
|
2020-04-21 04:50:45 +05:30
|
|
|
})
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("not exists record", func() {
|
|
|
|
uuid, username, err := s.Redis.GetUuidForMojangUsername("Mock")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Empty(username)
|
|
|
|
s.Require().Empty(uuid)
|
2021-03-03 06:02:38 +05:30
|
|
|
})
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestStoreUuid() {
|
|
|
|
s.Run("store uuid", func() {
|
|
|
|
err := s.Redis.StoreMojangUuid("MoCk", "d3ca513eb3e14946b58047f2bd3530fd")
|
|
|
|
s.Require().NoError(err)
|
2021-02-07 21:17:21 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
resp := s.cmd("GET", "mojang:uuid:mock")
|
|
|
|
s.Require().Equal(resp, "MoCk:d3ca513eb3e14946b58047f2bd3530fd")
|
2021-02-07 21:17:21 +05:30
|
|
|
})
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Run("store empty uuid", func() {
|
|
|
|
err := s.Redis.StoreMojangUuid("MoCk", "")
|
|
|
|
s.Require().NoError(err)
|
2021-02-07 21:17:21 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
resp := s.cmd("GET", "mojang:uuid:mock")
|
|
|
|
s.Require().Equal(resp, "MoCk:")
|
2021-02-07 21:17:21 +05:30
|
|
|
})
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (s *redisTestSuite) TestPing() {
|
2024-02-07 22:04:57 +05:30
|
|
|
err := s.Redis.Ping(context.Background())
|
2024-01-30 13:35:04 +05:30
|
|
|
s.Require().Nil(err)
|
2020-04-21 04:50:45 +05:30
|
|
|
}
|