@@ -202,4 +202,198 @@ describe('PolyVClient', () => {
202202 ) ;
203203 } ) ;
204204 } ) ;
205+
206+ describe ( 'error handling' , ( ) => {
207+ it ( '[P1] should handle request cancellation' , ( ) => {
208+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
209+ const mockInstance = mockAxiosCreate ( ) ;
210+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
211+ const errorHandler = responseInterceptorCalls [ 0 ] [ 1 ] ;
212+
213+ const cancelError = { __CANCEL__ : true , message : 'Request cancelled' } ;
214+ vi . mocked ( axios . isCancel ) . mockReturnValueOnce ( true ) ;
215+
216+ // Error handler throws synchronously
217+ expect ( ( ) => errorHandler ( cancelError ) ) . toThrow ( 'Request cancelled' ) ;
218+ } ) ;
219+
220+ it ( '[P1] should handle axios error with response' , ( ) => {
221+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
222+ const mockInstance = mockAxiosCreate ( ) ;
223+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
224+ const errorHandler = responseInterceptorCalls [ 0 ] [ 1 ] ;
225+
226+ const axiosError = {
227+ response : {
228+ status : 400 ,
229+ data : { message : 'Bad request' , error : { code : 'INVALID_PARAM' } } ,
230+ } ,
231+ message : 'Request failed' ,
232+ } ;
233+ vi . mocked ( axios . isAxiosError ) . mockReturnValueOnce ( true ) ;
234+ vi . mocked ( axios . isCancel ) . mockReturnValueOnce ( false ) ;
235+
236+ expect ( ( ) => errorHandler ( axiosError ) ) . toThrow ( 'Bad request' ) ;
237+ } ) ;
238+
239+ it ( '[P1] should handle axios error with timeout' , ( ) => {
240+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
241+ const mockInstance = mockAxiosCreate ( ) ;
242+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
243+ const errorHandler = responseInterceptorCalls [ 0 ] [ 1 ] ;
244+
245+ const axiosError = {
246+ code : 'ECONNABORTED' ,
247+ message : 'timeout of 30000ms exceeded' ,
248+ request : { } ,
249+ } ;
250+ vi . mocked ( axios . isAxiosError ) . mockReturnValueOnce ( true ) ;
251+ vi . mocked ( axios . isCancel ) . mockReturnValueOnce ( false ) ;
252+
253+ expect ( ( ) => errorHandler ( axiosError ) ) . toThrow ( 'Request timeout' ) ;
254+ } ) ;
255+
256+ it ( '[P1] should handle axios error with no response' , ( ) => {
257+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
258+ const mockInstance = mockAxiosCreate ( ) ;
259+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
260+ const errorHandler = responseInterceptorCalls [ 0 ] [ 1 ] ;
261+
262+ const axiosError = {
263+ message : 'Network error' ,
264+ request : { } ,
265+ } ;
266+ vi . mocked ( axios . isAxiosError ) . mockReturnValueOnce ( true ) ;
267+ vi . mocked ( axios . isCancel ) . mockReturnValueOnce ( false ) ;
268+
269+ expect ( ( ) => errorHandler ( axiosError ) ) . toThrow ( 'No response from server' ) ;
270+ } ) ;
271+
272+ it ( '[P1] should handle non-axios errors' , ( ) => {
273+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
274+ const mockInstance = mockAxiosCreate ( ) ;
275+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
276+ const errorHandler = responseInterceptorCalls [ 0 ] [ 1 ] ;
277+
278+ const genericError = new Error ( 'Unknown error' ) ;
279+ vi . mocked ( axios . isAxiosError ) . mockReturnValueOnce ( false ) ;
280+ vi . mocked ( axios . isCancel ) . mockReturnValueOnce ( false ) ;
281+
282+ expect ( ( ) => errorHandler ( genericError ) ) . toThrow ( 'Unknown error' ) ;
283+ } ) ;
284+
285+ it ( '[P1] should handle non-Error objects' , ( ) => {
286+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
287+ const mockInstance = mockAxiosCreate ( ) ;
288+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
289+ const errorHandler = responseInterceptorCalls [ 0 ] [ 1 ] ;
290+
291+ vi . mocked ( axios . isAxiosError ) . mockReturnValueOnce ( false ) ;
292+ vi . mocked ( axios . isCancel ) . mockReturnValueOnce ( false ) ;
293+
294+ expect ( ( ) => errorHandler ( 'string error' ) ) . toThrow ( 'Unknown error' ) ;
295+ } ) ;
296+ } ) ;
297+
298+ describe ( 'request interceptor' , ( ) => {
299+ it ( '[P1] should skip auth when X-Skip-Auth header is set' , async ( ) => {
300+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
301+ const mockInstance = mockAxiosCreate ( ) ;
302+ const requestInterceptorCalls = vi . mocked ( mockInstance . interceptors . request . use ) . mock . calls ;
303+ const requestHandler = requestInterceptorCalls [ 0 ] [ 0 ] ;
304+
305+ const config = {
306+ headers : { 'X-Skip-Auth' : true } ,
307+ params : { customParam : 'value' } ,
308+ } ;
309+
310+ const result = requestHandler ( config ) ;
311+
312+ expect ( result . headers [ 'X-Skip-Auth' ] ) . toBeUndefined ( ) ;
313+ } ) ;
314+
315+ it ( '[P0] should inject signature params' , async ( ) => {
316+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
317+ const mockInstance = mockAxiosCreate ( ) ;
318+ const requestInterceptorCalls = vi . mocked ( mockInstance . interceptors . request . use ) . mock . calls ;
319+ const requestHandler = requestInterceptorCalls [ 0 ] [ 0 ] ;
320+
321+ const config = {
322+ headers : { } ,
323+ params : { customParam : 'value' } ,
324+ } ;
325+
326+ const result = requestHandler ( config ) ;
327+
328+ expect ( result . params . appId ) . toBe ( 'test-app-id' ) ;
329+ expect ( result . params . timestamp ) . toBeDefined ( ) ;
330+ expect ( result . params . sign ) . toBeDefined ( ) ;
331+ } ) ;
332+
333+ it ( '[P1] should handle request interceptor error' , async ( ) => {
334+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
335+ const mockInstance = mockAxiosCreate ( ) ;
336+ const requestInterceptorCalls = vi . mocked ( mockInstance . interceptors . request . use ) . mock . calls ;
337+ const errorHandler = requestInterceptorCalls [ 0 ] [ 1 ] ;
338+
339+ const error = new Error ( 'Request setup failed' ) ;
340+
341+ await expect ( errorHandler ( error ) ) . rejects . toThrow ( 'Request setup failed' ) ;
342+ } ) ;
343+ } ) ;
344+
345+ describe ( 'response interceptor success' , ( ) => {
346+ it ( '[P0] should extract data from successful response' , async ( ) => {
347+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
348+ const mockInstance = mockAxiosCreate ( ) ;
349+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
350+ const successHandler = responseInterceptorCalls [ 0 ] [ 0 ] ;
351+
352+ const response = {
353+ data : {
354+ code : 200 ,
355+ data : { id : '123' , name : 'test' } ,
356+ } ,
357+ } ;
358+
359+ const result = successHandler ( response ) ;
360+
361+ expect ( result ) . toEqual ( { id : '123' , name : 'test' } ) ;
362+ } ) ;
363+
364+ it ( '[P1] should throw API error for non-200 code' , async ( ) => {
365+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
366+ const mockInstance = mockAxiosCreate ( ) ;
367+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
368+ const successHandler = responseInterceptorCalls [ 0 ] [ 0 ] ;
369+
370+ const response = {
371+ data : {
372+ code : 400 ,
373+ message : 'Bad request' ,
374+ error : { code : 'INVALID_PARAM' , desc : 'Invalid parameter' } ,
375+ } ,
376+ status : 400 ,
377+ } ;
378+
379+ expect ( ( ) => successHandler ( response ) ) . toThrow ( 'Invalid parameter' ) ;
380+ } ) ;
381+
382+ it ( '[P1] should use message when error.desc is not available' , async ( ) => {
383+ const mockAxiosCreate = vi . mocked ( axios . create ) ;
384+ const mockInstance = mockAxiosCreate ( ) ;
385+ const responseInterceptorCalls = vi . mocked ( mockInstance . interceptors . response . use ) . mock . calls ;
386+ const successHandler = responseInterceptorCalls [ 0 ] [ 0 ] ;
387+
388+ const response = {
389+ data : {
390+ code : 500 ,
391+ message : 'Internal server error' ,
392+ } ,
393+ status : 500 ,
394+ } ;
395+
396+ expect ( ( ) => successHandler ( response ) ) . toThrow ( 'Internal server error' ) ;
397+ } ) ;
398+ } ) ;
205399} ) ;
0 commit comments