Add unit tests for UpdateUserForm JSON unmarshalling
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
- Introduced `form_test.go` to validate the behavior of the `UpdateUserForm` struct's JSON unmarshalling, specifically for the `profile_image` field. - Added tests to ensure omitted `profile_image` is not treated as an update, null values clear the image, and valid URLs are preserved. - Enhanced the `UpdateUserForm` and `UpdateProfileForm` structs to include logic for distinguishing between omitted and explicitly null profile images during JSON unmarshalling. This update improves the robustness of user profile updates by ensuring correct handling of profile image data in JSON requests, enhancing overall data integrity in the user management system.
This commit is contained in:
+61
-1
@@ -1,6 +1,10 @@
|
||||
package user
|
||||
|
||||
import "tm/pkg/response"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"tm/pkg/response"
|
||||
)
|
||||
|
||||
// User Registration DTOs
|
||||
|
||||
@@ -27,6 +31,46 @@ type UpdateUserForm struct {
|
||||
Position *string `json:"position" valid:"optional,length(2|100)" example:"Lead"` // Updated position
|
||||
Phone *string `json:"phone" valid:"optional,length(10|20)" example:"+18289784438"` // Updated phone number
|
||||
ProfileImage *string `json:"profile_image" valid:"optional,url" example:""` // Updated profile image URL
|
||||
profileImageSet bool
|
||||
}
|
||||
|
||||
func unmarshalProfileImageField(data []byte, profileImage **string, profileImageSet *bool) ([]byte, error) {
|
||||
var raw map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
profileImageRaw, ok := raw["profile_image"]
|
||||
if ok {
|
||||
*profileImageSet = true
|
||||
if string(profileImageRaw) == "null" {
|
||||
*profileImage = nil
|
||||
} else {
|
||||
var image string
|
||||
if err := json.Unmarshal(profileImageRaw, &image); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*profileImage = &image
|
||||
}
|
||||
delete(raw, "profile_image")
|
||||
}
|
||||
|
||||
return json.Marshal(raw)
|
||||
}
|
||||
|
||||
// UnmarshalJSON distinguishes omitted profile_image from explicit null to allow clearing the image.
|
||||
func (f *UpdateUserForm) UnmarshalJSON(data []byte) error {
|
||||
rest, err := unmarshalProfileImageField(data, &f.ProfileImage, &f.profileImageSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type alias UpdateUserForm
|
||||
return json.Unmarshal(rest, (*alias)(f))
|
||||
}
|
||||
|
||||
func (f *UpdateUserForm) isProfileImageSet() bool {
|
||||
return f.profileImageSet
|
||||
}
|
||||
|
||||
// User Authentication DTOs
|
||||
@@ -56,6 +100,22 @@ type UpdateProfileForm struct {
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Lead"` // Updated position
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+18289784438"` // Updated phone number
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:""` // Updated profile image URL
|
||||
profileImageSet bool
|
||||
}
|
||||
|
||||
// UnmarshalJSON distinguishes omitted profile_image from explicit null to allow clearing the image.
|
||||
func (f *UpdateProfileForm) UnmarshalJSON(data []byte) error {
|
||||
rest, err := unmarshalProfileImageField(data, &f.ProfileImage, &f.profileImageSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type alias UpdateProfileForm
|
||||
return json.Unmarshal(rest, (*alias)(f))
|
||||
}
|
||||
|
||||
func (f *UpdateProfileForm) isProfileImageSet() bool {
|
||||
return f.profileImageSet
|
||||
}
|
||||
|
||||
// ResetPasswordForm represents the email address for password reset
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpdateUserForm_UnmarshalJSON_ProfileImage(t *testing.T) {
|
||||
t.Run("omitted profile_image is not treated as an update", func(t *testing.T) {
|
||||
var form UpdateUserForm
|
||||
err := json.Unmarshal([]byte(`{"full_name":"Jane Doe"}`), &form)
|
||||
if err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
if form.isProfileImageSet() {
|
||||
t.Fatal("expected profile image to be unset when omitted")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("null profile_image clears the image", func(t *testing.T) {
|
||||
var form UpdateUserForm
|
||||
err := json.Unmarshal([]byte(`{"profile_image":null}`), &form)
|
||||
if err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
if !form.isProfileImageSet() {
|
||||
t.Fatal("expected profile image to be marked as set")
|
||||
}
|
||||
if form.ProfileImage != nil {
|
||||
t.Fatalf("expected nil profile image, got %#v", form.ProfileImage)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("profile image url is preserved", func(t *testing.T) {
|
||||
var form UpdateUserForm
|
||||
err := json.Unmarshal([]byte(`{"profile_image":"https://example.com/avatar.png"}`), &form)
|
||||
if err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
if !form.isProfileImageSet() {
|
||||
t.Fatal("expected profile image to be marked as set")
|
||||
}
|
||||
if form.ProfileImage == nil || *form.ProfileImage != "https://example.com/avatar.png" {
|
||||
t.Fatalf("unexpected profile image: %#v", form.ProfileImage)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -204,7 +204,7 @@ func (s *userService) Update(ctx context.Context, id string, form *UpdateUserFor
|
||||
user.Phone = form.Phone
|
||||
}
|
||||
|
||||
if form.ProfileImage != nil {
|
||||
if form.isProfileImageSet() {
|
||||
user.ProfileImage = form.ProfileImage
|
||||
}
|
||||
|
||||
@@ -601,7 +601,7 @@ func (s *userService) UpdateProfile(ctx context.Context, userID string, form *Up
|
||||
}
|
||||
user.Phone = form.Phone
|
||||
}
|
||||
if form.ProfileImage != nil {
|
||||
if form.isProfileImageSet() {
|
||||
user.ProfileImage = form.ProfileImage
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user