Skip to content

Commit

Permalink
test(api): medias
Browse files Browse the repository at this point in the history
  • Loading branch information
sundowndev committed Nov 3, 2020
1 parent 3a58854 commit 97216c7
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 46 deletions.
27 changes: 11 additions & 16 deletions api/medias.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/dreamvo/gilfoyle/ent/schema"
"github.com/dreamvo/gilfoyle/httputils"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"net/http"
)
Expand Down Expand Up @@ -98,7 +97,7 @@ func getMedia(ctx *gin.Context) {
func deleteMedia(ctx *gin.Context) {
id := ctx.Param("id")

parsedUUID, err := uuid.Parse(id)
parsedUUID, err := httputils.ValidateUUID(id)
if err != nil {
httputils.NewError(ctx, http.StatusBadRequest, fmt.Errorf(ErrInvalidUUID))
return
Expand Down Expand Up @@ -132,14 +131,9 @@ func deleteMedia(ctx *gin.Context) {
// @Param media body CreateMedia true "Media data" validate(required)
func createMedia(ctx *gin.Context) {
var body CreateMedia
if err := ctx.BindJSON(&body); err != nil {
httputils.NewError(ctx, http.StatusBadRequest, err)
return
}

err := validator.New().Struct(body)
if err != nil {
httputils.NewValidationError(ctx, http.StatusBadRequest, err)
if err := httputils.ValidateBody(ctx, &body); err != nil {
httputils.NewValidationError(ctx, err)
return
}

Expand Down Expand Up @@ -174,9 +168,16 @@ func createMedia(ctx *gin.Context) {
// @Param id path string true "Media ID" validate(required)
// @Param media body UpdateMedia true "Media data" validate(required)
func updateMedia(ctx *gin.Context) {
var body CreateMedia

if err := httputils.ValidateBody(ctx, &body); err != nil {
httputils.NewValidationError(ctx, err)
return
}

id := ctx.Param("id")

parsedUUID, err := uuid.Parse(id)
parsedUUID, err := httputils.ValidateUUID(id)
if err != nil {
httputils.NewError(ctx, http.StatusBadRequest, fmt.Errorf(ErrInvalidUUID))
return
Expand All @@ -188,12 +189,6 @@ func updateMedia(ctx *gin.Context) {
return
}

var body UpdateMedia
if err := ctx.ShouldBindJSON(&body); err != nil {
httputils.NewError(ctx, http.StatusBadRequest, errors.Unwrap(err))
return
}

v, err = db.Client.Media.
UpdateOneID(parsedUUID).
SetTitle(body.Title).
Expand Down
82 changes: 67 additions & 15 deletions api/medias_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3"
assertTest "github.com/stretchr/testify/assert"
"net/http"
"testing"
)

Expand All @@ -27,7 +28,7 @@ func TestMedias(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

res, err := performRequest(r, "GET", "/medias", nil)
res, err := performRequest(r, http.MethodGet, "/medias", nil)
assert.NoError(err, "should be equal")

var body struct {
Expand All @@ -53,7 +54,7 @@ func TestMedias(t *testing.T) {
Save(context.Background())
}

res, err := performRequest(r, "GET", "/medias", nil)
res, err := performRequest(r, http.MethodGet, "/medias", nil)
assert.NoError(err, "should be equal")

var body struct {
Expand All @@ -79,7 +80,7 @@ func TestMedias(t *testing.T) {
Save(context.Background())
}

res, err := performRequest(r, "GET", "/medias?limit=2", nil)
res, err := performRequest(r, http.MethodGet, "/medias?limit=2", nil)
assert.NoError(err, "should be equal")

var body struct {
Expand Down Expand Up @@ -109,7 +110,7 @@ func TestMedias(t *testing.T) {
SetStatus(schema.MediaStatusProcessing).
Save(context.Background())

res, err := performRequest(r, "GET", "/medias?offset=1", nil)
res, err := performRequest(r, http.MethodGet, "/medias?offset=1", nil)
assert.NoError(err, "should be equal")

var body struct {
Expand All @@ -127,7 +128,7 @@ func TestMedias(t *testing.T) {

t.Run("GET /medias/{id}", func(t *testing.T) {
t.Run("should return error for invalid UUID", func(t *testing.T) {
res, err := performRequest(r, "GET", "/medias/uuid", nil)
res, err := performRequest(r, http.MethodGet, "/medias/uuid", nil)
assert.NoError(err, "should be equal")

var body httputils.ErrorResponse
Expand All @@ -148,7 +149,7 @@ func TestMedias(t *testing.T) {
SetStatus(schema.MediaStatusProcessing).
Save(context.Background())

res, err := performRequest(r, "GET", "/medias/"+v.ID.String(), nil)
res, err := performRequest(r, http.MethodGet, "/medias/"+v.ID.String(), nil)
assert.NoError(err, "should be equal")

var body struct {
Expand All @@ -174,12 +175,12 @@ func TestMedias(t *testing.T) {
SetStatus(schema.MediaStatusProcessing).
Save(context.Background())

res, err := performRequest(r, "DELETE", "/medias/"+v.ID.String(), nil)
res, err := performRequest(r, http.MethodDelete, "/medias/"+v.ID.String(), nil)
assert.NoError(err, "should be equal")

assert.Equal(res.Result().StatusCode, 200, "should be equal")

res, err = performRequest(r, "DELETE", "/medias/"+v.ID.String(), nil)
res, err = performRequest(r, http.MethodDelete, "/medias/"+v.ID.String(), nil)
assert.NoError(err, "should be equal")

var body httputils.ErrorResponse
Expand All @@ -193,7 +194,7 @@ func TestMedias(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

res, err := performRequest(r, "DELETE", "/medias/uuid", nil)
res, err := performRequest(r, http.MethodDelete, "/medias/uuid", nil)
assert.NoError(err, "should be equal")

var body httputils.ErrorResponse
Expand All @@ -210,7 +211,7 @@ func TestMedias(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

res, err := performRequest(r, "POST", "/medias", CreateMedia{
res, err := performRequest(r, http.MethodPost, "/medias", CreateMedia{
Title: "test",
})
assert.NoError(err)
Expand All @@ -227,7 +228,7 @@ func TestMedias(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

res, err := performRequest(r, "POST", "/medias", CreateMedia{
res, err := performRequest(r, http.MethodPost, "/medias", CreateMedia{
Title: "Vitae sunt aspernatur quia sunt blanditiis at et excepturi. Doloribus non ut minus saepe. Quas enim minus modi possimus. Blanditiis eius in ipsam incidunt rem et. Rerum blanditiis consequatur facilis eos quia. Sed autem inventore iure ducimus voluptas voluptas.",
})
assert.NoError(err, "should be equal")
Expand All @@ -246,7 +247,57 @@ func TestMedias(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

res, err := performRequest(r, "POST", "/medias", nil)
res, err := performRequest(r, http.MethodPost, "/medias", nil)
assert.NoError(err, "should be equal")

var body httputils.ValidationErrorResponse
_ = json.NewDecoder(res.Body).Decode(&body)

assert.Equal(400, res.Result().StatusCode, "should be equal")
assert.Equal("Bad request", body.Message)
assert.Equal(map[string]string(nil), body.Fields)
})
})

t.Run("PATCH /medias/{id}", func(t *testing.T) {
t.Run("should update a media", func(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

m, err := db.Client.Media.
Create().
SetTitle("test").
SetStatus(schema.MediaStatusProcessing).
Save(context.Background())
assert.NoError(err)

res, err := performRequest(r, http.MethodPatch, "/medias/"+m.ID.String(), CreateMedia{
Title: "test2",
})
assert.NoError(err)

var body httputils.DataResponse
_ = json.NewDecoder(res.Body).Decode(&body)

assert.Equal(200, res.Result().StatusCode)
assert.Equal("test2", body.Data.(map[string]interface{})["title"])
assert.Equal("processing", body.Data.(map[string]interface{})["status"])
})

t.Run("should return validation error", func(t *testing.T) {
db.Client = enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
defer db.Client.Close()

m, err := db.Client.Media.
Create().
SetTitle("test").
SetStatus(schema.MediaStatusProcessing).
Save(context.Background())
assert.NoError(err)

res, err := performRequest(r, http.MethodPatch, "/medias/"+m.ID.String(), CreateMedia{
Title: "Vitae sunt aspernatur quia sunt blanditiis at et excepturi. Doloribus non ut minus saepe. Quas enim minus modi possimus. Blanditiis eius in ipsam incidunt rem et. Rerum blanditiis consequatur facilis eos quia. Sed autem inventore iure ducimus voluptas voluptas.",
})
assert.NoError(err, "should be equal")

var body httputils.ValidationErrorResponse
Expand All @@ -255,10 +306,11 @@ func TestMedias(t *testing.T) {
assert.Equal(400, res.Result().StatusCode, "should be equal")
assert.Equal("Some parameters are missing or invalid", body.Message)
assert.Equal(map[string]string{
"title": "Key: 'CreateMedia.Title' Error:Field validation for 'Title' failed on the 'required' tag",
"title": "Key: 'CreateMedia.Title' Error:Field validation for 'Title' failed on the 'lte' tag",
}, body.Fields)
})
})

t.Run("PATCH /medias/{id}", func(t *testing.T) {})
t.Run("should return validation error because of bad UUID", func(t *testing.T) {})
t.Run("should return resource not found", func(t *testing.T) {})
})
}
9 changes: 9 additions & 0 deletions config/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package config

const unknown = "unknown"

// Version is the corresponding release tag
var Version = unknown

// Commit is the corresponding Git commit
var Commit = unknown
30 changes: 15 additions & 15 deletions httputils/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,49 @@ import (
"strings"
)

// ErrorResponse example
const (
ValidationErrorCode int = 400
)

// ErrorResponse represents an error API response
type ErrorResponse struct {
Code int `json:"code" example:"400"`
Message string `json:"message" example:"status bad request"`
}

type ValidationField struct {
Tag string `json:"tag"`
Type string `json:"type"`
}

// ValidationErrorResponse represents a validation error API response
type ValidationErrorResponse struct {
Code int `json:"code" example:"400"`
Message string `json:"message" example:"status bad request"`
Code int `json:"code" example:"400"`
Message string `json:"message" example:"status bad request"`
Fields map[string]string `json:"fields"`
}

// NewError returns a new error response
func NewError(ctx *gin.Context, status int, err error) {
er := ErrorResponse{
response := ErrorResponse{
Code: status,
Message: err.Error(),
}
ctx.JSON(status, er)
ctx.JSON(status, response)
}

// NewValidationError returns a new validation error response
func NewValidationError(ctx *gin.Context, status int, err error) {
func NewValidationError(ctx *gin.Context, err error) {
if _, ok := err.(*validator.InvalidValidationError); ok {
NewError(ctx, 500, fmt.Errorf("Unexpected error occurred"))
NewError(ctx, ValidationErrorCode, fmt.Errorf("Bad request"))
return
}

fields := map[string]string{}

for _, err := range err.(validator.ValidationErrors) {
fields[strings.ToLower(err.Field())] = err.Error()
}

response := ValidationErrorResponse{
Code: status,
Code: ValidationErrorCode,
Message: "Some parameters are missing or invalid",
Fields: fields,
}
ctx.JSON(status, response)
ctx.JSON(ValidationErrorCode, response)
}
25 changes: 25 additions & 0 deletions httputils/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package httputils

import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
)

func ValidateBody(ctx *gin.Context, obj interface{}) error {
err := ctx.BindJSON(&obj)
if err != nil {
return err
}

if err := validator.New().Struct(obj); err != nil {
return err
}

return nil
}

func ValidateUUID(id string) (uuid.UUID, error) {
parsedUUID, err := uuid.Parse(id)
return parsedUUID, err
}

0 comments on commit 97216c7

Please sign in to comment.