package repository import ( "context" "fmt" "git.sch9.ru/new_gate/ms-auth/internal/models" "git.sch9.ru/new_gate/ms-auth/pkg" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/valkey-io/valkey-go" "github.com/valkey-io/valkey-go/mock" "go.uber.org/mock/gomock" "strings" "testing" ) var ( matcherAny = mock.MatchFn(func(cmd []string) bool { return true }) ) func TestValkeyRepository_CreateSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewClient(ctrl) sessionRepo := NewValkeyRepository(client) var userId int32 = 1 matcher := mock.MatchFn(func(cmd []string) bool { if cmd[0] != "SET" { return false } if !strings.HasPrefix(cmd[1], fmt.Sprintf("userid:%d:sessionid:", userId)) { return false } if cmd[3] != "EX" { return false } if cmd[4] != "2400" { return false } return true }) t.Run("valid session creation", func(t *testing.T) { ctx := context.Background() client.EXPECT().Do(ctx, matcher) sessionId, err := sessionRepo.CreateSession(context.Background(), userId, models.RoleAdmin) require.NoError(t, err) require.NotEmpty(t, sessionId) }) t.Run("invalid session creation 1", func(t *testing.T) { ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.ErrorResult(valkey.Nil)) sessionId, err := sessionRepo.CreateSession(context.Background(), userId, models.RoleAdmin) require.ErrorIs(t, err, pkg.ErrBadInput) require.ErrorIs(t, err, valkey.Nil) require.Empty(t, sessionId) }) t.Run("invalid session creation 2 (invalid userid)", func(t *testing.T) { sessionId, err := sessionRepo.CreateSession(context.Background(), 0, models.RoleAdmin) require.ErrorIs(t, err, pkg.ErrBadInput) require.Empty(t, sessionId) }) t.Run("invalid session creation 3 (invalid role)", func(t *testing.T) { sessionId, err := sessionRepo.CreateSession(context.Background(), userId, 123) require.ErrorIs(t, err, pkg.ErrBadInput) require.Empty(t, sessionId) }) } func TestValkeyRepository_ReadSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewClient(ctrl) sessionRepo := NewValkeyRepository(client) matcher := mock.MatchFn(func(cmd []string) bool { if cmd[0] != "EVALSHA" { return false } if cmd[2] != "0" { return false } if !strings.HasPrefix(cmd[3], "userid:*:sessionid:") { return false } return true }) t.Run("valid session read", func(t *testing.T) { data, id, err := models.NewSession(1, models.RoleAdmin) require.NoError(t, err) ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.Result(mock.ValkeyString(data))) res, err := sessionRepo.ReadSession(context.Background(), id) require.NoError(t, err) require.Equal(t, int32(1), res.UserId) require.Equal(t, id, res.Id) require.Equal(t, models.RoleAdmin, res.Role) }) t.Run("invalid session read 1 (not found)", func(t *testing.T) { _, id, err := models.NewSession(1, models.RoleAdmin) require.NoError(t, err) ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.ErrorResult(valkey.Nil)) res, err := sessionRepo.ReadSession(context.Background(), id) require.ErrorIs(t, err, pkg.ErrNotFound) require.ErrorIs(t, err, valkey.Nil) require.Empty(t, res) }) t.Run("invalid session read 2 (corrupted session storage)", func(t *testing.T) { _, id, err := models.NewSession(1, models.RoleAdmin) require.NoError(t, err) ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.Result(mock.ValkeyInt64(123))) res, err := sessionRepo.ReadSession(context.Background(), id) require.ErrorIs(t, err, pkg.ErrInternal) require.True(t, valkey.IsParseErr(err)) require.Empty(t, res) }) t.Run("invalid session read 3 (bad sessionid)", func(t *testing.T) { res, err := sessionRepo.ReadSession(context.Background(), "123") require.ErrorIs(t, err, pkg.ErrBadInput) require.Empty(t, res) }) } func TestValkeyRepository_UpdateSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewClient(ctrl) sessionRepo := NewValkeyRepository(client) matcher := mock.MatchFn(func(cmd []string) bool { if cmd[0] != "EVALSHA" { return false } if cmd[2] != "0" { return false } if !strings.HasPrefix(cmd[3], "userid:*:sessionid:") { return false } return true }) t.Run("valid session update", func(t *testing.T) { id := uuid.NewString() ctx := context.Background() client.EXPECT().Do(ctx, matcher) err := sessionRepo.UpdateSession(context.Background(), id) require.NoError(t, err) }) t.Run("invalid session update 1 (nil response)", func(t *testing.T) { id := uuid.NewString() ctx := context.Background() client.EXPECT().Do(ctx, matcherAny).Return(mock.ErrorResult(valkey.Nil)) err := sessionRepo.UpdateSession(context.Background(), id) require.ErrorIs(t, err, pkg.ErrBadInput) require.ErrorIs(t, err, valkey.Nil) }) t.Run("invalid session update 2 (bad sessionid)", func(t *testing.T) { err := sessionRepo.UpdateSession(context.Background(), "123") require.ErrorIs(t, err, pkg.ErrBadInput) }) } func TestValkeyRepository_DeleteSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewClient(ctrl) sessionRepo := NewValkeyRepository(client) matcher := mock.MatchFn(func(cmd []string) bool { if cmd[0] != "EVALSHA" { return false } if cmd[2] != "0" { return false } if !strings.HasPrefix(cmd[3], "userid:*:sessionid:") { return false } return true }) t.Run("valid session delete", func(t *testing.T) { id := uuid.NewString() ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.Result(mock.ValkeyInt64(1))) err := sessionRepo.DeleteSession(context.Background(), id) require.NoError(t, err) }) t.Run("invalid session delete 1", func(t *testing.T) { id := uuid.NewString() ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.Result(mock.ValkeyNil())) err := sessionRepo.DeleteSession(context.Background(), id) require.ErrorIs(t, err, pkg.ErrBadInput) require.ErrorIs(t, err, valkey.Nil) }) t.Run("invalid session delete 2 (bad sessionid)", func(t *testing.T) { err := sessionRepo.DeleteSession(context.Background(), "123") require.ErrorIs(t, err, pkg.ErrBadInput) }) } func TestValkeyRepository_DeleteAllSessions(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewClient(ctrl) sessionRepo := NewValkeyRepository(client) matcher := mock.MatchFn(func(cmd []string) bool { if cmd[0] != "EVALSHA" { return false } if cmd[2] != "0" { return false } if !strings.HasPrefix(cmd[3], "userid:1:sessionid:*") { return false } return true }) t.Run("valid all sessions deletion", func(t *testing.T) { ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.Result(mock.ValkeyInt64(1))) err := sessionRepo.DeleteAllSessions(context.Background(), 1) require.NoError(t, err) }) t.Run("invalid all sessions deletion 1 (nil response)", func(t *testing.T) { ctx := context.Background() client.EXPECT().Do(ctx, matcher).Return(mock.Result(mock.ValkeyNil())) err := sessionRepo.DeleteAllSessions(context.Background(), 1) require.ErrorIs(t, err, pkg.ErrBadInput) require.ErrorIs(t, err, valkey.Nil) }) }