Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
meysamhadeli committed Oct 8, 2022
1 parent b25338c commit 43cd435
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 67 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func EchoErrorHandler(error error, c echo.Context) {

// resolve problem details error from response in echo
if !c.Response().Committed {
if err := problem.ResolveProblemDetails(c.Response(), c.Request(), error); err != nil {
if _, err := problem.ResolveProblemDetails(c.Response(), c.Request(), error); err != nil {
log.Error(err)
}
}
Expand Down Expand Up @@ -110,7 +110,7 @@ func GinErrorHandler() gin.HandlerFunc {
// add custom map problem details here...
if err := problem.ResolveProblemDetails(c.Writer, c.Request, err); err != nil {
if _, err := problem.ResolveProblemDetails(c.Writer, c.Request, err); err != nil {
log.Error(err)
}
}
Expand Down
53 changes: 30 additions & 23 deletions problem_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func Map[T error](funcProblem func() ProblemDetailErr) {
}

// ResolveProblemDetails retrieve and resolve error with format problem details error
func ResolveProblemDetails(w http.ResponseWriter, r *http.Request, err error) error {
func ResolveProblemDetails(w http.ResponseWriter, r *http.Request, err error) (ProblemDetailErr, error) {

var statusCode int = http.StatusInternalServerError
var echoError *echo.HTTPError
Expand All @@ -150,25 +150,31 @@ func ResolveProblemDetails(w http.ResponseWriter, r *http.Request, err error) er
}
if gin.Mode() == gin.TestMode {
var rw = w.(*httptest.ResponseRecorder)
statusCode = rw.Code
if rw.Code != http.StatusOK {
statusCode = rw.Code
}
}
err = err.(*gin.Error).Err.(error)
}

var mapCustomTypeErr, mapCustomType = setMapCustomType(w, r, err)
if mapCustomType {
return mapCustomTypeErr
var mapCustomType, mapCustomTypeErr = setMapCustomType(w, r, err)
if mapCustomType != nil {
return mapCustomType, mapCustomTypeErr
}

var mapStatusErr, mapStatus = setMapStatusCode(w, r, err, statusCode)
if mapStatus {
return mapStatusErr
var mapStatus, mapStatusErr = setMapStatusCode(w, r, err, statusCode)
if mapStatus != nil {
return mapStatus, mapStatusErr
}

return setDefaultProblemDetails(w, r, err, statusCode)
var p, errr = setDefaultProblemDetails(w, r, err, statusCode)
if errr != nil {
return nil, err
}
return p, errr
}

func setMapCustomType(w http.ResponseWriter, r *http.Request, err error) (error, bool) {
func setMapCustomType(w http.ResponseWriter, r *http.Request, err error) (ProblemDetailErr, error) {

problemCustomType := mappers[reflect.TypeOf(err)]
if problemCustomType != nil {
Expand All @@ -180,36 +186,36 @@ func setMapCustomType(w http.ResponseWriter, r *http.Request, err error) (error,
if k == prob.GetStatus() {
_, err = writeTo(w, v())
if err != nil {
return err, false
return nil, err
}
return err, true
return prob, err
}
}

_, err = writeTo(w, prob)
if err != nil {
return err, false
return nil, err
}
return err, true
return prob, err
}
return err, false
return nil, err
}

func setMapStatusCode(w http.ResponseWriter, r *http.Request, err error, statusCode int) (error, bool) {
func setMapStatusCode(w http.ResponseWriter, r *http.Request, err error, statusCode int) (ProblemDetailErr, error) {
problemStatus := mapperStatus[statusCode]
if problemStatus != nil {
prob := problemStatus()
validationProblems(prob, err, r)
_, err = writeTo(w, prob)
if err != nil {
return err, false
return nil, err
}
return err, true
return prob, err
}
return err, false
return nil, err
}

func setDefaultProblemDetails(w http.ResponseWriter, r *http.Request, err error, statusCode int) error {
func setDefaultProblemDetails(w http.ResponseWriter, r *http.Request, err error, statusCode int) (ProblemDetailErr, error) {
defaultProblem := func() ProblemDetailErr {
return &problemDetail{
Type: getDefaultType(statusCode),
Expand All @@ -219,11 +225,12 @@ func setDefaultProblemDetails(w http.ResponseWriter, r *http.Request, err error,
Instance: r.URL.RequestURI(),
}
}
_, err = writeTo(w, defaultProblem())
prob := defaultProblem()
_, err = writeTo(w, prob)
if err != nil {
return err
return nil, err
}
return err
return prob, err
}

func validationProblems(problem ProblemDetailErr, err error, r *http.Request) {
Expand Down
191 changes: 151 additions & 40 deletions problem_details_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,46 @@ func TestMap_CustomType_Echo(t *testing.T) {

err := echo_endpoint1(c)

var problemErr ProblemDetailErr

Map[custom_errors.BadRequestError](func() ProblemDetailErr {
problemErr = New(http.StatusBadRequest, "bad-request", err.Error())
return problemErr
return New(http.StatusBadRequest, "bad-request", err.Error())
})

_ = ResolveProblemDetails(c.Response(), c.Request(), err)
p, _ := ResolveProblemDetails(c.Response(), c.Request(), err)

assert.Equal(t, c.Response().Status, http.StatusBadRequest)
assert.Equal(t, err.Error(), problemErr.GetDetails())
assert.Equal(t, "bad-request", problemErr.GetTitle())
assert.Equal(t, "https://httpstatuses.io/400", problemErr.GetType())
assert.Equal(t, http.StatusBadRequest, problemErr.GetStatus())
assert.Equal(t, err.Error(), p.GetDetails())
assert.Equal(t, "bad-request", p.GetTitle())
assert.Equal(t, "https://httpstatuses.io/400", p.GetType())
assert.Equal(t, http.StatusBadRequest, p.GetStatus())
}

func TestMap_Custom_Problem_Err_Echo(t *testing.T) {

e := echo.New()
req := httptest.NewRequest(http.MethodGet, "http://echo_endpoint4", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)

err := echo_endpoint4(c)

Map[custom_errors.ConflictError](func() ProblemDetailErr {
return &CustomProblemDetailTest{
ProblemDetailErr: New(http.StatusConflict, "conflict", err.Error()),
AdditionalInfo: "some additional info...",
Description: "some description...",
}
})

p, _ := ResolveProblemDetails(c.Response(), c.Request(), err)
cp := p.(*CustomProblemDetailTest)

assert.Equal(t, c.Response().Status, http.StatusConflict)
assert.Equal(t, err.Error(), cp.GetDetails())
assert.Equal(t, "conflict", cp.GetTitle())
assert.Equal(t, "https://httpstatuses.io/409", cp.GetType())
assert.Equal(t, http.StatusConflict, cp.GetStatus())
assert.Equal(t, "some description...", cp.Description)
assert.Equal(t, "some additional info...", cp.AdditionalInfo)
}

func TestMap_Status_Echo(t *testing.T) {
Expand All @@ -54,21 +80,35 @@ func TestMap_Status_Echo(t *testing.T) {

err := echo_endpoint2(c)

var problemErr ProblemDetailErr

// map status code to problem details error
MapStatus(http.StatusBadGateway, func() ProblemDetailErr {
problemErr = New(http.StatusUnauthorized, "unauthorized", err.Error())
return problemErr
return New(http.StatusUnauthorized, "unauthorized", err.Error())
})

_ = ResolveProblemDetails(c.Response(), c.Request(), err)
p, _ := ResolveProblemDetails(c.Response(), c.Request(), err)

assert.Equal(t, c.Response().Status, http.StatusUnauthorized)
assert.Equal(t, err.(*echo.HTTPError).Message.(error).Error(), problemErr.GetDetails())
assert.Equal(t, "unauthorized", problemErr.GetTitle())
assert.Equal(t, "https://httpstatuses.io/401", problemErr.GetType())
assert.Equal(t, http.StatusUnauthorized, problemErr.GetStatus())
assert.Equal(t, err.(*echo.HTTPError).Message.(error).Error(), p.GetDetails())
assert.Equal(t, "unauthorized", p.GetTitle())
assert.Equal(t, "https://httpstatuses.io/401", p.GetType())
assert.Equal(t, http.StatusUnauthorized, p.GetStatus())
}

func TestMap_Unhandled_Err_Echo(t *testing.T) {

e := echo.New()
req := httptest.NewRequest(http.MethodGet, "http://echo_endpoint3", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)

err := echo_endpoint3(c)

p, _ := ResolveProblemDetails(c.Response(), c.Request(), err)

assert.Equal(t, c.Response().Status, http.StatusInternalServerError)
assert.Equal(t, err.Error(), p.GetDetails())
assert.Equal(t, "Internal Server Error", p.GetTitle())
assert.Equal(t, "https://httpstatuses.io/500", p.GetType())
assert.Equal(t, http.StatusInternalServerError, p.GetStatus())
}

func TestMap_CustomType_Gin(t *testing.T) {
Expand All @@ -89,19 +129,54 @@ func TestMap_CustomType_Gin(t *testing.T) {

for _, err := range c.Errors {

var problemErr ProblemDetailErr

Map[custom_errors.BadRequestError](func() ProblemDetailErr {
problemErr = New(http.StatusBadRequest, "bad-request", err.Error())
return problemErr
return New(http.StatusBadRequest, "bad-request", err.Error())
})

p, _ := ResolveProblemDetails(w, req, err)

assert.Equal(t, http.StatusBadRequest, p.GetStatus())
assert.Equal(t, err.Error(), p.GetDetails())
assert.Equal(t, "bad-request", p.GetTitle())
assert.Equal(t, "https://httpstatuses.io/400", p.GetType())
}
}

func TestMap_Custom_Problem_Err_Gin(t *testing.T) {

gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
r := gin.Default()

r.GET("/gin_endpoint4", func(ctx *gin.Context) {
err := errors.New("We have a custom error with custom problem details error in our endpoint")
customConflictError := custom_errors.ConflictError{InternalError: err}
_ = c.Error(customConflictError)
})

req, _ := http.NewRequest(http.MethodGet, "/gin_endpoint4", nil)
r.ServeHTTP(w, req)

for _, err := range c.Errors {

Map[custom_errors.ConflictError](func() ProblemDetailErr {
return &CustomProblemDetailTest{
ProblemDetailErr: New(http.StatusConflict, "conflict", err.Error()),
AdditionalInfo: "some additional info...",
Description: "some description...",
}
})

_ = ResolveProblemDetails(w, req, err)
p, _ := ResolveProblemDetails(w, req, err)
cp := p.(*CustomProblemDetailTest)

assert.Equal(t, http.StatusBadRequest, problemErr.GetStatus())
assert.Equal(t, err.Error(), problemErr.GetDetails())
assert.Equal(t, "bad-request", problemErr.GetTitle())
assert.Equal(t, "https://httpstatuses.io/400", problemErr.GetType())
assert.Equal(t, http.StatusConflict, cp.GetStatus())
assert.Equal(t, err.Error(), cp.GetDetails())
assert.Equal(t, "conflict", cp.GetTitle())
assert.Equal(t, "https://httpstatuses.io/409", cp.GetType())
assert.Equal(t, "some description...", cp.Description)
assert.Equal(t, "some additional info...", cp.AdditionalInfo)
}
}

Expand All @@ -114,7 +189,6 @@ func TestMap_Status_Gin(t *testing.T) {

r.GET("/gin_endpoint2", func(ctx *gin.Context) {
err := errors.New("We have a specific status code error in our endpoint")
// change status code 'StatusBadGateway' to 'StatusUnauthorized' base on handler config
_ = c.AbortWithError(http.StatusBadGateway, err)
})

Expand All @@ -123,20 +197,42 @@ func TestMap_Status_Gin(t *testing.T) {

for _, err := range c.Errors {

var problemErr ProblemDetailErr

// map status code to problem details error
MapStatus(http.StatusBadGateway, func() ProblemDetailErr {
problemErr = New(http.StatusUnauthorized, "unauthorized", err.Error())
return problemErr
return New(http.StatusUnauthorized, "unauthorized", err.Error())
})

_ = ResolveProblemDetails(w, req, err)
p, _ := ResolveProblemDetails(w, req, err)

assert.Equal(t, http.StatusUnauthorized, p.GetStatus())
assert.Equal(t, err.Error(), p.GetDetails())
assert.Equal(t, "unauthorized", p.GetTitle())
assert.Equal(t, "https://httpstatuses.io/401", p.GetType())
}
}

func TestMap_Unhandled_Err_Gin(t *testing.T) {

gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
r := gin.Default()

r.GET("/gin_endpoint3", func(ctx *gin.Context) {
err := errors.New("We have a unhandeled error in our endpoint")
_ = c.Error(err)
})

req, _ := http.NewRequest(http.MethodGet, "/gin_endpoint3", nil)
r.ServeHTTP(w, req)

for _, err := range c.Errors {

p, _ := ResolveProblemDetails(w, req, err)

assert.Equal(t, http.StatusUnauthorized, problemErr.GetStatus())
assert.Equal(t, err.Error(), problemErr.GetDetails())
assert.Equal(t, "unauthorized", problemErr.GetTitle())
assert.Equal(t, "https://httpstatuses.io/401", problemErr.GetType())
assert.Equal(t, http.StatusInternalServerError, p.GetStatus())
assert.Equal(t, err.Error(), p.GetDetails())
assert.Equal(t, "Internal Server Error", p.GetTitle())
assert.Equal(t, "https://httpstatuses.io/500", p.GetType())
}
}

Expand All @@ -147,6 +243,21 @@ func echo_endpoint1(c echo.Context) error {

func echo_endpoint2(c echo.Context) error {
err := errors.New("We have a specific status code error in our endpoint")
// change status code 'StatusBadGateway' to 'StatusUnauthorized' base on handler config
return echo.NewHTTPError(http.StatusBadGateway, err)
}

func echo_endpoint3(c echo.Context) error {
err := errors.New("We have a unhandeled error in our endpoint")
return err
}

func echo_endpoint4(c echo.Context) error {
err := errors.New("We have a custom error with custom problem details error in our endpoint")
return custom_errors.ConflictError{InternalError: err}
}

type CustomProblemDetailTest struct {
ProblemDetailErr
Description string `json:"description,omitempty"`
AdditionalInfo string `json:"additionalInfo,omitempty"`
}
2 changes: 1 addition & 1 deletion samples/cmd/echo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func EchoErrorHandler(error error, c echo.Context) {

// resolve problem details error from response in echo
if !c.Response().Committed {
if err := problem.ResolveProblemDetails(c.Response(), c.Request(), error); err != nil {
if _, err := problem.ResolveProblemDetails(c.Response(), c.Request(), error); err != nil {
log.Error(err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion samples/cmd/gin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func GinErrorHandler() gin.HandlerFunc {
return problem.New(http.StatusUnauthorized, "unauthorized", err.Error())
})

if err := problem.ResolveProblemDetails(c.Writer, c.Request, err); err != nil {
if _, err := problem.ResolveProblemDetails(c.Writer, c.Request, err); err != nil {
log.Error(err)
}
}
Expand Down

0 comments on commit 43cd435

Please sign in to comment.