add: mockSMTP Server for testing; getUser func
This commit is contained in:
2
go.mod
2
go.mod
@@ -13,6 +13,8 @@ require (
|
|||||||
|
|
||||||
require github.com/kelseyhightower/envconfig v1.4.0
|
require github.com/kelseyhightower/envconfig v1.4.0
|
||||||
|
|
||||||
|
require github.com/mocktools/go-smtp-mock/v2 v2.3.1 // indirect
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.11.9 // indirect
|
github.com/bytedance/sonic v1.11.9 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
|||||||
2
go.sum
2
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-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 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
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-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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
|||||||
@@ -34,20 +34,25 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
|
|||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode userdata"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode userdata"})
|
||||||
return
|
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)
|
selectedModel, err := uc.MembershipService.GetModelByName(®Data.User.Membership.SubscriptionModel.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error.Printf("No subscription model found: %#v", err)
|
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
|
return
|
||||||
}
|
}
|
||||||
regData.User.Membership.SubscriptionModel = *selectedModel
|
regData.User.Membership.SubscriptionModel = *selectedModel
|
||||||
logger.Info.Printf("REGISTERING user: %#v", regData.User)
|
// logger.Info.Printf("REGISTERING user: %#v", regData.User)
|
||||||
// Register User
|
// Register User
|
||||||
id, token, err := uc.Service.RegisterUser(®Data.User)
|
id, token, err := uc.Service.RegisterUser(®Data.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error.Printf("Couldn't register User: %v", err)
|
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
|
return
|
||||||
}
|
}
|
||||||
regData.User.ID = id
|
regData.User.ID = id
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"strconv"
|
||||||
|
|
||||||
|
// "io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@@ -12,37 +14,64 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"GoMembership/internal/config"
|
"GoMembership/internal/config"
|
||||||
"GoMembership/internal/database"
|
"GoMembership/internal/database"
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"GoMembership/internal/repositories"
|
"GoMembership/internal/repositories"
|
||||||
"GoMembership/internal/services"
|
"GoMembership/internal/services"
|
||||||
|
"GoMembership/internal/utils"
|
||||||
"GoMembership/pkg/logger"
|
"GoMembership/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type keyValuePair struct {
|
|
||||||
value interface{}
|
|
||||||
key string
|
|
||||||
}
|
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
name string
|
name string
|
||||||
wantResponse uint16
|
|
||||||
userEmail string
|
|
||||||
wantDBData []keyValuePair
|
|
||||||
assert bool
|
|
||||||
input string
|
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) {
|
func TestUserController(t *testing.T) {
|
||||||
err := database.InitDB("test.db")
|
_ = deleteTestDB("test.db")
|
||||||
if err != nil {
|
|
||||||
|
if err := database.InitDB("test.db"); err != nil {
|
||||||
t.Errorf("Failed to create DB: %#v", err)
|
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()
|
config.LoadConfig()
|
||||||
|
utils.SMTPStart(Host, Port)
|
||||||
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail)
|
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail)
|
||||||
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
|
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
|
||||||
consentService := &services.ConsentService{Repo: consentRepo}
|
consentService := &services.ConsentService{Repo: consentRepo}
|
||||||
@@ -57,44 +86,16 @@ func TestUserController(t *testing.T) {
|
|||||||
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
|
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
|
||||||
userService := &services.UserService{Repo: userRepo}
|
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 {
|
if err := initSubscriptionPlans(); err != nil {
|
||||||
t.Errorf("Failed to init Susbcription plans: %#v", err)
|
t.Errorf("Failed to init Susbcription plans: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := getTestUsers()
|
tests := getTestUsers()
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
c, _ := getMockedContext([]byte(tt.input))
|
|
||||||
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
uc.RegisterUser(c)
|
runSingleTest(t, &tt)
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err := deleteTestDB("test.db"); err != nil {
|
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 {
|
func initSubscriptionPlans() error {
|
||||||
subscription := models.SubscriptionModel{
|
subscription := models.SubscriptionModel{
|
||||||
Name: "basic",
|
Name: "Basic",
|
||||||
|
Details: "Test Plan",
|
||||||
|
MonthlyFee: 2,
|
||||||
|
HourlyRate: 3,
|
||||||
}
|
}
|
||||||
result := database.DB.Create(&subscription)
|
result := database.DB.Create(&subscription)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
@@ -117,39 +152,16 @@ func getMockedContext(jsonStr []byte) (*gin.Context, *httptest.ResponseRecorder)
|
|||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := gin.CreateTestContext(w)
|
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")
|
c.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
return c, w
|
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 {
|
func deleteTestDB(dbPath string) error {
|
||||||
err := os.Remove(dbPath)
|
err := os.Remove(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -159,13 +171,35 @@ func deleteTestDB(dbPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TEST DATA:
|
// TEST DATA:
|
||||||
func generateUserJSON(user models.User) string {
|
func getBaseUser() models.User {
|
||||||
data, err := json.Marshal(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 {
|
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 ""
|
||||||
}
|
}
|
||||||
return string(data)
|
return string(jsonBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTestUsers() []test {
|
func getTestUsers() []test {
|
||||||
@@ -173,105 +207,129 @@ func getTestUsers() []test {
|
|||||||
{
|
{
|
||||||
name: "birthday < 18 should fail",
|
name: "birthday < 18 should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
wantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: []keyValuePair{
|
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
{
|
|
||||||
key: "Email",
|
|
||||||
value: "john.doe@example.com"},
|
|
||||||
},
|
|
||||||
assert: false,
|
assert: false,
|
||||||
input: generateUserJSON(
|
input: generateInputJSON(func(user models.User) models.User {
|
||||||
models.User{
|
user.DateOfBirth = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
DateOfBirth: time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC),
|
return user
|
||||||
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,
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "FirstName empty, should fail",
|
name: "FirstName empty, should fail",
|
||||||
wantResponse: http.StatusNotAcceptable,
|
wantResponse: http.StatusNotAcceptable,
|
||||||
wantDBData: []keyValuePair{
|
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
{
|
|
||||||
key: "Email",
|
|
||||||
value: "john.doe@example.com"},
|
|
||||||
},
|
|
||||||
assert: false,
|
assert: false,
|
||||||
input: generateUserJSON(
|
input: generateInputJSON(func(user models.User) models.User {
|
||||||
models.User{
|
user.FirstName = ""
|
||||||
DateOfBirth: time.Date(1990, time.January, 1, 0, 0, 0, 0, time.UTC),
|
return user
|
||||||
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 {
|
name: "LastName Empty should fail",
|
||||||
DateOfBirth: time.Date(1985, time.March, 15, 0, 0, 0, 0, time.UTC),
|
wantResponse: http.StatusNotAcceptable,
|
||||||
FirstName: "Jane",
|
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
LastName: "Smith",
|
assert: false,
|
||||||
Email: "jane.smith@example.com",
|
input: generateInputJSON(func(user models.User) models.User {
|
||||||
Address: "456 Oak St",
|
user.LastName = ""
|
||||||
ZipCode: "67890",
|
return user
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
DateOfBirth: time.Date(2000, time.July, 30, 0, 0, 0, 0, time.UTC),
|
name: "EMail wrong format should fail",
|
||||||
FirstName: "Alice",
|
wantResponse: http.StatusNotAcceptable,
|
||||||
LastName: "Brown",
|
wantDBData: map[string]interface{}{"email": "johnexample.com"},
|
||||||
Email: "alice.brown@example.com",
|
assert: false,
|
||||||
Address: "789 Pine St",
|
input: generateInputJSON(func(user models.User) models.User {
|
||||||
ZipCode: "11223",
|
user.Email = "johnexample.com"
|
||||||
City: "Villageville",
|
return user
|
||||||
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: "",
|
name: "Missing Zip Code should fail",
|
||||||
Company: stringPtr("AnotherCorp"),
|
wantResponse: http.StatusNotAcceptable,
|
||||||
Notes: nil,
|
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
|
||||||
Password: "mypassword789",
|
assert: false,
|
||||||
ID: 3,
|
input: generateInputJSON(func(user models.User) models.User {
|
||||||
PaymentStatus: 1,
|
user.ZipCode = ""
|
||||||
Status: 1,
|
return user
|
||||||
RoleID: 3,
|
}),
|
||||||
}, */
|
},
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ type User struct {
|
|||||||
DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"`
|
DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
Salt *string `json:"-"`
|
Salt *string `json:"-"`
|
||||||
Company *string `json:"company" validate:"omitempty,omitnil"`
|
Company string `json:"company" validate:"omitempty,omitnil"`
|
||||||
Phone string `json:"phone" validate:"omitempty,omitnil"`
|
Phone string `json:"phone" validate:"omitempty,omitnil"`
|
||||||
Notes *string `json:"notes"`
|
Notes *string `json:"notes"`
|
||||||
FirstName string `gorm:"not null" json:"first_name" validate:"required"`
|
FirstName string `gorm:"not null" json:"first_name" validate:"required"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Email string `gorm:"unique;not null" json:"email" validate:"required,email"`
|
Email string `gorm:"unique;not null" json:"email" validate:"required,email"`
|
||||||
LastName string `gorm:"not null" json:"last_name" validate:"required"`
|
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"`
|
Address string `gorm:"not null" json:"address" validate:"required"`
|
||||||
ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum"`
|
ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum"`
|
||||||
City string `form:"not null" json:"city" validate:"required,alphaunicode"`
|
City string `form:"not null" json:"city" validate:"required,alphaunicode"`
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
type UserRepositoryInterface interface {
|
type UserRepositoryInterface interface {
|
||||||
CreateUser(user *models.User) (int64, error)
|
CreateUser(user *models.User) (int64, error)
|
||||||
UpdateUser(userID int64, user *models.User) error
|
UpdateUser(userID int64, user *models.User) error
|
||||||
|
GetUsers(where map[string]interface{}) (*[]models.User, error)
|
||||||
FindUserByID(id int64) (*models.User, error)
|
FindUserByID(id int64) (*models.User, error)
|
||||||
FindUserByEmail(email string) (*models.User, error)
|
FindUserByEmail(email string) (*models.User, error)
|
||||||
SetVerificationToken(user *models.User, token *string) (int64, 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
|
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) {
|
func (ur *UserRepository) FindUserByID(id int64) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
result := database.DB.
|
result := database.DB.
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
|
|||||||
MembershipFee float32
|
MembershipFee float32
|
||||||
RentalFee float32
|
RentalFee float32
|
||||||
}{
|
}{
|
||||||
Company: *user.Company,
|
Company: user.Company,
|
||||||
FirstName: user.FirstName,
|
FirstName: user.FirstName,
|
||||||
MembershipModel: user.Membership.SubscriptionModel.Name,
|
MembershipModel: user.Membership.SubscriptionModel.Name,
|
||||||
MembershipID: user.Membership.ID,
|
MembershipID: user.Membership.ID,
|
||||||
@@ -121,7 +121,7 @@ func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error {
|
|||||||
RentalFee float32
|
RentalFee float32
|
||||||
MembershipFee float32
|
MembershipFee float32
|
||||||
}{
|
}{
|
||||||
Company: *user.Company,
|
Company: user.Company,
|
||||||
FirstName: user.FirstName,
|
FirstName: user.FirstName,
|
||||||
LastName: user.LastName,
|
LastName: user.LastName,
|
||||||
MembershipModel: user.Membership.SubscriptionModel.Name,
|
MembershipModel: user.Membership.SubscriptionModel.Name,
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
|
||||||
"GoMembership/internal/constants"
|
"GoMembership/internal/constants"
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"GoMembership/internal/repositories"
|
"GoMembership/internal/repositories"
|
||||||
"GoMembership/internal/utils"
|
"GoMembership/internal/utils"
|
||||||
"GoMembership/pkg/logger"
|
"GoMembership/pkg/logger"
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
// "crypto/rand"
|
|
||||||
// "encoding/base64"
|
|
||||||
// "golang.org/x/crypto/bcrypt"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserServiceInterface interface {
|
type UserServiceInterface interface {
|
||||||
RegisterUser(user *models.User) (int64, string, error)
|
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
|
// AuthenticateUser(email, password string) (*models.User, error)A
|
||||||
VerifyUser(token *string) (*models.User, error)
|
VerifyUser(token *string) (*models.User, error)
|
||||||
}
|
}
|
||||||
@@ -32,7 +36,7 @@ func (service *UserService) RegisterUser(user *models.User) (int64, string, erro
|
|||||||
*/
|
*/
|
||||||
err := validateRegistrationData(user)
|
err := validateRegistrationData(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, "", err
|
return http.StatusNotAcceptable, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Status = constants.UnverifiedStatus
|
user.Status = constants.UnverifiedStatus
|
||||||
@@ -40,27 +44,38 @@ func (service *UserService) RegisterUser(user *models.User) (int64, string, erro
|
|||||||
user.UpdatedAt = time.Now()
|
user.UpdatedAt = time.Now()
|
||||||
|
|
||||||
id, err := service.Repo.CreateUser(user)
|
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
|
user.ID = id
|
||||||
|
|
||||||
token, err := utils.GenerateVerificationToken()
|
token, err := utils.GenerateVerificationToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, "", err
|
return http.StatusInternalServerError, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info.Printf("TOKEN: %v", token)
|
logger.Info.Printf("TOKEN: %v", token)
|
||||||
|
|
||||||
_, err = service.Repo.SetVerificationToken(user, &token)
|
_, err = service.Repo.SetVerificationToken(user, &token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, "", err
|
return http.StatusInternalServerError, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return id, token, nil
|
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) {
|
func (service *UserService) VerifyUser(token *string) (*models.User, error) {
|
||||||
user, err := service.Repo.VerifyUserOfToken(token)
|
user, err := service.Repo.VerifyUserOfToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
33
internal/utils/mock_smtp.go
Normal file
33
internal/utils/mock_smtp.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user