package usecase import ( "context" "git.sch9.ru/new_gate/ms-auth/config" "git.sch9.ru/new_gate/ms-auth/internal/models" mock_users "git.sch9.ru/new_gate/ms-auth/internal/users/usecase/mock" "git.sch9.ru/new_gate/ms-auth/pkg" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "testing" ) func TestUseCase_CreateUser(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() pgRepository := mock_users.NewMockPgRepository(ctrl) vkRepository := mock_users.NewMockValkeyRepository(ctrl) caller := mock_users.NewMockCaller(ctrl) uc := NewUseCase( pgRepository, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid create user (admin > moderator)", func(t *testing.T) { userId := int32(1) username := "username" password := "password" role := models.RoleModerator ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).Times(2) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleAdmin, }, nil) caller.EXPECT().CreateUser(ctx, username, password, role).Return(int32(2), nil) id, err := uc.CreateUser(ctx, username, password, role) require.NoError(t, err) require.Equal(t, int32(2), id) }) t.Run("valid create user (moderator > participant)", func(t *testing.T) { userId := int32(1) username := "username" password := "password" role := models.RoleParticipant ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).Times(2) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleModerator, }, nil) caller.EXPECT().CreateUser(ctx, username, password, role).Return(int32(2), nil) id, err := uc.CreateUser(ctx, username, password, role) require.NoError(t, err) require.Equal(t, int32(2), id) }) t.Run("valid create user (admin > participant)", func(t *testing.T) { userId := int32(1) username := "username" password := "password" role := models.RoleParticipant ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).Times(2) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleAdmin, }, nil) caller.EXPECT().CreateUser(ctx, username, password, role).Return(int32(2), nil) id, err := uc.CreateUser(ctx, username, password, role) require.NoError(t, err) require.Equal(t, int32(2), id) }) t.Run("invalid user create 1 (no user id in context)", func(t *testing.T) { _, err := uc.CreateUser(context.Background(), "username", "password", models.RoleModerator) require.Error(t, err) require.ErrorIs(t, err, pkg.ErrUnauthenticated) }) t.Run("invalid user create 2 (user not found)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserById(ctx, userId).Return(nil, pkg.ErrNotFound) _, err := uc.CreateUser(ctx, "username", "password", models.RoleModerator) require.Error(t, err) require.ErrorIs(t, err, pkg.ErrNotFound) }) t.Run("invalid user create 3 (no permission, participant < admin)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleParticipant, }, nil) _, err := uc.CreateUser(ctx, "username", "password", models.RoleAdmin) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) t.Run("invalid user create 4 (no permission, participant < moderator)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleParticipant, }, nil) _, err := uc.CreateUser(ctx, "username", "password", models.RoleModerator) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) t.Run("invalid user create 5 (no permission, moderator < admin)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleModerator, }, nil) _, err := uc.CreateUser(ctx, "username", "password", models.RoleAdmin) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) t.Run("invalid user create 6 (bad input, bad username)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).Times(2) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleModerator, }, nil) caller.EXPECT().CreateUser(ctx, "test", "password", models.RoleParticipant, ).Return(int32(0), pkg.ErrBadInput) _, err := uc.CreateUser(ctx, "test", "password", models.RoleParticipant) require.Error(t, err) require.ErrorIs(t, err, pkg.ErrBadInput) }) } func TestUseCase_ReadUserById(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() pgRepository := mock_users.NewMockPgRepository(ctrl) vkRepository := mock_users.NewMockValkeyRepository(ctrl) caller := mock_users.NewMockCaller(ctrl) uc := NewUseCase( pgRepository, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid user read", func(t *testing.T) { userId := int32(1) ctx := context.Background() pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{Id: userId}, nil) user, err := uc.ReadUserById(ctx, userId) require.NoError(t, err) require.Equal(t, userId, user.Id) }) t.Run("invalid user read 1 (not found)", func(t *testing.T) { userId := int32(0) ctx := context.Background() pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserById(ctx, userId).Return(nil, pkg.ErrNotFound) _, err := uc.ReadUserById(ctx, userId) require.Error(t, err) require.ErrorIs(t, err, pkg.ErrNotFound) }) } func TestUseCase_ReadUserByUsername(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() pgRepository := mock_users.NewMockPgRepository(ctrl) vkRepository := mock_users.NewMockValkeyRepository(ctrl) caller := mock_users.NewMockCaller(ctrl) uc := NewUseCase( pgRepository, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid user read", func(t *testing.T) { username := "username" ctx := context.Background() pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserByUsername(ctx, username).Return(&models.User{Username: username}, nil) user, err := uc.ReadUserByUsername(ctx, username) require.NoError(t, err) require.Equal(t, username, user.Username) }) t.Run("invalid user read 1 (not found)", func(t *testing.T) { username := "username" ctx := context.Background() pgRepository.EXPECT().C().Return(caller) caller.EXPECT().ReadUserByUsername(ctx, username).Return(nil, pkg.ErrNotFound) _, err := uc.ReadUserByUsername(ctx, username) require.Error(t, err) require.ErrorIs(t, err, pkg.ErrNotFound) }) } func TestUseCase_UpdateUser(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() pgRepository := mock_users.NewMockPgRepository(ctrl) vkRepository := mock_users.NewMockValkeyRepository(ctrl) caller := mock_users.NewMockCaller(ctrl) txCaller := mock_users.NewMockTxCaller(ctrl) uc := NewUseCase( pgRepository, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid user update", func(t *testing.T) { userId := int32(1) userId2 := int32(2) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).AnyTimes() caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleAdmin, }, nil) caller.EXPECT().ReadUserById(ctx, userId2).Return(&models.User{ Id: userId2, Role: models.RoleModerator, }, nil) pgRepository.EXPECT().BeginTx(ctx).Return(txCaller, nil) txCaller.EXPECT().UpdateUser(ctx, userId2, StringP("newusername"), RoleP(models.RoleParticipant), ).Return(nil) vkRepository.EXPECT().DeleteAllSessions(ctx, userId2).Return(nil) txCaller.EXPECT().Commit().Return(nil) err := uc.UpdateUser(ctx, userId2, StringP("newusername"), RoleP(models.RoleParticipant)) require.NoError(t, err) }) t.Run("invalid user update 1 (no user id in context)", func(t *testing.T) { pgRepository.EXPECT().C().Return(caller).AnyTimes() err := uc.UpdateUser(context.Background(), 0, StringP("newusername"), RoleP(models.RoleParticipant)) require.Error(t, err) require.ErrorIs(t, err, pkg.ErrUnauthenticated) }) t.Run("invalid user update 2 (cant update role of myself)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).AnyTimes() caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleAdmin, }, nil).Times(2) err := uc.UpdateUser(ctx, userId, StringP("newusername"), RoleP(models.RoleParticipant)) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) t.Run("invalid user update 3 (cant set role >= my role)", func(t *testing.T) { userId := int32(1) userId2 := int32(2) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).AnyTimes() caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleModerator, }, nil) caller.EXPECT().ReadUserById(ctx, userId2).Return(&models.User{ Id: userId2, Role: models.RoleParticipant, }, nil) err := uc.UpdateUser(ctx, userId2, StringP("newusername"), RoleP(models.RoleModerator)) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) t.Run("invalid user update 4 (cant edit user with >= role than mine)", func(t *testing.T) { userId := int32(1) userId2 := int32(2) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).AnyTimes() caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleModerator, }, nil) caller.EXPECT().ReadUserById(ctx, userId2).Return(&models.User{ Id: userId2, Role: models.RoleModerator, }, nil) err := uc.UpdateUser(ctx, userId2, StringP("newusername"), RoleP(models.RoleParticipant)) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) } func TestUseCase_DeleteUser(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() pgRepository := mock_users.NewMockPgRepository(ctrl) vkRepository := mock_users.NewMockValkeyRepository(ctrl) caller := mock_users.NewMockCaller(ctrl) txCaller := mock_users.NewMockTxCaller(ctrl) uc := NewUseCase( pgRepository, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid user delete", func(t *testing.T) { userId := int32(1) userId2 := int32(2) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).AnyTimes() caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleAdmin, }, nil) pgRepository.EXPECT().BeginTx(ctx).Return(txCaller, nil) vkRepository.EXPECT().DeleteAllSessions(ctx, userId2).Return(nil) txCaller.EXPECT().DeleteUser(ctx, userId2).Return(nil) txCaller.EXPECT().Commit().Return(nil) err := uc.DeleteUser(ctx, userId2) require.NoError(t, err) }) t.Run("invalid delete (cant delete myself)", func(t *testing.T) { userId := int32(1) ctx := context.WithValue(context.Background(), "userId", userId) pgRepository.EXPECT().C().Return(caller).AnyTimes() caller.EXPECT().ReadUserById(ctx, userId).Return(&models.User{ Id: userId, Role: models.RoleAdmin, }, nil) err := uc.DeleteUser(ctx, userId) require.Error(t, err) require.ErrorIs(t, err, pkg.NoPermission) }) } func TestUseCase_CreateSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() vkRepository := mock_users.NewMockValkeyRepository(ctrl) uc := NewUseCase( nil, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid session creation", func(t *testing.T) { ctx := context.Background() sid := uuid.NewString() vkRepository.EXPECT().CreateSession(ctx, int32(1), models.RoleAdmin).Return(sid, nil) sessionId, err := uc.CreateSession(ctx, int32(1), models.RoleAdmin) require.NoError(t, err) require.Equal(t, sessionId, sid) }) } func TestUseCase_ReadSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() vkRepository := mock_users.NewMockValkeyRepository(ctrl) uc := NewUseCase( nil, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid session read", func(t *testing.T) { ctx := context.Background() sid := uuid.NewString() vkRepository.EXPECT().ReadSession(ctx, sid).Return(&models.Session{ UserId: 1, Id: sid, Role: models.RoleAdmin, }, nil) session, err := uc.ReadSession(ctx, sid) require.NoError(t, err) require.Equal(t, session.Id, sid) require.Equal(t, session.UserId, int32(1)) require.Equal(t, session.Role, models.RoleAdmin) }) } func TestUseCase_UpdateSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() vkRepository := mock_users.NewMockValkeyRepository(ctrl) uc := NewUseCase( nil, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid session update", func(t *testing.T) { ctx := context.Background() sid := uuid.NewString() vkRepository.EXPECT().UpdateSession(ctx, sid).Return(nil) err := uc.UpdateSession(ctx, sid) require.NoError(t, err) }) } func TestUseCase_DeleteSession(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() vkRepository := mock_users.NewMockValkeyRepository(ctrl) uc := NewUseCase( nil, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid session delete", func(t *testing.T) { ctx := context.Background() sid := uuid.NewString() vkRepository.EXPECT().DeleteSession(ctx, sid).Return(nil) err := uc.DeleteSession(ctx, sid) require.NoError(t, err) }) } func TestUseCase_DeleteAllSessions(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() vkRepository := mock_users.NewMockValkeyRepository(ctrl) uc := NewUseCase( nil, vkRepository, config.Config{ JWTSecret: "abc", }, ) t.Run("valid session delete", func(t *testing.T) { ctx := context.Background() userId := int32(1) vkRepository.EXPECT().DeleteAllSessions(ctx, userId).Return(nil) err := uc.DeleteAllSessions(ctx, userId) require.NoError(t, err) }) } func TestUseCase_Verify(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) defer ctrl.Finish() vkRepository := mock_users.NewMockValkeyRepository(ctrl) cfg := config.Config{JWTSecret: "abc"} uc := NewUseCase( nil, vkRepository, cfg, ) t.Run("valid verification", func(t *testing.T) { ctx := context.Background() rsess := &models.Session{ Id: uuid.NewString(), UserId: 1, Role: models.RoleAdmin, } vkRepository.EXPECT().ReadSession(ctx, rsess.Id).Return(rsess, nil) session, err := uc.Verify(ctx, rsess.Id) require.NoError(t, err) token, err := jwt.ParseWithClaims(session, &Token{}, func(token *jwt.Token) (interface{}, error) { return []byte(cfg.JWTSecret), nil }) claims, ok := token.Claims.(*Token) require.True(t, ok) require.NoError(t, err) require.Equal(t, claims.SessionId, rsess.Id) require.Equal(t, claims.UserId, rsess.UserId) require.Equal(t, claims.Role, rsess.Role) }) } func StringP(s string) *string { return &s } func RoleP(r models.Role) *models.Role { return &r }