@@ -4,7 +4,11 @@ import (
44 "encoding/json"
55 "errors"
66 "fmt"
7+ "net/http"
8+ "net/http/httptest"
79 "testing"
10+
11+ "github.com/stretchr/testify/assert"
812)
913
1014func TestAppendFieldError (t * testing.T ) {
@@ -51,3 +55,263 @@ func TestAppendFieldError(t *testing.T) {
5155 })
5256 }
5357}
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