I have this code for request handler:
func (h *Handlers) UpdateProfile() gin.HandlerFunc {
type request struct {
Username string `json:"username" binding:"required,min=4,max=20"`
Description string `json:"description" binding:"required,max=100"`
}
return func(c *gin.Context) {
var updateRequest request
if err := c.BindJSON(&updateRequest); err != nil {
var validationErrors validator.ValidationErrors
if errors.As(err, &validationErrors) {
validateErrors := base.BindingError(validationErrors)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": validateErrors})
} else {
c.AbortWithError(http.StatusBadRequest, err)
}
return
}
avatar, err := c.FormFile("avatar")
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "image not contains in request",
})
return
}
log.Print(avatar)
if avatar.Size > 3<<20 { // if avatar size more than 3mb
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "image is too large",
})
return
}
file, err := avatar.Open()
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
}
session := sessions.Default(c)
id := session.Get("sessionId")
log.Printf("ID type: %T", id)
err = h.userService.UpdateProfile(fmt.Sprintf("%v", id), file, updateRequest.Username, updateRequest.Description)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{})
return
}
c.IndentedJSON(http.StatusNoContent, gin.H{"message": "succesfull update"})
}
}
And I have this unit test for this handler:
func TestUser_UpdateProfile(t *testing.T) {
type testCase struct {
name string
image io.Reader
username string
description string
expectedStatusCode int
}
router := gin.Default()
memStore := memstore.NewStore([]byte("secret"))
router.Use(sessions.Sessions("session", memStore))
userGroup := router.Group("user")
repo := user.NewMemory()
service := userService.New(repo)
userHandlers.Register(userGroup, service)
testImage := make([]byte, 100)
rand.Read(testImage)
image := bytes.NewReader(testImage)
testCases := []testCase{
{
name: "Request With Image",
image: image,
username: "bobik",
description: "wanna be sharik",
expectedStatusCode: http.StatusNoContent,
},
{
name: "Request Without Image",
image: nil,
username: "sharik",
description: "wanna be bobik",
expectedStatusCode: http.StatusNoContent,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
imageWriter, err := writer.CreateFormFile("avatar", "test_avatar.jpg")
if err != nil {
t.Fatal(err)
}
if _, err := io.Copy(imageWriter, image); err != nil {
t.Fatal(err)
}
data := map[string]interface{}{
"username": tc.username,
"description": tc.description,
}
jsonData, err := json.Marshal(data)
if err != nil {
t.Fatal(err)
}
jsonWriter, err := writer.CreateFormField("json")
if err != nil {
t.Fatal(err)
}
if _, err := jsonWriter.Write(jsonData); err != nil {
t.Fatal(err)
}
writer.Close()
// Creating request
req := httptest.NewRequest(
http.MethodPost,
"http://localhost:8080/user/account/updateprofile",
body,
)
req.Header.Set("Content-Type", writer.FormDataContentType())
log.Print(req)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, tc.expectedStatusCode, w.Result().StatusCode)
})
}
}
During test I have this error: Error #01: invalid character '-' in numeric literal
And here is request body (I am printing it with log.Print(req)):
&{POST http://localhost:8080/user/account/updateprofile HTTP/1.1 1 1 map[Content-Type:[multipart/form-data; boundary=30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035]] {--30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035
Content-Disposition: form-data; name="avatar"; filename="test_avatar.jpg"
Content-Type: application/octet-stream
--30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035
Content-Disposition: form-data; name="json"
{"description":"wanna be bobik","username":"sharik"}
--30b24345de9d8d83ecbdd146262d86894c45b4f3485e4615553621fd2035--
} <nil> 414 [] false localhost:8080 map[] map[] <nil> map[] 192.0.2.1:1234 http://localhost:8080/user/account/updateprofile <nil> <nil> <nil> <nil>}
First I just have strings as json data and converted it to bytes. When error appeared I converted json data using json.Marshal, but it didn't work out. I want to parse json data with c.Bind and parse given image with c.FormFile, does it possible?
Upd. I replaced code to get avatar first, and then get json by Bind structure. Now I have EOF error.