From c02b96e538a208783c3878004df3b2ddebacec36 Mon Sep 17 00:00:00 2001 From: "$(pass /github/name)" <$(pass /github/email)> Date: Thu, 8 Aug 2024 09:29:49 +0200 Subject: [PATCH] add: mockSMTP Server for testing; getUser func --- go.mod | 2 + go.sum | 2 + internal/controllers/user_controller.go | 11 +- internal/controllers/user_controller_test.go | 400 +++++++++++-------- internal/models/user.go | 4 +- internal/repositories/user_repository.go | 19 + internal/services/email_service.go | 4 +- internal/services/user_service.go | 33 +- internal/utils/mock_smtp.go | 33 ++ 9 files changed, 321 insertions(+), 187 deletions(-) create mode 100644 internal/utils/mock_smtp.go diff --git a/go.mod b/go.mod index de23eaa..6adb5e4 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,8 @@ require ( require github.com/kelseyhightower/envconfig v1.4.0 +require github.com/mocktools/go-smtp-mock/v2 v2.3.1 // indirect + require ( github.com/bytedance/sonic v1.11.9 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect diff --git a/go.sum b/go.sum index 987de58..93b96ac 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mocktools/go-smtp-mock/v2 v2.3.1 h1:wq75NDSsOy5oHo/gEQQT0fRRaYKRqr1IdkjhIPXxagM= +github.com/mocktools/go-smtp-mock/v2 v2.3.1/go.mod h1:h9AOf/IXLSU2m/1u4zsjtOM/WddPwdOUBz56dV9f81M= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/internal/controllers/user_controller.go b/internal/controllers/user_controller.go index d34669c..354637e 100644 --- a/internal/controllers/user_controller.go +++ b/internal/controllers/user_controller.go @@ -34,20 +34,25 @@ func (uc *UserController) RegisterUser(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode userdata"}) return } + if regData.User.Membership.SubscriptionModel.Name == "" { + logger.Error.Printf("No subscription model provided") + c.JSON(http.StatusNotAcceptable, gin.H{"error": "No subscription model provided"}) + return + } selectedModel, err := uc.MembershipService.GetModelByName(®Data.User.Membership.SubscriptionModel.Name) if err != nil { logger.Error.Printf("No subscription model found: %#v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Not a valid subscription model"}) + c.JSON(http.StatusNotFound, gin.H{"error": "Not a valid subscription model"}) return } regData.User.Membership.SubscriptionModel = *selectedModel - logger.Info.Printf("REGISTERING user: %#v", regData.User) + // logger.Info.Printf("REGISTERING user: %#v", regData.User) // Register User id, token, err := uc.Service.RegisterUser(®Data.User) if err != nil { logger.Error.Printf("Couldn't register User: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Couldn't register User: %v", err)}) + c.JSON(int(id), gin.H{"error": fmt.Sprintf("Couldn't register User: %v", err)}) return } regData.User.ID = id diff --git a/internal/controllers/user_controller_test.go b/internal/controllers/user_controller_test.go index 5678e1f..74f0eb5 100644 --- a/internal/controllers/user_controller_test.go +++ b/internal/controllers/user_controller_test.go @@ -3,7 +3,9 @@ package controllers import ( "bytes" "encoding/json" - "fmt" + "strconv" + + // "io/ioutil" "net/http" "net/http/httptest" "os" @@ -12,37 +14,64 @@ import ( "github.com/gin-gonic/gin" "gorm.io/gorm" - "reflect" "GoMembership/internal/config" "GoMembership/internal/database" "GoMembership/internal/models" "GoMembership/internal/repositories" "GoMembership/internal/services" + "GoMembership/internal/utils" "GoMembership/pkg/logger" ) -type keyValuePair struct { - value interface{} - key string -} - type test struct { name string - wantResponse uint16 - userEmail string - wantDBData []keyValuePair - assert bool input string + wantDBData map[string]interface{} + wantResponse uint16 + assert bool } +// type RegistrationData struct { +// User models.User `json:"user"` +// } + +const ( + Host = "127.0.0.1" + Port int = 2525 + User = "alex@mail.de" + Pass = "secret" + AdminMail = "admin@mail.de" +) + +var ( + uc UserController +) + func TestUserController(t *testing.T) { - err := database.InitDB("test.db") - if err != nil { + _ = deleteTestDB("test.db") + + if err := database.InitDB("test.db"); err != nil { t.Errorf("Failed to create DB: %#v", err) } + if err := os.Setenv("SMTP_HOST", Host); err != nil { + t.Errorf("Error setting environment variable: %v", err) + } + if err := os.Setenv("SMTP_PORT", strconv.Itoa(Port)); err != nil { + t.Errorf("Error setting environment variable: %v", err) + } + if err := os.Setenv("ADMIN_MAIL", AdminMail); err != nil { + t.Errorf("Error setting environment variable: %v", err) + } + if err := os.Setenv("SMTP_USER", User); err != nil { + t.Errorf("Error setting environment variable: %v", err) + } + if err := os.Setenv("SMTP_PASS", Pass); err != nil { + t.Errorf("Error setting environment variable: %v", err) + } config.LoadConfig() + utils.SMTPStart(Host, Port) emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail) var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{} consentService := &services.ConsentService{Repo: consentRepo} @@ -57,44 +86,16 @@ func TestUserController(t *testing.T) { var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{} userService := &services.UserService{Repo: userRepo} - uc := UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService} + uc = UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService} if err := initSubscriptionPlans(); err != nil { t.Errorf("Failed to init Susbcription plans: %#v", err) } tests := getTestUsers() - for _, tt := range tests { - c, _ := getMockedContext([]byte(tt.input)) - t.Run(tt.name, func(t *testing.T) { - uc.RegisterUser(c) - user, err := userRepo.FindUserByEmail(tt.userEmail) - if err == gorm.ErrRecordNotFound && !tt.assert { - //pass - } else if err == nil && tt.assert { - // check value: - if tt.wantDBData != nil { - for _, kvp := range tt.wantDBData { - /* dbValue, err := getFieldValue(*user, kvp.key) - if err != nil { - t.Errorf("getFieldValue failed: %#v", err) - } */ - dbValue, err := getAttr(&user, kvp.key) - if err != nil { - t.Errorf("Couldn't get Attribute: %#v", err) - } - if kvp.value != dbValue { - // fail - t.Errorf("Value is not expected: %v != %v", kvp.value, dbValue) - } - } - } - } else { - // fail - t.Errorf("FindUserByEmail failed: %#v", err) - } + runSingleTest(t, &tt) }) } if err := deleteTestDB("test.db"); err != nil { @@ -102,9 +103,43 @@ func TestUserController(t *testing.T) { } } +func runSingleTest(t *testing.T, tt *test) { + + c, w := getMockedContext([]byte(tt.input)) + uc.RegisterUser(c) + + if w.Code != int(tt.wantResponse) { + t.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, tt.wantResponse) + } + validateUser(t, tt.assert, tt.wantDBData) +} + +func handleError(t *testing.T, err error, assert bool) { + if err == gorm.ErrRecordNotFound && !assert { + return // Expected case: user not found and assertion is false + } + + if err != nil { + t.Errorf("Error during testing: %#v", err) + } +} + +func validateUser(t *testing.T, assert bool, wantDBData map[string]interface{}) { + users, err := uc.Service.GetUsers(wantDBData) + if err != nil { + t.Errorf("Error in database ops: %#v", err) + } + if assert != (len(*users) != 0) { + t.Errorf("User entry query didn't met expectation: %v != %#v", assert, *users) + } +} + func initSubscriptionPlans() error { subscription := models.SubscriptionModel{ - Name: "basic", + Name: "Basic", + Details: "Test Plan", + MonthlyFee: 2, + HourlyRate: 3, } result := database.DB.Create(&subscription) if result.Error != nil { @@ -117,39 +152,16 @@ func getMockedContext(jsonStr []byte) (*gin.Context, *httptest.ResponseRecorder) gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - c.Request, _ = http.NewRequest("POST", "/register", bytes.NewBuffer(jsonStr)) + var err error + c.Request, err = http.NewRequest("POST", "/register", bytes.NewBuffer(jsonStr)) + if err != nil { + logger.Error.Fatalf("Failed to create new Request: %#v", err) + } c.Request.Header.Set("Content-Type", "application/json") + return c, w } -func getAttr(obj interface{}, fieldName string) (interface{}, error) { - pointToStruct := reflect.ValueOf(obj) // addressable - curStruct := pointToStruct.Elem() - if curStruct.Kind() != reflect.Struct { - return nil, fmt.Errorf("expected a struct, but got %v", curStruct.Kind()) - } - curField := curStruct.FieldByName(fieldName) // type: reflect.Value - if !curField.IsValid() { - return nil, fmt.Errorf("no such field: %s in user", fieldName) - } - return curField.Interface(), nil -} - -/* func getFieldValue(user models.User, fieldName string) (interface{}, error) { - v := reflect.ValueOf(user) - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - if v.Kind() != reflect.Struct { - return nil, fmt.Errorf("expected a struct, but got %v", v.Kind()) - } - fieldVal := v.FieldByName(fieldName) - if !fieldVal.IsValid() { - return nil, fmt.Errorf("no such field: %s in user", fieldName) - } - return fieldVal.Interface(), nil -} */ - func deleteTestDB(dbPath string) error { err := os.Remove(dbPath) if err != nil { @@ -159,13 +171,35 @@ func deleteTestDB(dbPath string) error { } // TEST DATA: -func generateUserJSON(user models.User) string { - data, err := json.Marshal(user) +func getBaseUser() models.User { + return models.User{ + DateOfBirth: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC), + FirstName: "John", + LastName: "Doe", + Email: "john.doe@example.com", + Address: "Pablo Escobar Str. 4", + ZipCode: "25474", + City: "Hasloh", + Phone: "01738484993", + BankAccount: models.BankAccount{IBAN: "DE89370400440532013000"}, + Membership: models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}}, + ProfilePicture: "", + Password: "password123", + Company: "", + } +} + +func generateInputJSON(customize func(models.User) models.User) string { + user := getBaseUser() + user = customize(user) // Apply the customization + regData := RegistrationData{User: user} + + jsonBytes, err := json.Marshal(regData) if err != nil { - logger.Error.Printf("couldn't generate Json from Uer: %#v\nERROR: %#v", user, err) + logger.Error.Printf("couldn't generate Json from User: %#v\nERROR: %#v", regData, err) return "" } - return string(data) + return string(jsonBytes) } func getTestUsers() []test { @@ -173,105 +207,129 @@ func getTestUsers() []test { { name: "birthday < 18 should fail", wantResponse: http.StatusNotAcceptable, - wantDBData: []keyValuePair{ - { - key: "Email", - value: "john.doe@example.com"}, - }, - assert: false, - input: generateUserJSON( - models.User{ - DateOfBirth: time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC), - FirstName: "John", - LastName: "Doe", - Email: "john.doe@example.com", - Address: "123 Main St", - ZipCode: "12345", - City: "Cityville", - Phone: "123-456-7890", - BankAccount: models.BankAccount{IBAN: "DE89370400440532013000"}, - Membership: models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}}, - ProfilePicture: "http://example.com/profile.jpg", - Company: nil, - Notes: nil, - Password: "password123", - ID: 1, - PaymentStatus: 1, - Status: 1, - RoleID: 1, - }), + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.DateOfBirth = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC) + return user + }), }, { name: "FirstName empty, should fail", wantResponse: http.StatusNotAcceptable, - wantDBData: []keyValuePair{ - { - key: "Email", - value: "john.doe@example.com"}, - }, - assert: false, - input: generateUserJSON( - models.User{ - DateOfBirth: time.Date(1990, time.January, 1, 0, 0, 0, 0, time.UTC), - FirstName: "", - LastName: "Doe", - Email: "john.doe@example.com", - Address: "123 Main St", - ZipCode: "12345", - City: "Cityville", - Phone: "123-456-7890", - BankAccount: models.BankAccount{IBAN: "DE89370400440532013000"}, - Membership: models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}}, - ProfilePicture: "http://example.com/profile.jpg", - Company: nil, - Notes: nil, - Password: "password123", - ID: 1, - PaymentStatus: 1, - Status: 1, - RoleID: 1, - }), - }, - /* Vjjjjjjjj - models.User { - DateOfBirth: time.Date(1985, time.March, 15, 0, 0, 0, 0, time.UTC), - FirstName: "Jane", - LastName: "Smith", - Email: "jane.smith@example.com", - Address: "456 Oak St", - ZipCode: "67890", - City: "Townsville", - Phone: "098-765-4321", - BankAccount: models.BankAccount{IBAN: "FR7630006000011234567890189"}, - Membership: models.Membership{SubscriptionModel: SubscriptionModel{Name: "Premium", Details: "Premium Subscription", Conditions: "None", MonthlyFee: 19.99, HourlyRate: 25.00}}, - ProfilePicture: "http://example.com/profile2.jpg", - Company: stringPtr("ExampleCorp"), - Notes: stringPtr("Important client"), - Password: "securepassword456", - ID: 2, - PaymentStatus: 0, - Status: 2, - RoleID: 2, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.FirstName = "" + return user + }), }, { - DateOfBirth: time.Date(2000, time.July, 30, 0, 0, 0, 0, time.UTC), - FirstName: "Alice", - LastName: "Brown", - Email: "alice.brown@example.com", - Address: "789 Pine St", - ZipCode: "11223", - City: "Villageville", - Phone: "555-555-5555", - BankAccount: models.BankAccount{IBAN: "GB29NWBK60161331926819"}, - Membership: models.Membership{SubscriptionModel: SubscriptionModel{Name: "Standard", Details: "Standard Subscription", Conditions: "None", MonthlyFee: 14.99, HourlyRate: 20.00}}, - ProfilePicture: "", - Company: stringPtr("AnotherCorp"), - Notes: nil, - Password: "mypassword789", - ID: 3, - PaymentStatus: 1, - Status: 1, - RoleID: 3, - }, */ + name: "LastName Empty should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.LastName = "" + return user + }), + }, + { + name: "EMail wrong format should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "johnexample.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.Email = "johnexample.com" + return user + }), + }, + { + name: "Missing Zip Code should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.ZipCode = "" + return user + }), + }, + { + name: "Missing Address should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.Address = "" + return user + }), + }, + { + name: "Missing City should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.City = "" + return user + }), + }, + { + name: "Missing IBAN should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.BankAccount.IBAN = "" + return user + }), + }, + { + name: "Invalid IBAN should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.BankAccount.IBAN = "DE1234234123134" + return user + }), + }, + { + name: "Missing subscription plan should fail", + wantResponse: http.StatusNotAcceptable, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.Membership.SubscriptionModel.Name = "" + return user + }), + }, + { + name: "Invalid subscription plan should fail", + wantResponse: http.StatusNotFound, + wantDBData: map[string]interface{}{"email": "john.doe@example.com"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.Membership.SubscriptionModel.Name = "NOTEXISTENTPLAN" + return user + }), + }, + { + name: "Correct Entry should pass", + wantResponse: http.StatusCreated, + wantDBData: map[string]interface{}{"Email": "john.doe@example.com"}, + assert: true, + input: generateInputJSON(func(user models.User) models.User { return user }), + }, + { + name: "Email duplicate should fail", + wantResponse: http.StatusConflict, + wantDBData: map[string]interface{}{"first_name": "Jane"}, + assert: false, + input: generateInputJSON(func(user models.User) models.User { + user.FirstName = "Jane" + return user + }), + }, } } diff --git a/internal/models/user.go b/internal/models/user.go index 210e36a..ae85b34 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -11,14 +11,14 @@ type User struct { DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"` CreatedAt time.Time Salt *string `json:"-"` - Company *string `json:"company" validate:"omitempty,omitnil"` + Company string `json:"company" validate:"omitempty,omitnil"` Phone string `json:"phone" validate:"omitempty,omitnil"` Notes *string `json:"notes"` FirstName string `gorm:"not null" json:"first_name" validate:"required"` Password string `json:"password"` Email string `gorm:"unique;not null" json:"email" validate:"required,email"` LastName string `gorm:"not null" json:"last_name" validate:"required"` - ProfilePicture string `json:"profile_picture" validate:"omitempty,image"` + ProfilePicture string `json:"profile_picture" validate:"omitempty,omitnil,image"` Address string `gorm:"not null" json:"address" validate:"required"` ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum"` City string `form:"not null" json:"city" validate:"required,alphaunicode"` diff --git a/internal/repositories/user_repository.go b/internal/repositories/user_repository.go index f91df3a..e523934 100644 --- a/internal/repositories/user_repository.go +++ b/internal/repositories/user_repository.go @@ -17,6 +17,7 @@ import ( type UserRepositoryInterface interface { CreateUser(user *models.User) (int64, error) UpdateUser(userID int64, user *models.User) error + GetUsers(where map[string]interface{}) (*[]models.User, error) FindUserByID(id int64) (*models.User, error) FindUserByEmail(email string) (*models.User, error) SetVerificationToken(user *models.User, token *string) (int64, error) @@ -51,6 +52,24 @@ func (ur *UserRepository) UpdateUser(userID int64, user *models.User) error { return nil } +func (ur *UserRepository) GetUsers(where map[string]interface{}) (*[]models.User, error) { + var users []models.User + result := database.DB. + Preload("Consents"). + Preload("BankAccount"). + Preload("Verification"). + Preload("Membership", func(db *gorm.DB) *gorm.DB { + return db.Preload("SubscriptionModel") + }).Where(where).Find(&users) + if result.Error != nil { + if result.Error == gorm.ErrRecordNotFound { + return nil, gorm.ErrRecordNotFound + } + return nil, result.Error + } + return &users, nil +} + func (ur *UserRepository) FindUserByID(id int64) (*models.User, error) { var user models.User result := database.DB. diff --git a/internal/services/email_service.go b/internal/services/email_service.go index 94c096a..b032550 100644 --- a/internal/services/email_service.go +++ b/internal/services/email_service.go @@ -86,7 +86,7 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error { MembershipFee float32 RentalFee float32 }{ - Company: *user.Company, + Company: user.Company, FirstName: user.FirstName, MembershipModel: user.Membership.SubscriptionModel.Name, MembershipID: user.Membership.ID, @@ -121,7 +121,7 @@ func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error { RentalFee float32 MembershipFee float32 }{ - Company: *user.Company, + Company: user.Company, FirstName: user.FirstName, LastName: user.LastName, MembershipModel: user.Membership.SubscriptionModel.Name, diff --git a/internal/services/user_service.go b/internal/services/user_service.go index 9a6ebdb..f70878a 100644 --- a/internal/services/user_service.go +++ b/internal/services/user_service.go @@ -1,20 +1,24 @@ package services import ( + "net/http" + "strings" + + "github.com/go-playground/validator/v10" + "GoMembership/internal/constants" "GoMembership/internal/models" "GoMembership/internal/repositories" "GoMembership/internal/utils" "GoMembership/pkg/logger" - "github.com/go-playground/validator/v10" - // "crypto/rand" - // "encoding/base64" - // "golang.org/x/crypto/bcrypt" + "time" ) type UserServiceInterface interface { RegisterUser(user *models.User) (int64, string, error) + FindUserByEmail(email string) (*models.User, error) + GetUsers(where map[string]interface{}) (*[]models.User, error) // AuthenticateUser(email, password string) (*models.User, error)A VerifyUser(token *string) (*models.User, error) } @@ -32,7 +36,7 @@ func (service *UserService) RegisterUser(user *models.User) (int64, string, erro */ err := validateRegistrationData(user) if err != nil { - return -1, "", err + return http.StatusNotAcceptable, "", err } user.Status = constants.UnverifiedStatus @@ -40,27 +44,38 @@ func (service *UserService) RegisterUser(user *models.User) (int64, string, erro user.UpdatedAt = time.Now() id, err := service.Repo.CreateUser(user) - if err != nil { - return -1, "", err + + if err != nil && strings.Contains(err.Error(), "UNIQUE constraint failed") { + return http.StatusConflict, "", err + } else if err != nil { + return http.StatusInternalServerError, "", err } user.ID = id token, err := utils.GenerateVerificationToken() if err != nil { - return -1, "", err + return http.StatusInternalServerError, "", err } logger.Info.Printf("TOKEN: %v", token) _, err = service.Repo.SetVerificationToken(user, &token) if err != nil { - return -1, "", err + return http.StatusInternalServerError, "", err } return id, token, nil } +func (service *UserService) FindUserByEmail(email string) (*models.User, error) { + return service.Repo.FindUserByEmail(email) +} + +func (service *UserService) GetUsers(where map[string]interface{}) (*[]models.User, error) { + return service.Repo.GetUsers(where) +} + func (service *UserService) VerifyUser(token *string) (*models.User, error) { user, err := service.Repo.VerifyUserOfToken(token) if err != nil { diff --git a/internal/utils/mock_smtp.go b/internal/utils/mock_smtp.go new file mode 100644 index 0000000..d6bcd36 --- /dev/null +++ b/internal/utils/mock_smtp.go @@ -0,0 +1,33 @@ +package utils + +import ( + smtpmock "github.com/mocktools/go-smtp-mock/v2" +) + +var Server smtpmock.Server + +// StartMockSMTPServer starts a mock SMTP server for testing +func SMTPStart(host string, port int) error { + Server = *smtpmock.New(smtpmock.ConfigurationAttr{ + HostAddress: host, + PortNumber: port, + LogToStdout: false, + LogServerActivity: false, + }) + if err := Server.Start(); err != nil { + return err + } + return nil +} + +func SMTPGetMessages() []smtpmock.Message { + return Server.MessagesAndPurge() +} + +func SMTPStop() error { + + if err := Server.Stop(); err != nil { + return err + } + return nil +}