Skip to content
This repository was archived by the owner on May 19, 2020. It is now read-only.

Commit f5b868c

Browse files
committed
Tests! SQL errors handling correction.
1 parent df716cd commit f5b868c

File tree

4 files changed

+105
-31
lines changed

4 files changed

+105
-31
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import (
1616
)
1717

1818
func main() {
19-
ginErrors.InitValidator()
19+
ginerrors.InitValidator()
2020
r := gin.New()
2121
r.GET("/", func(c*gin.Context) {c.JSON(http.StatusOK,`{"status":"ok"}`)})
22-
r.GET("/err", func(c *gin.Context) { ginErrors.Response(c, errors.New("error")) })
22+
r.GET("/err", func(c *gin.Context) { ginerrors.Response(c, errors.New("error")) })
2323
_ = r.Run(":8080")
2424
}
2525
```

gin.go

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ func MakeResponse(err interface{}) (int, *ErrorObject) {
4040
errObj := &ErrorObject{}
4141
errCode := http.StatusBadRequest
4242

43-
if hasSQLErr, errCode, msg := checkSQLErr(err); hasSQLErr {
44-
errObj.Message = msg
45-
return errCode, errObj
46-
}
47-
4843
switch et := err.(type) {
4944
case []error:
5045
errCode = http.StatusInternalServerError
@@ -53,6 +48,7 @@ func MakeResponse(err interface{}) (int, *ErrorObject) {
5348
msgs = append(msgs, e.Error())
5449
}
5550
errObj.Message = strings.Join(msgs, "; ")
51+
5652
case validator.ValidationErrors:
5753
errCode = http.StatusUnprocessableEntity
5854

@@ -76,37 +72,15 @@ func MakeResponse(err interface{}) (int, *ErrorObject) {
7672
return errCode, errObj
7773
}
7874

79-
func checkSQLErr(err interface{}) (bool, int, string) {
80-
var (
81-
errCode int
82-
hasSQLErr = true
83-
msg = ""
84-
)
85-
switch err {
86-
case sql.ErrNoRows:
87-
errCode = http.StatusNotFound
88-
case sql.ErrTxDone, sql.ErrConnDone:
89-
errCode = http.StatusInternalServerError
90-
default:
91-
hasSQLErr = false
92-
}
93-
94-
if hasSQLErr {
95-
msg = err.(error).Error()
96-
}
97-
98-
return hasSQLErr, errCode, msg
99-
}
100-
10175
func getErrCode(et error) (errCode int) {
10276
switch et.Error() {
10377
case ErrNotFound.Error():
10478
errCode = http.StatusNotFound
10579
case ErrNoMethod.Error():
10680
errCode = http.StatusMethodNotAllowed
107-
case ErrServerError.Error():
81+
case ErrServerError.Error(), sql.ErrConnDone.Error(), sql.ErrTxDone.Error():
10882
errCode = http.StatusInternalServerError
109-
case ErrRecordNotFound.Error():
83+
case ErrRecordNotFound.Error(), sql.ErrNoRows.Error():
11084
errCode = http.StatusNotFound
11185
default:
11286
errCode = http.StatusBadRequest

gin_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package ginerrors
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
10+
"github.com/gin-gonic/gin"
11+
"github.com/pkg/errors"
12+
"github.com/stretchr/testify/assert"
13+
"gopkg.in/go-playground/validator.v9"
14+
)
15+
16+
func TestMakeResponse(t *testing.T) {
17+
type testCase struct {
18+
name string
19+
err interface{}
20+
isErr bool
21+
httpCode int
22+
errObject *ErrorObject
23+
}
24+
25+
cases := []testCase{
26+
{name: "common error", err: errors.New("common err"), isErr: true, httpCode: http.StatusBadRequest, errObject: &ErrorObject{Message: "common err"}},
27+
28+
{name: "validation error", err: makeValidationError(), isErr: true, httpCode: http.StatusUnprocessableEntity, errObject: &ErrorObject{Message: "validation error", Validation: map[string][]string{"String": {"Ошибка валидации для свойства `String` с правилом `%!s(MISSING)`"}}, Debug: ""}},
29+
30+
{name: "mux err no method allowed", err: ErrNoMethod, isErr: true, httpCode: http.StatusMethodNotAllowed, errObject: &ErrorObject{Message: ErrNoMethod.Error()}},
31+
{name: "mux err route not found", err: ErrNotFound, isErr: true, httpCode: http.StatusNotFound, errObject: &ErrorObject{Message: ErrNotFound.Error()}},
32+
33+
{name: "errors slice", err: []error{errors.New("common err 1"), errors.New("common err 2")}, isErr: true, httpCode: http.StatusInternalServerError, errObject: &ErrorObject{Message: "common err 1; common err 2"}},
34+
{name: "map of errors", err: map[string]error{"common_err": errors.New("common err")}, isErr: true, httpCode: http.StatusBadRequest, errObject: &ErrorObject{Message: map[string]string{"common_err": "common err"}}},
35+
36+
{name: "sql error no rows", err: sql.ErrNoRows, isErr: true, httpCode: http.StatusNotFound, errObject: &ErrorObject{Message: sql.ErrNoRows.Error()}},
37+
{name: "sql error conn done", err: sql.ErrConnDone, isErr: true, httpCode: http.StatusInternalServerError, errObject: &ErrorObject{Message: sql.ErrConnDone.Error()}},
38+
{name: "sql error tx done", err: sql.ErrTxDone, isErr: true, httpCode: http.StatusInternalServerError, errObject: &ErrorObject{Message: sql.ErrTxDone.Error()}},
39+
}
40+
for _, testCase := range cases {
41+
t.Run(testCase.name, func(t *testing.T) {
42+
errCode, errObject := MakeResponse(testCase.err)
43+
assert.Equal(t, testCase.errObject, errObject, testCase.name)
44+
assert.Equal(t, testCase.httpCode, errCode, testCase.name)
45+
})
46+
}
47+
}
48+
49+
func setupRouter() *gin.Engine {
50+
InitValidator()
51+
r := gin.New()
52+
53+
r.NoRoute(func(c *gin.Context) { Response(c, ErrNotFound) })
54+
r.GET("/ping", func(c *gin.Context) {
55+
c.String(200, "pong")
56+
})
57+
return r
58+
}
59+
60+
func TestResponse(t *testing.T) {
61+
router := setupRouter()
62+
63+
t.Run("not found", func(t *testing.T) {
64+
w := httptest.NewRecorder()
65+
req, _ := http.NewRequest("GET", "/pong", nil)
66+
router.ServeHTTP(w, req)
67+
68+
assert.Equal(t, http.StatusNotFound, w.Code)
69+
assert.Equal(t, "{\"error\":{\"message\":\"route not found\"}}\n", w.Body.String())
70+
})
71+
}
72+
73+
func makeValidationError() error {
74+
// MyStruct ..
75+
type MyStruct struct {
76+
String string `validate:"is-awesome"`
77+
}
78+
79+
// use a single instance of Validate, it caches struct info
80+
var validate *validator.Validate
81+
82+
validate = validator.New()
83+
_ = validate.RegisterValidation("is-awesome", ValidateMyVal)
84+
85+
s := MyStruct{String: "awesome"}
86+
87+
err := validate.Struct(s)
88+
if err != nil {
89+
fmt.Printf("Err(s):\n%+v\n", err)
90+
}
91+
92+
s.String = "not awesome"
93+
return validate.Struct(s)
94+
}
95+
96+
// ValidateMyVal implements validator.Func
97+
func ValidateMyVal(fl validator.FieldLevel) bool {
98+
return fl.Field().String() == "awesome"
99+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/gin-gonic/gin v1.4.1-0.20190922073534-0b96dd8ae554
77
github.com/kr/pretty v0.1.0 // indirect
88
github.com/pkg/errors v0.8.1
9+
github.com/stretchr/testify v1.4.0
910
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 // indirect
1011
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
1112
gopkg.in/go-playground/validator.v9 v9.29.1

0 commit comments

Comments
 (0)