package unit import ( "errors" "testing" "time" "wish-list-api/api/presenter" "wish-list-api/pkg/auth" "wish-list-api/pkg/entities" "wish-list-api/pkg/user" "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/bson/primitive" ) var ErrUserNotFound = errors.New("user not found") type MockUserRepository struct { users map[string]*entities.User email map[string]*entities.User telegram map[int64]*entities.User } func NewMockUserRepository() user.Repository { return &MockUserRepository{ users: make(map[string]*entities.User), email: make(map[string]*entities.User), telegram: make(map[int64]*entities.User), } } func (r *MockUserRepository) CreateUser(user *entities.User) (*entities.User, error) { if user.ID.IsZero() { user.ID = primitive.NewObjectID() } user.CreatedAt = time.Now() user.UpdatedAt = time.Now() r.users[user.ID.Hex()] = user r.email[user.Email] = user if user.TelegramID != 0 { r.telegram[user.TelegramID] = user } return user, nil } func (r *MockUserRepository) ReadUser(id string) (*entities.User, error) { if user, ok := r.users[id]; ok { return user, nil } return nil, ErrUserNotFound } func (r *MockUserRepository) ReadUserByEmail(email string) (*entities.User, error) { if user, ok := r.email[email]; ok { return user, nil } return nil, ErrUserNotFound } func (r *MockUserRepository) ReadAllUsers() (*[]presenter.User, error) { var users []presenter.User for _, u := range r.users { users = append(users, presenter.User{ ID: u.ID, Email: u.Email, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, }) } return &users, nil } func (r *MockUserRepository) UpdateUser(user *entities.User) (*entities.User, error) { if _, ok := r.users[user.ID.Hex()]; !ok { return nil, ErrUserNotFound } user.UpdatedAt = time.Now() r.users[user.ID.Hex()] = user for email, u := range r.email { if u.ID == user.ID && email != user.Email { delete(r.email, email) break } } r.email[user.Email] = user if user.TelegramID != 0 { r.telegram[user.TelegramID] = user } return user, nil } func (r *MockUserRepository) DeleteUser(id string) error { user, ok := r.users[id] if !ok { return ErrUserNotFound } delete(r.users, id) delete(r.email, user.Email) if user.TelegramID != 0 { delete(r.telegram, user.TelegramID) } return nil } func (r *MockUserRepository) ReadUserByTelegramID(telegramID int64) (*entities.User, error) { if user, ok := r.telegram[telegramID]; ok { return user, nil } return nil, ErrUserNotFound } func (r *MockUserRepository) UpdateUserTelegramData(user *entities.User) (*entities.User, error) { if _, ok := r.users[user.ID.Hex()]; !ok { return nil, ErrUserNotFound } existingUser := r.users[user.ID.Hex()] existingUser.TelegramID = user.TelegramID existingUser.TelegramUsername = user.TelegramUsername existingUser.FirstName = user.FirstName existingUser.LastName = user.LastName existingUser.PhotoURL = user.PhotoURL existingUser.UpdatedAt = time.Now() r.users[user.ID.Hex()] = existingUser if user.TelegramID != 0 { for tgID, u := range r.telegram { if u.ID == user.ID && tgID != user.TelegramID { delete(r.telegram, tgID) break } } r.telegram[user.TelegramID] = existingUser } return existingUser, nil } func TestRegister(t *testing.T) { mockRepo := NewMockUserRepository() mockUserService := user.NewService(mockRepo) serviceConfig := auth.ServiceConfig{ UserService: mockUserService, } service := auth.NewService(serviceConfig) t.Run("Register_Success", func(t *testing.T) { userData := &entities.RegisterRequest{ Email: "test@example.com", Password: "password123", } user, err := service.Register(userData) assert.NoError(t, err) assert.NotNil(t, user) assert.Equal(t, userData.Email, user.Email) assert.NotEqual(t, userData.Password, user.Password) }) t.Run("Register_DuplicateEmail", func(t *testing.T) { userData := &entities.RegisterRequest{ Email: "test@example.com", Password: "different_password", } user, err := service.Register(userData) assert.Error(t, err) assert.Nil(t, user) }) } func TestLogin(t *testing.T) { mockRepo := NewMockUserRepository() mockUserService := user.NewService(mockRepo) serviceConfig := auth.ServiceConfig{ UserService: mockUserService, } service := auth.NewService(serviceConfig) registerData := &entities.RegisterRequest{ Email: "login_test@example.com", Password: "password123", } _, err := service.Register(registerData) assert.NoError(t, err) t.Run("Login_Success", func(t *testing.T) { loginData := &entities.LoginRequest{ Email: "login_test@example.com", Password: "password123", } tokens, err := service.Login(loginData) assert.NoError(t, err) assert.NotNil(t, tokens) assert.NotEmpty(t, tokens.AccessToken) assert.NotEmpty(t, tokens.RefreshToken) }) t.Run("Login_InvalidEmail", func(t *testing.T) { loginData := &entities.LoginRequest{ Email: "nonexistent@example.com", Password: "password123", } tokens, err := service.Login(loginData) assert.Error(t, err) assert.Nil(t, tokens) }) t.Run("Login_InvalidPassword", func(t *testing.T) { loginData := &entities.LoginRequest{ Email: "login_test@example.com", Password: "wrong_password", } tokens, err := service.Login(loginData) assert.Error(t, err) assert.Nil(t, tokens) }) } func TestRefreshToken(t *testing.T) { mockRepo := NewMockUserRepository() mockUserService := user.NewService(mockRepo) serviceConfig := auth.ServiceConfig{ UserService: mockUserService, } service := auth.NewService(serviceConfig) registerData := &entities.RegisterRequest{ Email: "refresh_test@example.com", Password: "password123", } _, err := service.Register(registerData) assert.NoError(t, err) loginData := &entities.LoginRequest{ Email: "refresh_test@example.com", Password: "password123", } tokens, err := service.Login(loginData) assert.NoError(t, err) t.Run("RefreshToken_Success", func(t *testing.T) { newTokens, err := service.RefreshToken(tokens.RefreshToken) assert.NoError(t, err) assert.NotNil(t, newTokens) assert.NotEmpty(t, newTokens.AccessToken) assert.NotEmpty(t, newTokens.RefreshToken) }) t.Run("RefreshToken_Invalid", func(t *testing.T) { newTokens, err := service.RefreshToken("invalid_refresh_token") assert.Error(t, err) assert.Nil(t, newTokens) }) } func TestValidateToken(t *testing.T) { mockRepo := NewMockUserRepository() mockUserService := user.NewService(mockRepo) serviceConfig := auth.ServiceConfig{ UserService: mockUserService, } service := auth.NewService(serviceConfig) registerData := &entities.RegisterRequest{ Email: "validate_test@example.com", Password: "password123", } _, err := service.Register(registerData) assert.NoError(t, err) loginData := &entities.LoginRequest{ Email: "validate_test@example.com", Password: "password123", } tokens, err := service.Login(loginData) assert.NoError(t, err) t.Run("ValidateToken_Success", func(t *testing.T) { token, err := service.ValidateToken(tokens.AccessToken) assert.NoError(t, err) assert.NotNil(t, token) }) t.Run("ValidateToken_Invalid", func(t *testing.T) { token, err := service.ValidateToken("invalid_token") assert.Error(t, err) assert.Nil(t, token) }) } func TestGetUserIDFromToken(t *testing.T) { mockRepo := NewMockUserRepository() mockUserService := user.NewService(mockRepo) serviceConfig := auth.ServiceConfig{ UserService: mockUserService, } service := auth.NewService(serviceConfig) registerData := &entities.RegisterRequest{ Email: "userid_test@example.com", Password: "password123", } user, err := service.Register(registerData) assert.NoError(t, err) loginData := &entities.LoginRequest{ Email: "userid_test@example.com", Password: "password123", } tokens, err := service.Login(loginData) assert.NoError(t, err) t.Run("GetUserIDFromToken_Success", func(t *testing.T) { userID, err := service.GetUserIDFromToken(tokens.AccessToken) assert.NoError(t, err) assert.Equal(t, user.ID.Hex(), userID) }) t.Run("GetUserIDFromToken_Invalid", func(t *testing.T) { userID, err := service.GetUserIDFromToken("invalid_token") assert.Error(t, err) assert.Empty(t, userID) }) }