-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy patherror_middleware_test.go
154 lines (143 loc) · 5.42 KB
/
error_middleware_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package handlers
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"testing"
pkgerrors "github.com/pkg/errors"
"github.com/sirupsen/logrus"
pkgtest "github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
errorutils "github.com/Scalingo/go-utils/errors/v2"
"github.com/Scalingo/go-utils/logger"
"github.com/Scalingo/go-utils/security"
)
func TestErrorMiddlware(t *testing.T) {
tests := map[string]struct {
contentType string
handlerFunc HandlerFunc
assertLogs func(*testing.T, *pkgtest.Hook)
expectedStatusCode int
expectedBody string
}{
"it should set the status code to 500 if there is none": {
contentType: "application/json",
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
log := logger.Get(r.Context()).WithField("field", "value")
return errorutils.Wrapf(logger.ToCtx(context.Background(), log), errors.New("error"), "wrapping")
},
assertLogs: func(t *testing.T, hook *pkgtest.Hook) {
require.Equal(t, 1, len(hook.Entries))
assert.Equal(t, "value", hook.Entries[0].Data["field"])
},
expectedStatusCode: 500,
expectedBody: "{\"error\":\"wrapping: error\"}\n",
},
"it should set the status code to 422 if this is a ValidationErrors": {
contentType: "application/json",
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
err := (&errorutils.ValidationErrors{
Errors: map[string][]string{
"test": {"biniou"},
},
})
return pkgerrors.Wrap(err, "biniou")
},
expectedStatusCode: 422,
expectedBody: "{\"errors\":{\"test\":[\"biniou\"]}}\n",
},
"it should set the status code to 400 if this is a BadRequestError": {
contentType: "application/json",
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
err := (&BadRequestError{
Errors: map[string][]string{
"per_page": {"must be greater than 0"},
},
})
return pkgerrors.Wrap(err, "biniou")
},
expectedStatusCode: 400,
expectedBody: "{\"error\":\"biniou: * per_page → must be greater than 0\"}\n",
},
"it should set the status code to 401 if this is an error due to an invalidity token": {
contentType: "application/json",
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
err := security.ErrFutureTimestamp
return pkgerrors.Wrap(err, "biniou")
},
expectedStatusCode: 401,
expectedBody: "{\"error\":\"biniou: invalid timestamp in the future\"}\n",
},
"it should detect any Content-Type ending with +json as JSON": {
contentType: "application/vnd.docker.plugins.v1.1+json",
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
log := logger.Get(r.Context()).WithField("field", "value")
return errorutils.Wrapf(logger.ToCtx(context.Background(), log), errors.New("error"), "wrapping")
},
assertLogs: func(t *testing.T, hook *pkgtest.Hook) {
require.Equal(t, 1, len(hook.Entries))
assert.Equal(t, "value", hook.Entries[0].Data["field"])
},
expectedStatusCode: 500,
expectedBody: "{\"error\":\"wrapping: error\"}\n",
},
"it should add the Content-Type plain text if none is specified": {
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
log := logger.Get(r.Context()).WithField("field", "value")
return errorutils.Wrapf(logger.ToCtx(context.Background(), log), errors.New("error"), "wrapping")
},
assertLogs: func(t *testing.T, hook *pkgtest.Hook) {
require.Equal(t, 1, len(hook.Entries))
assert.Equal(t, "value", hook.Entries[0].Data["field"])
},
expectedStatusCode: 500,
expectedBody: "wrapping: error\n",
},
"it should not write anything in the body if it has already been written": {
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
w.WriteHeader(500)
// My handler writes something in the body but returns an error
fmt.Fprintln(w, "biniou")
return errors.New("my error")
},
expectedStatusCode: 500,
expectedBody: "biniou\n",
},
"it should log the error for all 5xx status code": {
handlerFunc: func(w http.ResponseWriter, r *http.Request, vars map[string]string) error {
w.WriteHeader(502)
log := logger.Get(r.Context()).WithField("field", "value")
return errorutils.Wrapf(logger.ToCtx(context.Background(), log), errors.New("error"), "wrapping")
},
assertLogs: func(t *testing.T, hook *pkgtest.Hook) {
require.Equal(t, 1, len(hook.Entries))
assert.Equal(t, "value", hook.Entries[0].Data["field"])
},
expectedStatusCode: 502,
},
}
for msg, test := range tests {
t.Run(msg, func(t *testing.T) {
handler := ErrorMiddleware(test.handlerFunc)
log, hook := pkgtest.NewNullLogger()
log.SetLevel(logrus.DebugLevel)
defer hook.Reset()
ctx := logger.ToCtx(context.Background(), log)
w := httptest.NewRecorder()
w.Header().Set("Content-Type", test.contentType)
r := httptest.NewRequest("GET", "/", nil).WithContext(ctx)
err := handler(w, r, map[string]string{})
require.Error(t, err)
if test.assertLogs != nil {
test.assertLogs(t, hook)
}
if test.expectedBody != "" {
assert.Equal(t, test.expectedBody, w.Body.String())
}
assert.Equal(t, test.expectedStatusCode, w.Code)
})
}
}