1+ import { describe , it , expect , jest } from '@jest/globals' ;
2+ import {
3+ SessionError ,
4+ ApiError ,
5+ FileSystemError ,
6+ RateLimitError ,
7+ ConversationLockedError ,
8+ SessionNotFoundError ,
9+ } from '../errors/index.js' ;
10+ import { ErrorClassifier } from '../utils/ErrorClassifier.js' ;
11+
12+ describe ( 'Custom Error Classes' , ( ) => {
13+ describe ( 'SessionError' , ( ) => {
14+ it ( 'should create a session error with proper properties' , ( ) => {
15+ const error = new SessionError ( 'Test message' , 'TEST_CODE' , 'session-123' ) ;
16+
17+ expect ( error ) . toBeInstanceOf ( Error ) ;
18+ expect ( error ) . toBeInstanceOf ( SessionError ) ;
19+ expect ( error . name ) . toBe ( 'SessionError' ) ;
20+ expect ( error . message ) . toBe ( 'Test message' ) ;
21+ expect ( error . code ) . toBe ( 'TEST_CODE' ) ;
22+ expect ( error . sessionId ) . toBe ( 'session-123' ) ;
23+ } ) ;
24+ } ) ;
25+
26+ describe ( 'ApiError' , ( ) => {
27+ it ( 'should create an API error with proper properties' , ( ) => {
28+ const error = new ApiError ( 'API failed' , 'API_ERROR' , 'gemini' , 500 ) ;
29+
30+ expect ( error ) . toBeInstanceOf ( Error ) ;
31+ expect ( error ) . toBeInstanceOf ( ApiError ) ;
32+ expect ( error . name ) . toBe ( 'ApiError' ) ;
33+ expect ( error . message ) . toBe ( 'API failed' ) ;
34+ expect ( error . code ) . toBe ( 'API_ERROR' ) ;
35+ expect ( error . service ) . toBe ( 'gemini' ) ;
36+ expect ( error . statusCode ) . toBe ( 500 ) ;
37+ } ) ;
38+ } ) ;
39+
40+ describe ( 'FileSystemError' , ( ) => {
41+ it ( 'should create a filesystem error with proper properties' , ( ) => {
42+ const error = new FileSystemError ( 'File not found' , 'ENOENT' , '/path/to/file' , 'read' ) ;
43+
44+ expect ( error ) . toBeInstanceOf ( Error ) ;
45+ expect ( error ) . toBeInstanceOf ( FileSystemError ) ;
46+ expect ( error . name ) . toBe ( 'FileSystemError' ) ;
47+ expect ( error . message ) . toBe ( 'File not found' ) ;
48+ expect ( error . code ) . toBe ( 'ENOENT' ) ;
49+ expect ( error . path ) . toBe ( '/path/to/file' ) ;
50+ expect ( error . operation ) . toBe ( 'read' ) ;
51+ } ) ;
52+ } ) ;
53+
54+ describe ( 'RateLimitError' , ( ) => {
55+ it ( 'should create a rate limit error with proper properties' , ( ) => {
56+ const error = new RateLimitError ( 'Rate limit exceeded' , 'gemini' , 60 ) ;
57+
58+ expect ( error ) . toBeInstanceOf ( Error ) ;
59+ expect ( error ) . toBeInstanceOf ( ApiError ) ;
60+ expect ( error ) . toBeInstanceOf ( RateLimitError ) ;
61+ expect ( error . name ) . toBe ( 'RateLimitError' ) ;
62+ expect ( error . message ) . toBe ( 'Rate limit exceeded' ) ;
63+ expect ( error . code ) . toBe ( 'RATE_LIMIT_ERROR' ) ;
64+ expect ( error . service ) . toBe ( 'gemini' ) ;
65+ expect ( error . statusCode ) . toBe ( 429 ) ;
66+ expect ( error . retryAfter ) . toBe ( 60 ) ;
67+ } ) ;
68+ } ) ;
69+
70+ describe ( 'ConversationLockedError' , ( ) => {
71+ it ( 'should create a conversation locked error' , ( ) => {
72+ const error = new ConversationLockedError ( 'session-123' ) ;
73+
74+ expect ( error ) . toBeInstanceOf ( SessionError ) ;
75+ expect ( error . name ) . toBe ( 'ConversationLockedError' ) ;
76+ expect ( error . message ) . toBe ( 'Session session-123 is currently processing another request' ) ;
77+ expect ( error . code ) . toBe ( 'SESSION_LOCKED' ) ;
78+ expect ( error . sessionId ) . toBe ( 'session-123' ) ;
79+ } ) ;
80+ } ) ;
81+
82+ describe ( 'SessionNotFoundError' , ( ) => {
83+ it ( 'should create a session not found error' , ( ) => {
84+ const error = new SessionNotFoundError ( 'session-456' ) ;
85+
86+ expect ( error ) . toBeInstanceOf ( SessionError ) ;
87+ expect ( error . name ) . toBe ( 'SessionNotFoundError' ) ;
88+ expect ( error . message ) . toBe ( 'Session session-456 not found or expired' ) ;
89+ expect ( error . code ) . toBe ( 'SESSION_NOT_FOUND' ) ;
90+ expect ( error . sessionId ) . toBe ( 'session-456' ) ;
91+ } ) ;
92+ } ) ;
93+ } ) ;
94+
95+ describe ( 'ErrorClassifier with Custom Errors' , ( ) => {
96+ it ( 'should classify SessionError correctly' , ( ) => {
97+ const error = new SessionError ( 'Test' , 'SESSION_ERROR' , 'session-123' ) ;
98+ const classification = ErrorClassifier . classify ( error ) ;
99+
100+ expect ( classification . category ) . toBe ( 'session' ) ;
101+ expect ( classification . code ) . toBe ( 'SESSION_ERROR' ) ;
102+ expect ( classification . isRetryable ) . toBe ( false ) ;
103+ } ) ;
104+
105+ it ( 'should classify ConversationLockedError as retryable' , ( ) => {
106+ const error = new ConversationLockedError ( 'session-123' ) ;
107+ const classification = ErrorClassifier . classify ( error ) ;
108+
109+ expect ( classification . category ) . toBe ( 'session' ) ;
110+ expect ( classification . code ) . toBe ( 'SESSION_LOCKED' ) ;
111+ expect ( classification . isRetryable ) . toBe ( true ) ;
112+ } ) ;
113+
114+ it ( 'should classify RateLimitError correctly' , ( ) => {
115+ const error = new RateLimitError ( 'Rate limited' , 'gemini' ) ;
116+ const classification = ErrorClassifier . classify ( error ) ;
117+
118+ expect ( classification . category ) . toBe ( 'api' ) ;
119+ expect ( classification . code ) . toBe ( 'RATE_LIMIT_ERROR' ) ;
120+ expect ( classification . isRetryable ) . toBe ( true ) ;
121+ } ) ;
122+
123+ it ( 'should classify FileSystemError correctly' , ( ) => {
124+ const error = new FileSystemError ( 'File error' , 'ENOENT' , '/path' ) ;
125+ const classification = ErrorClassifier . classify ( error ) ;
126+
127+ expect ( classification . category ) . toBe ( 'filesystem' ) ;
128+ expect ( classification . code ) . toBe ( 'ENOENT' ) ;
129+ expect ( classification . isRetryable ) . toBe ( false ) ;
130+ } ) ;
131+
132+ it ( 'should still handle native filesystem errors' , ( ) => {
133+ const error = new Error ( 'ENOENT: no such file or directory' ) ;
134+ ( error as any ) . code = 'ENOENT' ;
135+
136+ const classification = ErrorClassifier . classify ( error ) ;
137+
138+ expect ( classification . category ) . toBe ( 'filesystem' ) ;
139+ expect ( classification . code ) . toBe ( 'ENOENT' ) ;
140+ expect ( classification . isRetryable ) . toBe ( false ) ;
141+ } ) ;
142+
143+ it ( 'should handle GoogleGenerativeAIError for backward compatibility' , ( ) => {
144+ const error = new Error ( 'GoogleGenerativeAIError: Invalid API key' ) ;
145+
146+ const classification = ErrorClassifier . classify ( error ) ;
147+
148+ expect ( classification . category ) . toBe ( 'api' ) ;
149+ expect ( classification . code ) . toBe ( 'API_AUTH_ERROR' ) ;
150+ expect ( classification . isRetryable ) . toBe ( false ) ;
151+ } ) ;
152+ } ) ;
0 commit comments