chg: backend: error struct

This commit is contained in:
Alex
2024-10-09 18:08:33 +02:00
parent 3b3dc9d251
commit 02d75f0ab1
7 changed files with 275 additions and 74 deletions

View File

@@ -26,6 +26,6 @@ func testXSSAttempt(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotAcceptable, w.Code) assert.Equal(t, http.StatusBadRequest, w.Code)
assert.NotContains(t, w.Body.String(), xssPayload) assert.NotContains(t, w.Body.String(), xssPayload)
} }

View File

@@ -22,6 +22,7 @@ import (
"GoMembership/internal/repositories" "GoMembership/internal/repositories"
"GoMembership/internal/services" "GoMembership/internal/services"
"GoMembership/internal/utils" "GoMembership/internal/utils"
"GoMembership/internal/validation"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
) )
@@ -102,7 +103,9 @@ func TestSuite(t *testing.T) {
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{} var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
userService := &services.UserService{Repo: userRepo, Licences: licenceRepo} userService := &services.UserService{Repo: userRepo, Licences: licenceRepo}
Uc = &UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService} driversLicenceService := &services.DriversLicenceService{Repo: licenceRepo}
Uc = &UserController{Service: userService, DriversLicenceService: driversLicenceService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
Mc = &MembershipController{Service: *membershipService} Mc = &MembershipController{Service: *membershipService}
Cc = &ContactController{EmailService: emailService} Cc = &ContactController{EmailService: emailService}
@@ -110,6 +113,10 @@ func TestSuite(t *testing.T) {
log.Fatalf("Failed to init Subscription plans: %#v", err) log.Fatalf("Failed to init Subscription plans: %#v", err)
} }
if err := initLicenceCategories(); err != nil {
log.Fatalf("Failed to init Categories: %v", err)
}
validation.SetupValidators()
t.Run("userController", func(t *testing.T) { t.Run("userController", func(t *testing.T) {
testUserController(t) testUserController(t)
}) })
@@ -139,6 +146,35 @@ func TestSuite(t *testing.T) {
// } // }
} }
func initLicenceCategories() error {
categories := []models.LicenceCategory{
{Category: "AM"},
{Category: "A1"},
{Category: "A2"},
{Category: "A"},
{Category: "B"},
{Category: "C1"},
{Category: "C"},
{Category: "D1"},
{Category: "D"},
{Category: "BE"},
{Category: "C1E"},
{Category: "CE"},
{Category: "D1E"},
{Category: "DE"},
{Category: "T"},
{Category: "L"},
}
for _, category := range categories {
result := database.DB.Create(&category)
if result.Error != nil {
return result.Error
}
}
return nil
}
func initSubscriptionPlans() error { func initSubscriptionPlans() error {
subscriptions := []models.SubscriptionModel{ subscriptions := []models.SubscriptionModel{
{ {

View File

@@ -26,6 +26,7 @@ func (mc *MembershipController) RegisterSubscription(c *gin.Context) {
if err := c.ShouldBindJSON(&regData); err != nil { if err := c.ShouldBindJSON(&regData); err != nil {
logger.Error.Printf("Couln't decode subscription data: %v", err) logger.Error.Printf("Couln't decode subscription data: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode subscription data"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode subscription data"})
return
} }
// Register Subscription // Register Subscription

View File

@@ -86,7 +86,7 @@ func getSubscriptionData() []RegisterSubscriptionTest {
return []RegisterSubscriptionTest{ return []RegisterSubscriptionTest{
{ {
Name: "Missing details should fail", Name: "Missing details should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"name": "Just a Subscription"}, WantDBData: map[string]interface{}{"name": "Just a Subscription"},
Assert: false, Assert: false,
Input: GenerateInputJSON( Input: GenerateInputJSON(
@@ -97,7 +97,7 @@ func getSubscriptionData() []RegisterSubscriptionTest {
}, },
{ {
Name: "Missing model name should fail", Name: "Missing model name should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"name": ""}, WantDBData: map[string]interface{}{"name": ""},
Assert: false, Assert: false,
Input: GenerateInputJSON( Input: GenerateInputJSON(
@@ -108,7 +108,7 @@ func getSubscriptionData() []RegisterSubscriptionTest {
}, },
{ {
Name: "Negative monthly fee should fail", Name: "Negative monthly fee should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"name": "Premium"}, WantDBData: map[string]interface{}{"name": "Premium"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData { Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData {
@@ -118,7 +118,7 @@ func getSubscriptionData() []RegisterSubscriptionTest {
}, },
{ {
Name: "Negative hourly rate should fail", Name: "Negative hourly rate should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"name": "Premium"}, WantDBData: map[string]interface{}{"name": "Premium"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData { Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData {

View File

@@ -7,10 +7,12 @@ import (
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/internal/services" "GoMembership/internal/services"
"GoMembership/internal/utils" "GoMembership/internal/utils"
"strings"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"GoMembership/pkg/errors" "GoMembership/pkg/errors"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
@@ -33,13 +35,34 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
var user models.User var user models.User
if err := c.ShouldBindJSON(&user); err != nil { if err := c.ShouldBindJSON(&user); err != nil {
logger.Error.Printf("Couldn't decode input: %v", err) logger.Error.Printf("Couldn't decode input: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode request data"}) var validationErrors []gin.H
if ve, ok := err.(validator.ValidationErrors); ok {
for _, e := range ve {
validationErrors = append(validationErrors, gin.H{
"field": e.Field(),
"key": "server.validation." + e.Tag(),
})
}
} else {
validationErrors = append(validationErrors, gin.H{
"field": "general",
"key": "server.error.invalid_json",
})
}
logger.Error.Printf("ValidationErrors: %#v", validationErrors)
c.JSON(http.StatusBadRequest, gin.H{"errors": validationErrors})
c.Abort()
return return
} }
logger.Error.Print("Continuing...")
tokenString, err := c.Cookie("jwt") tokenString, err := c.Cookie("jwt")
if err != nil { if err != nil {
logger.Error.Printf("No Auth token: %v\n", err) logger.Error.Printf("No Auth token: %v\n", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "No Auth token"}) c.JSON(http.StatusUnauthorized, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.no_auth_token",
}}})
c.Abort() c.Abort()
return return
} }
@@ -47,7 +70,10 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
if err != nil { if err != nil {
logger.Error.Printf("Error retrieving token and claims from JWT") logger.Error.Printf("Error retrieving token and claims from JWT")
c.JSON(http.StatusInternalServerError, gin.H{"error": "JWT parsing error"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.jwt_parsing_error",
}}})
return return
} }
jwtUserID := uint((*claims)["user_id"].(float64)) jwtUserID := uint((*claims)["user_id"].(float64))
@@ -55,22 +81,27 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
if user.ID == 0 { if user.ID == 0 {
logger.Error.Printf("No User.ID in request from user with id: %v, aborting", jwtUserID) logger.Error.Printf("No User.ID in request from user with id: %v, aborting", jwtUserID)
c.JSON(http.StatusBadRequest, gin.H{"error": "No user id provided"}) c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "id",
"key": "server.validation.no_user_id_provided",
}}})
return return
} }
if user.ID != jwtUserID && userRole < constants.Roles.Editor { if user.ID != jwtUserID && userRole < constants.Roles.Editor {
c.JSON(http.StatusForbidden, gin.H{"error": "You are not authorized to update this user"}) c.JSON(http.StatusForbidden, gin.H{"errors": []gin.H{{
return "field": "general",
} "key": "server.error.unauthorized_update",
if user.Membership.SubscriptionModel.Name == "" { }}})
logger.Error.Printf("No subscription model provided: %v", user.Email)
c.JSON(http.StatusNotAcceptable, gin.H{"error": "No subscription model provided"})
return return
} }
selectedModel, err := uc.MembershipService.GetModelByName(&user.Membership.SubscriptionModel.Name) selectedModel, err := uc.MembershipService.GetModelByName(&user.Membership.SubscriptionModel.Name)
if err != nil { if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", user.Email, err) logger.Error.Printf("%v:No subscription model found: %#v", user.Email, err)
c.JSON(http.StatusNotFound, gin.H{"error": "Not a valid subscription model"}) c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "subscription_model",
"key": "server.validation.invalid_subscription_model",
}}})
return return
} }
user.Membership.SubscriptionModel = *selectedModel user.Membership.SubscriptionModel = *selectedModel
@@ -84,21 +115,29 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
// user.Email = existingUser.Email // user.Email = existingUser.Email
// user.RoleID = existingUser.RoleID // user.RoleID = existingUser.RoleID
// } // }
updatedUser, err := uc.Service.UpdateUser(&user, userRole) updatedUser, err := uc.Service.UpdateUser(&user, userRole)
if err != nil { if err != nil {
switch err { switch err {
case errors.ErrUserNotFound: case errors.ErrUserNotFound:
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": user.FirstName + " " + user.LastName,
"key": "server.validation.user_not_found",
}}})
case errors.ErrInvalidUserData: case errors.ErrInvalidUserData:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user data"}) c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "user",
"key": "server.validation.invalid_user_data",
}}})
default: default:
logger.Error.Printf("Failed to update user: %v", err) logger.Error.Printf("Failed to update user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server error"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
} "field": "general",
"key": "server.error.internal_server_error",
}}})
return return
} }
}
c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser}) c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser})
} }
@@ -106,37 +145,51 @@ func (uc *UserController) CurrentUserHandler(c *gin.Context) {
userIDInterface, ok := c.Get("user_id") userIDInterface, ok := c.Get("user_id")
if !ok || userIDInterface == nil { if !ok || userIDInterface == nil {
logger.Error.Printf("Error getting user_id from header") logger.Error.Printf("Error getting user_id from header")
c.JSON(http.StatusInternalServerError, gin.H{"error": "Missing or invalid user ID type"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.validation.no_user_id_provided",
}}})
return return
} }
userID, ok := userIDInterface.(uint) userID, ok := userIDInterface.(uint)
if !ok { if !ok {
logger.Error.Printf("Error: user_id is not of type uint") logger.Error.Printf("Error: user_id is not of type uint")
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "user",
"key": "server.error.internal_server_error",
}}})
return return
} }
user, err := uc.Service.GetUserByID(uint(userID)) user, err := uc.Service.GetUserByID(uint(userID))
if err != nil { if err != nil {
logger.Error.Printf("Error retrieving valid user: %v", err) logger.Error.Printf("Error retrieving valid user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving user."}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return return
} }
subscriptions, err := uc.MembershipService.GetSubscriptions(nil) subscriptions, err := uc.MembershipService.GetSubscriptions(nil)
if err != nil { if err != nil {
logger.Error.Printf("Error retrieving subscriptions: %v", err) logger.Error.Printf("Error retrieving subscriptions: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving subscriptions."}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "validation.internal_server_error",
}}})
return return
} }
licenceCategories, err := uc.DriversLicenceService.GetAllCategories() licenceCategories, err := uc.DriversLicenceService.GetAllCategories()
if err != nil { if err != nil {
logger.Error.Printf("Error retrieving licence categories: %v", err) logger.Error.Printf("Error retrieving licence categories: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving licence categories."}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "validation.internal_server_error",
}}})
return return
} }
logger.Error.Printf("licenceCategories: %#v", licenceCategories)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"user": user.Safe(), "user": user.Safe(),
"subscriptions": subscriptions, "subscriptions": subscriptions,
@@ -164,14 +217,20 @@ func (uc *UserController) LoginHandler(c *gin.Context) {
if err := c.ShouldBindJSON(&input); err != nil { if err := c.ShouldBindJSON(&input); err != nil {
logger.Error.Printf("Couldn't decode input: %v", err.Error()) logger.Error.Printf("Couldn't decode input: %v", err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode request data"}) c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.invalid_json",
}}})
return return
} }
user, err := uc.Service.GetUserByEmail(input.Email) user, err := uc.Service.GetUserByEmail(input.Email)
if err != nil { if err != nil {
logger.Error.Printf("Error during user(%v) retrieval: %v\n", input.Email, err) logger.Error.Printf("Error during user(%v) retrieval: %v\n", input.Email, err)
c.JSON(http.StatusNotFound, gin.H{"error": "Couldn't find user"}) c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "login",
"key": "server.validation.user_not_found_or_wrong_password",
}}})
return return
} }
@@ -179,20 +238,29 @@ func (uc *UserController) LoginHandler(c *gin.Context) {
if err != nil { if err != nil {
logger.Error.Printf("Error during Password comparison: %v", err.Error()) logger.Error.Printf("Error during Password comparison: %v", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "couldn't calculate match"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return return
} }
if !ok { if !ok {
logger.Error.Printf("Wrong Password: %v %v", user.FirstName, user.LastName) logger.Error.Printf("Wrong Password: %v %v", user.FirstName, user.LastName)
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Wrong Password"}) c.JSON(http.StatusNotAcceptable, gin.H{"errors": []gin.H{{
"field": "login",
"key": "server.validation.user_not_found_or_wrong_password",
}}})
return return
} }
logger.Error.Printf("jwtsevret: %v", config.Auth.JWTSecret) logger.Error.Printf("jwtsevret: %v", config.Auth.JWTSecret)
token, err := middlewares.GenerateToken(config.Auth.JWTSecret, user, "") token, err := middlewares.GenerateToken(config.Auth.JWTSecret, user, "")
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate JWT token"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.jwt_generation_failed",
}}})
return return
} }
@@ -209,18 +277,31 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
if err := c.ShouldBindJSON(&regData); err != nil { if err := c.ShouldBindJSON(&regData); err != nil {
logger.Error.Printf("Couldn't decode Userdata: %v", err) logger.Error.Printf("Couldn't decode Userdata: %v", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode userdata"}) var validationErrors []gin.H
return if ve, ok := err.(validator.ValidationErrors); ok {
} for _, e := range ve {
if regData.User.Membership.SubscriptionModel.Name == "" { validationErrors = append(validationErrors, gin.H{
logger.Error.Printf("No subscription model provided: %v", regData.User.Email) "field": e.Field(),
c.JSON(http.StatusNotAcceptable, gin.H{"error": "No subscription model provided"}) "key": "server.validation." + e.Tag(),
})
}
} else {
validationErrors = append(validationErrors, gin.H{
"field": "general",
"key": "server.error.invalid_json",
})
}
c.JSON(http.StatusBadRequest, gin.H{"error": validationErrors})
return return
} }
selectedModel, err := uc.MembershipService.GetModelByName(&regData.User.Membership.SubscriptionModel.Name) selectedModel, err := uc.MembershipService.GetModelByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil { if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", regData.User.Email, err) logger.Error.Printf("%v:No subscription model found: %#v", regData.User.Email, err)
c.JSON(http.StatusNotFound, gin.H{"error": "Not a valid subscription model"}) c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "subscription_model",
"key": "server.validation.invalid_subscription_model",
}}})
return return
} }
regData.User.Membership.SubscriptionModel = *selectedModel regData.User.Membership.SubscriptionModel = *selectedModel
@@ -231,7 +312,18 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
id, token, err := uc.Service.RegisterUser(&regData.User) id, token, err := uc.Service.RegisterUser(&regData.User)
if err != nil { if err != nil {
logger.Error.Printf("Couldn't register User(%v): %v", regData.User.Email, err) logger.Error.Printf("Couldn't register User(%v): %v", regData.User.Email, err)
c.JSON(int(id), gin.H{"error": "Couldn't register User"}) if strings.Contains(err.Error(), "UNIQUE constraint failed: users.email") {
c.JSON(http.StatusConflict, gin.H{"errors": []gin.H{{
"field": "email",
"key": "server.validation.email_already_registered",
}}})
} else {
logger.Error.Printf("Failed to register user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
}
return return
} }
regData.User.ID = id regData.User.ID = id
@@ -255,7 +347,10 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
_, err = uc.ConsentService.RegisterConsent(&consent) _, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil { if err != nil {
logger.Error.Printf("%v, Couldn't register consent: %v", regData.User.Email, err) logger.Error.Printf("%v, Couldn't register consent: %v", regData.User.Email, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't register User-consent"}) c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return return
} }
} }

View File

@@ -243,6 +243,7 @@ func testCurrentUserHandler(t *testing.T, loginEmail string, loginCookie http.Co
expectedUserMail string expectedUserMail string
expectedStatus int expectedStatus int
expectNewCookie bool expectNewCookie bool
expectedErrors []map[string]string
}{ }{
{ {
name: "With valid cookie", name: "With valid cookie",
@@ -274,6 +275,9 @@ func testCurrentUserHandler(t *testing.T, loginEmail string, loginCookie http.Co
name: "Without cookie", name: "Without cookie",
setupCookie: func(req *http.Request) {}, setupCookie: func(req *http.Request) {},
expectedStatus: http.StatusUnauthorized, expectedStatus: http.StatusUnauthorized,
expectedErrors: []map[string]string{
{"field": "general", "key": "server.error.no_auth_token"},
},
}, },
{ {
name: "With invalid cookie", name: "With invalid cookie",
@@ -281,6 +285,9 @@ func testCurrentUserHandler(t *testing.T, loginEmail string, loginCookie http.Co
req.AddCookie(&invalidCookie) req.AddCookie(&invalidCookie)
}, },
expectedStatus: http.StatusUnauthorized, expectedStatus: http.StatusUnauthorized,
expectedErrors: []map[string]string{
{"field": "general", "key": "server.error.no_auth_token"},
},
}, },
} }
@@ -327,12 +334,22 @@ func testCurrentUserHandler(t *testing.T, loginEmail string, loginCookie http.Co
assert.Nil(t, newCookie, "No new cookie should be set for non-expired token") assert.Nil(t, newCookie, "No new cookie should be set for non-expired token")
} }
} else { } else {
// For unauthorized requests, check for an error message // For unauthorized requests, check for the new error structure
var errorResponse map[string]string var errorResponse map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &errorResponse) err := json.Unmarshal(w.Body.Bytes(), &errorResponse)
assert.NoError(t, err) assert.NoError(t, err)
assert.Contains(t, errorResponse, "error")
assert.NotEmpty(t, errorResponse["error"]) errors, ok := errorResponse["errors"].([]interface{})
assert.True(t, ok, "Expected 'errors' field in response")
assert.Len(t, errors, len(tt.expectedErrors), "Unexpected number of errors")
for i, expectedError := range tt.expectedErrors {
if i < len(errors) {
actualError := errors[i].(map[string]interface{})
assert.Equal(t, expectedError["field"], actualError["field"], "Mismatched error field")
assert.Equal(t, expectedError["key"], actualError["key"], "Mismatched error key")
}
}
} }
}) })
@@ -414,7 +431,7 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
setupCookie func(*http.Request) setupCookie func(*http.Request)
updateFunc func(*models.User) updateFunc func(*models.User)
expectedStatus int expectedStatus int
expectedError string expectedErrors []map[string]string
}{ }{
{ {
name: "Valid Update", name: "Valid Update",
@@ -441,7 +458,9 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
u.Phone = "01738484994" u.Phone = "01738484994"
}, },
expectedStatus: http.StatusUnauthorized, expectedStatus: http.StatusUnauthorized,
expectedError: "Auth token invalid", expectedErrors: []map[string]string{
{"field": "general", "key": "server.error.no_auth_token"},
},
}, },
{ {
name: "Invalid Email Update", name: "Invalid Email Update",
@@ -456,7 +475,9 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
u.Email = "invalid-email" u.Email = "invalid-email"
}, },
expectedStatus: http.StatusBadRequest, expectedStatus: http.StatusBadRequest,
expectedError: "Invalid user data", expectedErrors: []map[string]string{
{"field": "Email", "key": "server.validation.email"},
},
}, },
{ {
name: "Change LicenceNumber", name: "Change LicenceNumber",
@@ -491,7 +512,7 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
expectedStatus: http.StatusAccepted, expectedStatus: http.StatusAccepted,
}, },
{ {
name: "Add 2 categories", name: "Delete 1 and add 1 category",
setupCookie: func(req *http.Request) { setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie) req.AddCookie(&loginCookie)
}, },
@@ -502,13 +523,31 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
u.Phone = "01738484994" u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "B072RRE2I50" u.DriversLicence.LicenceNumber = "B072RRE2I50"
var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{} var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{}
category, err := licenceRepo.FindCategoryByName("B") category, err := licenceRepo.FindCategoryByName("A")
category2, err := licenceRepo.FindCategoryByName("BE") category2, err := licenceRepo.FindCategoryByName("BE")
assert.NoError(t, err) assert.NoError(t, err)
u.DriversLicence.LicenceCategories = []models.LicenceCategory{category, category2} u.DriversLicence.LicenceCategories = []models.LicenceCategory{category, category2}
}, },
expectedStatus: http.StatusAccepted, expectedStatus: http.StatusAccepted,
}, },
{
name: "Delete 1 category",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = ""
u.FirstName = "John Updated"
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "B072RRE2I50"
var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{}
category, err := licenceRepo.FindCategoryByName("A")
assert.NoError(t, err)
u.DriversLicence.LicenceCategories = []models.LicenceCategory{category}
},
expectedStatus: http.StatusAccepted,
},
{ {
name: "Delete all categories", name: "Delete all categories",
setupCookie: func(req *http.Request) { setupCookie: func(req *http.Request) {
@@ -538,7 +577,9 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
u.FirstName = "John Missing ID" u.FirstName = "John Missing ID"
}, },
expectedStatus: http.StatusForbidden, expectedStatus: http.StatusForbidden,
expectedError: "You are not authorized to update this user", expectedErrors: []map[string]string{
{"field": "general", "key": "server.error.unauthorized_update"},
},
}, },
{ {
name: "Password Update", name: "Password Update",
@@ -582,7 +623,7 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
t.Fatalf("Failed to marshal user data: %v", err) t.Fatalf("Failed to marshal user data: %v", err)
} }
logger.Error.Printf("Updated User: %#v", updatedUser) // logger.Error.Printf("Updated User: %#v", updatedUser)
// Create request // Create request
req, _ := http.NewRequest("PUT", "/users/"+strconv.FormatUint(uint64(user.ID), 10), bytes.NewBuffer(jsonData)) req, _ := http.NewRequest("PUT", "/users/"+strconv.FormatUint(uint64(user.ID), 10), bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
@@ -599,18 +640,38 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
// Perform request // Perform request
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
bodyBytes, _ := io.ReadAll(w.Body)
t.Logf("Response Body: %s", string(bodyBytes))
// Check status code // Check status code
assert.Equal(t, tt.expectedStatus, w.Code) assert.Equal(t, tt.expectedStatus, w.Code)
// Parse response // Parse response
var response map[string]interface{} var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response) err = json.Unmarshal(bodyBytes, &response)
assert.NoError(t, err) if err != nil {
t.Fatalf("Failed to unmarshal response body: %v", err)
}
if tt.expectedError != "" { if tt.expectedErrors != nil {
assert.Equal(t, tt.expectedError, response["error"]) errors, ok := response["errors"].([]interface{})
if !ok {
t.Fatalf("Expected 'errors' field in response, got: %v", response)
}
assert.Len(t, errors, len(tt.expectedErrors), "Unexpected number of errors")
for i, expectedError := range tt.expectedErrors {
if i < len(errors) {
actualError := errors[i].(map[string]interface{})
assert.Equal(t, expectedError["field"], actualError["field"], "Mismatched error field")
assert.Equal(t, expectedError["key"], actualError["key"], "Mismatched error key")
}
}
} else { } else {
assert.Equal(t, "User updated successfully", response["message"]) // Check for success message
message, ok := response["message"].(string)
if !ok {
t.Fatalf("Expected 'message' field in response, got: %v", response)
}
assert.Equal(t, "User updated successfully", message)
// Verify the update in the database // Verify the update in the database
updatedUserFromDB, err := Uc.Service.GetUserByID(user.ID) updatedUserFromDB, err := Uc.Service.GetUserByID(user.ID)
@@ -844,7 +905,7 @@ func getTestUsers() []RegisterUserTest {
return []RegisterUserTest{ return []RegisterUserTest{
{ {
Name: "birthday < 18 should fail", Name: "birthday < 18 should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -854,7 +915,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "FirstName empty, should fail", Name: "FirstName empty, should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -864,7 +925,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "LastName Empty should fail", Name: "LastName Empty should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -874,7 +935,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "EMail wrong format should fail", Name: "EMail wrong format should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "johnexample.com"}, WantDBData: map[string]interface{}{"email": "johnexample.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -884,7 +945,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Missing Zip Code should fail", Name: "Missing Zip Code should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -894,7 +955,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Missing Address should fail", Name: "Missing Address should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -904,7 +965,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Missing City should fail", Name: "Missing City should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -914,7 +975,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Missing IBAN should fail", Name: "Missing IBAN should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -924,7 +985,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Invalid IBAN should fail", Name: "Invalid IBAN should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -934,7 +995,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Missing subscription plan should fail", Name: "Missing subscription plan should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -944,7 +1005,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Invalid subscription plan should fail", Name: "Invalid subscription plan should fail",
WantResponse: http.StatusNotFound, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -982,7 +1043,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Subscription constraints not entered; should fail", Name: "Subscription constraints not entered; should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -993,7 +1054,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "Subscription constraints wrong; should fail", Name: "Subscription constraints wrong; should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
@@ -1028,7 +1089,7 @@ func getTestUsers() []RegisterUserTest {
}, },
{ {
Name: "wrong driverslicence number, should fail", Name: "wrong driverslicence number, should fail",
WantResponse: http.StatusNotAcceptable, WantResponse: http.StatusBadRequest,
WantDBData: map[string]interface{}{"email": "john.wronglicence.doe@example.com"}, WantDBData: map[string]interface{}{"email": "john.wronglicence.doe@example.com"},
Assert: false, Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {

View File

@@ -78,7 +78,11 @@ func AuthMiddleware() gin.HandlerFunc {
tokenString, err := c.Cookie("jwt") tokenString, err := c.Cookie("jwt")
if err != nil { if err != nil {
logger.Error.Printf("No Auth token: %v\n", err) logger.Error.Printf("No Auth token: %v\n", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "No Auth token"}) c.JSON(http.StatusUnauthorized,
gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.no_auth_token",
}}})
c.Abort() c.Abort()
return return
} }
@@ -91,7 +95,11 @@ func AuthMiddleware() gin.HandlerFunc {
return return
} }
logger.Error.Printf("Token(%v) is invalid: %v\n", tokenString, err) logger.Error.Printf("Token(%v) is invalid: %v\n", tokenString, err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Auth token invalid"}) c.JSON(http.StatusUnauthorized,
gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.no_auth_token",
}}})
c.Abort() c.Abort()
return return
} }