1+ import { beforeEach , describe , expect , it , vi } from 'vitest' ;
2+ import {
3+ APIError ,
4+ APIConnectionError ,
5+ APIConnectionTimeoutError ,
6+ AuthenticationError ,
7+ BadRequestError ,
8+ ConflictError ,
9+ InternalServerError ,
10+ NotFoundError ,
11+ PermissionDeniedError ,
12+ RateLimitError ,
13+ UnprocessableEntityError ,
14+ } from './errors' ;
15+
16+ describe ( 'Error Classes' , ( ) => {
17+ describe ( 'APIError' , ( ) => {
18+ it ( 'should create APIError with all parameters' , ( ) => {
19+ const status = 400 ;
20+ const error = { message : 'Test error' , code : 'test_error' } ;
21+ const message = 'API Error occurred' ;
22+ const headers = { 'lb-request-id' : 'req-123' } ;
23+
24+ const apiError = new APIError ( status , error , message , headers ) ;
25+
26+ expect ( apiError . status ) . toBe ( 400 ) ;
27+ expect ( apiError . error ) . toEqual ( error ) ;
28+ expect ( apiError . message ) . toBe ( '400 API Error occurred' ) ;
29+ expect ( apiError . headers ) . toEqual ( headers ) ;
30+ expect ( apiError . request_id ) . toBe ( 'req-123' ) ;
31+ expect ( apiError . code ) . toBe ( 'test_error' ) ;
32+ } ) ;
33+
34+ it ( 'should handle error with nested message object' , ( ) => {
35+ const error = { message : { details : 'Detailed error info' } } ;
36+ const apiError = new APIError ( 400 , error , 'Test' , { } ) ;
37+
38+ expect ( apiError . message ) . toBe ( '400 {"details":"Detailed error info"}' ) ;
39+ } ) ;
40+
41+ it ( 'should handle error without specific message' , ( ) => {
42+ const error = { code : 'generic_error' } ;
43+ const apiError = new APIError ( 500 , error , undefined , { } ) ;
44+
45+ expect ( apiError . message ) . toBe ( '500 {"code":"generic_error"}' ) ;
46+ } ) ;
47+
48+ it ( 'should handle status only' , ( ) => {
49+ const apiError = new APIError ( 404 , undefined , undefined , { } ) ;
50+
51+ expect ( apiError . message ) . toBe ( '404 status code (no body)' ) ;
52+ } ) ;
53+
54+ it ( 'should handle message only' , ( ) => {
55+ const apiError = new APIError ( undefined , undefined , 'Custom message' , { } ) ;
56+
57+ expect ( apiError . message ) . toBe ( 'Custom message' ) ;
58+ } ) ;
59+
60+ it ( 'should handle no parameters' , ( ) => {
61+ const apiError = new APIError ( undefined , undefined , undefined , { } ) ;
62+
63+ expect ( apiError . message ) . toBe ( '(no status code or body)' ) ;
64+ } ) ;
65+
66+ describe ( 'APIError.generate' , ( ) => {
67+ it ( 'should generate BadRequestError for 400 status' , ( ) => {
68+ const error = APIError . generate (
69+ 400 ,
70+ { error : { message : 'Bad request' } } ,
71+ 'Bad Request' ,
72+ { }
73+ ) ;
74+
75+ expect ( error ) . toBeInstanceOf ( BadRequestError ) ;
76+ expect ( error . status ) . toBe ( 400 ) ;
77+ } ) ;
78+
79+ it ( 'should generate AuthenticationError for 401 status' , ( ) => {
80+ const error = APIError . generate (
81+ 401 ,
82+ { error : { message : 'Unauthorized' } } ,
83+ 'Unauthorized' ,
84+ { }
85+ ) ;
86+
87+ expect ( error ) . toBeInstanceOf ( AuthenticationError ) ;
88+ expect ( error . status ) . toBe ( 401 ) ;
89+ } ) ;
90+
91+ it ( 'should generate PermissionDeniedError for 403 status' , ( ) => {
92+ const error = APIError . generate (
93+ 403 ,
94+ { error : { message : 'Forbidden' } } ,
95+ 'Forbidden' ,
96+ { }
97+ ) ;
98+
99+ expect ( error ) . toBeInstanceOf ( PermissionDeniedError ) ;
100+ expect ( error . status ) . toBe ( 403 ) ;
101+ } ) ;
102+
103+ it ( 'should generate NotFoundError for 404 status' , ( ) => {
104+ const error = APIError . generate (
105+ 404 ,
106+ { error : { message : 'Not found' } } ,
107+ 'Not Found' ,
108+ { }
109+ ) ;
110+
111+ expect ( error ) . toBeInstanceOf ( NotFoundError ) ;
112+ expect ( error . status ) . toBe ( 404 ) ;
113+ } ) ;
114+
115+ it ( 'should generate ConflictError for 409 status' , ( ) => {
116+ const error = APIError . generate (
117+ 409 ,
118+ { error : { message : 'Conflict' } } ,
119+ 'Conflict' ,
120+ { }
121+ ) ;
122+
123+ expect ( error ) . toBeInstanceOf ( ConflictError ) ;
124+ expect ( error . status ) . toBe ( 409 ) ;
125+ } ) ;
126+
127+ it ( 'should generate UnprocessableEntityError for 422 status' , ( ) => {
128+ const error = APIError . generate (
129+ 422 ,
130+ { error : { message : 'Validation failed' } } ,
131+ 'Unprocessable Entity' ,
132+ { }
133+ ) ;
134+
135+ expect ( error ) . toBeInstanceOf ( UnprocessableEntityError ) ;
136+ expect ( error . status ) . toBe ( 422 ) ;
137+ } ) ;
138+
139+ it ( 'should generate RateLimitError for 429 status' , ( ) => {
140+ const error = APIError . generate (
141+ 429 ,
142+ { error : { message : 'Rate limit exceeded' } } ,
143+ 'Too Many Requests' ,
144+ { }
145+ ) ;
146+
147+ expect ( error ) . toBeInstanceOf ( RateLimitError ) ;
148+ expect ( error . status ) . toBe ( 429 ) ;
149+ } ) ;
150+
151+ it ( 'should generate InternalServerError for 500+ status' , ( ) => {
152+ const error = APIError . generate (
153+ 500 ,
154+ { error : { message : 'Internal error' } } ,
155+ 'Internal Server Error' ,
156+ { }
157+ ) ;
158+
159+ expect ( error ) . toBeInstanceOf ( InternalServerError ) ;
160+ expect ( error . status ) . toBe ( 500 ) ;
161+ } ) ;
162+
163+ it ( 'should generate generic APIError for unknown status codes' , ( ) => {
164+ const error = APIError . generate (
165+ 418 ,
166+ { error : { message : "I'm a teapot" } } ,
167+ "I'm a teapot" ,
168+ { }
169+ ) ;
170+
171+ expect ( error ) . toBeInstanceOf ( APIError ) ;
172+ expect ( error . status ) . toBe ( 418 ) ;
173+ } ) ;
174+
175+ it ( 'should generate APIConnectionError when status is undefined' , ( ) => {
176+ const error = APIError . generate (
177+ undefined ,
178+ new Error ( 'Network error' ) ,
179+ undefined ,
180+ { }
181+ ) ;
182+
183+ expect ( error ) . toBeInstanceOf ( APIConnectionError ) ;
184+ } ) ;
185+ } ) ;
186+ } ) ;
187+
188+ describe ( 'APIConnectionError' , ( ) => {
189+ it ( 'should create with default message' , ( ) => {
190+ const error = new APIConnectionError ( { } ) ;
191+
192+ expect ( error . status ) . toBeUndefined ( ) ;
193+ expect ( error . message ) . toBe ( 'Connection error.' ) ;
194+ expect ( error ) . toBeInstanceOf ( APIError ) ;
195+ } ) ;
196+
197+ it ( 'should create with custom message' , ( ) => {
198+ const error = new APIConnectionError ( { message : 'Custom connection error' } ) ;
199+
200+ expect ( error . message ) . toBe ( 'Custom connection error' ) ;
201+ } ) ;
202+
203+ it ( 'should create with cause' , ( ) => {
204+ const cause = new Error ( 'Network failure' ) ;
205+ const error = new APIConnectionError ( { cause} ) ;
206+
207+ expect ( ( error as any ) . cause ) . toBe ( cause ) ;
208+ } ) ;
209+
210+ it ( 'should create with both message and cause' , ( ) => {
211+ const cause = new Error ( 'Network failure' ) ;
212+ const error = new APIConnectionError ( {
213+ message : 'Connection failed' ,
214+ cause,
215+ } ) ;
216+
217+ expect ( error . message ) . toBe ( 'Connection failed' ) ;
218+ expect ( ( error as any ) . cause ) . toBe ( cause ) ;
219+ } ) ;
220+ } ) ;
221+
222+ describe ( 'APIConnectionTimeoutError' , ( ) => {
223+ it ( 'should create with default timeout message' , ( ) => {
224+ const error = new APIConnectionTimeoutError ( ) ;
225+
226+ expect ( error . message ) . toBe ( 'Request timed out.' ) ;
227+ expect ( error ) . toBeInstanceOf ( APIConnectionError ) ;
228+ } ) ;
229+
230+ it ( 'should create with custom timeout message' , ( ) => {
231+ const error = new APIConnectionTimeoutError ( {
232+ message : 'Custom timeout error' ,
233+ } ) ;
234+
235+ expect ( error . message ) . toBe ( 'Custom timeout error' ) ;
236+ } ) ;
237+ } ) ;
238+
239+ describe ( 'Specific Error Classes' , ( ) => {
240+ it ( 'BadRequestError should have correct status' , ( ) => {
241+ const error = new BadRequestError ( 400 , { } , 'Bad request' , { } ) ;
242+ expect ( error . status ) . toBe ( 400 ) ;
243+ } ) ;
244+
245+ it ( 'AuthenticationError should have correct status' , ( ) => {
246+ const error = new AuthenticationError ( 401 , { } , 'Unauthorized' , { } ) ;
247+ expect ( error . status ) . toBe ( 401 ) ;
248+ } ) ;
249+
250+ it ( 'PermissionDeniedError should have correct status' , ( ) => {
251+ const error = new PermissionDeniedError ( 403 , { } , 'Forbidden' , { } ) ;
252+ expect ( error . status ) . toBe ( 403 ) ;
253+ } ) ;
254+
255+ it ( 'NotFoundError should have correct status' , ( ) => {
256+ const error = new NotFoundError ( 404 , { } , 'Not found' , { } ) ;
257+ expect ( error . status ) . toBe ( 404 ) ;
258+ } ) ;
259+
260+ it ( 'ConflictError should have correct status' , ( ) => {
261+ const error = new ConflictError ( 409 , { } , 'Conflict' , { } ) ;
262+ expect ( error . status ) . toBe ( 409 ) ;
263+ } ) ;
264+
265+ it ( 'UnprocessableEntityError should have correct status' , ( ) => {
266+ const error = new UnprocessableEntityError ( 422 , { } , 'Unprocessable' , { } ) ;
267+ expect ( error . status ) . toBe ( 422 ) ;
268+ } ) ;
269+
270+ it ( 'RateLimitError should have correct status' , ( ) => {
271+ const error = new RateLimitError ( 429 , { } , 'Rate limited' , { } ) ;
272+ expect ( error . status ) . toBe ( 429 ) ;
273+ } ) ;
274+
275+ it ( 'InternalServerError should inherit from APIError' , ( ) => {
276+ const error = new InternalServerError ( 500 , { } , 'Internal error' , { } ) ;
277+ expect ( error ) . toBeInstanceOf ( APIError ) ;
278+ expect ( error . status ) . toBe ( 500 ) ;
279+ } ) ;
280+ } ) ;
281+ } ) ;
0 commit comments