@@ -4,7 +4,11 @@ import (
4
4
"encoding/json"
5
5
"errors"
6
6
"fmt"
7
+ "net/http"
8
+ "net/http/httptest"
7
9
"testing"
10
+
11
+ "github.com/stretchr/testify/assert"
8
12
)
9
13
10
14
func TestAppendFieldError (t * testing.T ) {
@@ -51,3 +55,263 @@ func TestAppendFieldError(t *testing.T) {
51
55
})
52
56
}
53
57
}
58
+
59
+ func TestNewAPIError (t * testing.T ) {
60
+ tests := []struct {
61
+ name string
62
+ err error
63
+ newFunction func (err error ) * APIError
64
+ want struct {
65
+ statusCode int
66
+ message string
67
+ }
68
+ }{
69
+ {
70
+ name : "BadRequestError" ,
71
+ err : errors .New ("bad request" ),
72
+ newFunction : NewBadRequestError ,
73
+ want : struct {
74
+ statusCode int
75
+ message string
76
+ }{
77
+ statusCode : http .StatusBadRequest ,
78
+ message : "bad request" ,
79
+ },
80
+ },
81
+ {
82
+ name : "UnauthorizedError" ,
83
+ err : errors .New ("auth failed" ),
84
+ newFunction : NewUnauthorizedError ,
85
+ want : struct {
86
+ statusCode int
87
+ message string
88
+ }{
89
+ statusCode : http .StatusUnauthorized ,
90
+ message : "Unauthorized" ,
91
+ },
92
+ },
93
+ {
94
+ name : "InternalServerError" ,
95
+ newFunction : NewInternalServerError ,
96
+ err : errors .New ("internal server error" ),
97
+ want : struct {
98
+ statusCode int
99
+ message string
100
+ }{
101
+ statusCode : http .StatusInternalServerError ,
102
+ message : "internal server error" ,
103
+ },
104
+ },
105
+ }
106
+
107
+ for _ , tt := range tests {
108
+ t .Run (tt .name , func (t * testing.T ) {
109
+ apiErr := tt .newFunction (tt .err )
110
+
111
+ assert .Equal (t , tt .want .statusCode , apiErr .StatusCode , "StatusCode should match" )
112
+ assert .Equal (t , tt .want .message , apiErr .Message , "Message should match" )
113
+ assert .Equal (t , tt .err , apiErr .err , "Original error should be stored" )
114
+ })
115
+ }
116
+ }
117
+
118
+ func TestAPIError_Error (t * testing.T ) {
119
+ tests := []struct {
120
+ name string
121
+ apiError * APIError
122
+ expected string
123
+ }{
124
+ {
125
+ name : "nil error" ,
126
+ apiError : nil ,
127
+ expected : "" ,
128
+ },
129
+ {
130
+ name : "error with message, no sub-errors" ,
131
+ apiError : & APIError {
132
+ Message : "main error message" ,
133
+ Errors : []* APIError {},
134
+ },
135
+ expected : "main error message" ,
136
+ },
137
+ {
138
+ name : "error with message and one sub-error" ,
139
+ apiError : & APIError {
140
+ Message : "main error message" ,
141
+ Errors : []* APIError {
142
+ {Message : "sub-error 1" },
143
+ },
144
+ },
145
+ expected : "main error message: sub-error 1" ,
146
+ },
147
+ {
148
+ name : "error with message and multiple sub-errors" ,
149
+ apiError : & APIError {
150
+ Message : "main error message" ,
151
+ Errors : []* APIError {
152
+ {Message : "sub-error 1" },
153
+ {Message : "sub-error 2" },
154
+ {Message : "sub-error 3" },
155
+ },
156
+ },
157
+ expected : "main error message: sub-error 1; sub-error 2; sub-error 3" ,
158
+ },
159
+ }
160
+
161
+ for _ , tt := range tests {
162
+ t .Run (tt .name , func (t * testing.T ) {
163
+ got := tt .apiError .Error ()
164
+ assert .Equal (t , tt .expected , got , "Error() should return the expected string" )
165
+ })
166
+ }
167
+ }
168
+
169
+ func TestAPIError_ErrorOrNil (t * testing.T ) {
170
+ tests := []struct {
171
+ name string
172
+ err * APIError
173
+ wantNil bool
174
+ }{
175
+ {
176
+ name : "nil error" ,
177
+ err : (* APIError )(nil ),
178
+ wantNil : true ,
179
+ },
180
+ {
181
+ name : "error without child errors" ,
182
+ err : & APIError {
183
+ StatusCode : http .StatusBadRequest ,
184
+ Message : "bad request" ,
185
+ Errors : nil ,
186
+ },
187
+ wantNil : true ,
188
+ },
189
+ {
190
+ name : "error with empty errors slice" ,
191
+ err : & APIError {
192
+ StatusCode : http .StatusBadRequest ,
193
+ Message : "bad request" ,
194
+ Errors : []* APIError {},
195
+ },
196
+ wantNil : true ,
197
+ },
198
+ {
199
+ name : "error with child errors" ,
200
+ err : & APIError {
201
+ StatusCode : http .StatusBadRequest ,
202
+ Message : "validation failed" ,
203
+ Errors : []* APIError {
204
+ {
205
+ Message : "field1: invalid value" ,
206
+ Field : "field1" ,
207
+ },
208
+ },
209
+ },
210
+ wantNil : false ,
211
+ },
212
+ }
213
+
214
+ for _ , tt := range tests {
215
+ t .Run (tt .name , func (t * testing.T ) {
216
+ result := tt .err .ErrorOrNil ()
217
+
218
+ if tt .wantNil {
219
+ assert .Nil (t , result , "ErrorOrNil() should return nil" )
220
+ } else {
221
+ assert .NotNil (t , result , "ErrorOrNil() should not return nil" )
222
+ assert .Equal (t , tt .err , result , "ErrorOrNil() should return the error itself" )
223
+ }
224
+ })
225
+ }
226
+ }
227
+
228
+ func TestAPIError_JSON (t * testing.T ) {
229
+ tests := []struct {
230
+ name string
231
+ apiErr * APIError
232
+ wantCode int
233
+ wantJSON map [string ]any
234
+ }{
235
+ {
236
+ name : "simple error" ,
237
+ apiErr : & APIError {
238
+ StatusCode : http .StatusInternalServerError ,
239
+ Message : "invalid request" ,
240
+ },
241
+ wantCode : http .StatusInternalServerError ,
242
+ wantJSON : map [string ]any {
243
+ "status_code" : float64 (http .StatusInternalServerError ),
244
+ "message" : "invalid request" ,
245
+ },
246
+ },
247
+ {
248
+ name : "field error" ,
249
+ apiErr : & APIError {
250
+ StatusCode : http .StatusBadRequest ,
251
+ Message : "validation error" ,
252
+ Field : "username" ,
253
+ },
254
+ wantCode : http .StatusBadRequest ,
255
+ wantJSON : map [string ]any {
256
+ "status_code" : float64 (http .StatusBadRequest ),
257
+ "message" : "validation error" ,
258
+ "field" : "username" ,
259
+ },
260
+ },
261
+ {
262
+ name : "error with nested errors" ,
263
+ apiErr : & APIError {
264
+ StatusCode : http .StatusBadRequest ,
265
+ Message : "multiple validation errors" ,
266
+ Errors : []* APIError {
267
+ {
268
+ Message : "field1 is required" ,
269
+ Field : "field1" ,
270
+ },
271
+ {
272
+ Message : "field2 must be a number" ,
273
+ Field : "field2" ,
274
+ },
275
+ },
276
+ },
277
+ wantCode : http .StatusBadRequest ,
278
+ wantJSON : map [string ]any {
279
+ "status_code" : float64 (http .StatusBadRequest ),
280
+ "message" : "multiple validation errors" ,
281
+ "errors" : []any {
282
+ map [string ]any {
283
+ "message" : "field1 is required" ,
284
+ "field" : "field1" ,
285
+ },
286
+ map [string ]any {
287
+ "message" : "field2 must be a number" ,
288
+ "field" : "field2" ,
289
+ },
290
+ },
291
+ },
292
+ },
293
+ }
294
+
295
+ for _ , tt := range tests {
296
+ t .Run (tt .name , func (t * testing.T ) {
297
+ // Create a mock HTTP response recorder
298
+ rec := httptest .NewRecorder ()
299
+
300
+ // Call the JSON method
301
+ tt .apiErr .JSON (rec )
302
+
303
+ // Check status code
304
+ assert .Equal (t , tt .wantCode , rec .Code , "Status code should match" )
305
+
306
+ // Check content type header
307
+ contentType := rec .Header ().Get ("Content-Type" )
308
+ assert .Equal (t , "application/json" , contentType , "Content-Type header should be application/json" )
309
+
310
+ // Parse and check the JSON response
311
+ var gotJSON map [string ]any
312
+ err := json .Unmarshal (rec .Body .Bytes (), & gotJSON )
313
+ assert .NoError (t , err , "Should be able to parse the JSON response" )
314
+ assert .Equal (t , tt .wantJSON , gotJSON , "JSON response should match expected structure" )
315
+ })
316
+ }
317
+ }
0 commit comments