@@ -51,6 +51,14 @@ describe('OpenRouterHandler', () => {
5151 } )
5252 } )
5353
54+ test ( 'getModel returns default model info when options are not provided' , ( ) => {
55+ const handler = new OpenRouterHandler ( { } )
56+ const result = handler . getModel ( )
57+
58+ expect ( result . id ) . toBe ( 'anthropic/claude-3.5-sonnet:beta' )
59+ expect ( result . info . supportsPromptCache ) . toBe ( true )
60+ } )
61+
5462 test ( 'createMessage generates correct stream chunks' , async ( ) => {
5563 const handler = new OpenRouterHandler ( mockOptions )
5664 const mockStream = {
@@ -118,4 +126,158 @@ describe('OpenRouterHandler', () => {
118126 stream : true
119127 } ) )
120128 } )
129+
130+ test ( 'createMessage with middle-out transform enabled' , async ( ) => {
131+ const handler = new OpenRouterHandler ( {
132+ ...mockOptions ,
133+ openRouterUseMiddleOutTransform : true
134+ } )
135+ const mockStream = {
136+ async * [ Symbol . asyncIterator ] ( ) {
137+ yield {
138+ id : 'test-id' ,
139+ choices : [ {
140+ delta : {
141+ content : 'test response'
142+ }
143+ } ]
144+ }
145+ }
146+ }
147+
148+ const mockCreate = jest . fn ( ) . mockResolvedValue ( mockStream )
149+ ; ( OpenAI as jest . MockedClass < typeof OpenAI > ) . prototype . chat = {
150+ completions : { create : mockCreate }
151+ } as any
152+ ; ( axios . get as jest . Mock ) . mockResolvedValue ( { data : { data : { } } } )
153+
154+ await handler . createMessage ( 'test' , [ ] ) . next ( )
155+
156+ expect ( mockCreate ) . toHaveBeenCalledWith ( expect . objectContaining ( {
157+ transforms : [ 'middle-out' ]
158+ } ) )
159+ } )
160+
161+ test ( 'createMessage with Claude model adds cache control' , async ( ) => {
162+ const handler = new OpenRouterHandler ( {
163+ ...mockOptions ,
164+ openRouterModelId : 'anthropic/claude-3.5-sonnet'
165+ } )
166+ const mockStream = {
167+ async * [ Symbol . asyncIterator ] ( ) {
168+ yield {
169+ id : 'test-id' ,
170+ choices : [ {
171+ delta : {
172+ content : 'test response'
173+ }
174+ } ]
175+ }
176+ }
177+ }
178+
179+ const mockCreate = jest . fn ( ) . mockResolvedValue ( mockStream )
180+ ; ( OpenAI as jest . MockedClass < typeof OpenAI > ) . prototype . chat = {
181+ completions : { create : mockCreate }
182+ } as any
183+ ; ( axios . get as jest . Mock ) . mockResolvedValue ( { data : { data : { } } } )
184+
185+ const messages : Anthropic . Messages . MessageParam [ ] = [
186+ { role : 'user' , content : 'message 1' } ,
187+ { role : 'assistant' , content : 'response 1' } ,
188+ { role : 'user' , content : 'message 2' }
189+ ]
190+
191+ await handler . createMessage ( 'test system' , messages ) . next ( )
192+
193+ expect ( mockCreate ) . toHaveBeenCalledWith ( expect . objectContaining ( {
194+ messages : expect . arrayContaining ( [
195+ expect . objectContaining ( {
196+ role : 'system' ,
197+ content : expect . arrayContaining ( [
198+ expect . objectContaining ( {
199+ cache_control : { type : 'ephemeral' }
200+ } )
201+ ] )
202+ } )
203+ ] )
204+ } ) )
205+ } )
206+
207+ test ( 'createMessage handles API errors' , async ( ) => {
208+ const handler = new OpenRouterHandler ( mockOptions )
209+ const mockStream = {
210+ async * [ Symbol . asyncIterator ] ( ) {
211+ yield {
212+ error : {
213+ message : 'API Error' ,
214+ code : 500
215+ }
216+ }
217+ }
218+ }
219+
220+ const mockCreate = jest . fn ( ) . mockResolvedValue ( mockStream )
221+ ; ( OpenAI as jest . MockedClass < typeof OpenAI > ) . prototype . chat = {
222+ completions : { create : mockCreate }
223+ } as any
224+
225+ const generator = handler . createMessage ( 'test' , [ ] )
226+ await expect ( generator . next ( ) ) . rejects . toThrow ( 'OpenRouter API Error 500: API Error' )
227+ } )
228+
229+ test ( 'completePrompt returns correct response' , async ( ) => {
230+ const handler = new OpenRouterHandler ( mockOptions )
231+ const mockResponse = {
232+ choices : [ {
233+ message : {
234+ content : 'test completion'
235+ }
236+ } ]
237+ }
238+
239+ const mockCreate = jest . fn ( ) . mockResolvedValue ( mockResponse )
240+ ; ( OpenAI as jest . MockedClass < typeof OpenAI > ) . prototype . chat = {
241+ completions : { create : mockCreate }
242+ } as any
243+
244+ const result = await handler . completePrompt ( 'test prompt' )
245+
246+ expect ( result ) . toBe ( 'test completion' )
247+ expect ( mockCreate ) . toHaveBeenCalledWith ( {
248+ model : mockOptions . openRouterModelId ,
249+ messages : [ { role : 'user' , content : 'test prompt' } ] ,
250+ temperature : 0 ,
251+ stream : false
252+ } )
253+ } )
254+
255+ test ( 'completePrompt handles API errors' , async ( ) => {
256+ const handler = new OpenRouterHandler ( mockOptions )
257+ const mockError = {
258+ error : {
259+ message : 'API Error' ,
260+ code : 500
261+ }
262+ }
263+
264+ const mockCreate = jest . fn ( ) . mockResolvedValue ( mockError )
265+ ; ( OpenAI as jest . MockedClass < typeof OpenAI > ) . prototype . chat = {
266+ completions : { create : mockCreate }
267+ } as any
268+
269+ await expect ( handler . completePrompt ( 'test prompt' ) )
270+ . rejects . toThrow ( 'OpenRouter API Error 500: API Error' )
271+ } )
272+
273+ test ( 'completePrompt handles unexpected errors' , async ( ) => {
274+ const handler = new OpenRouterHandler ( mockOptions )
275+ const mockCreate = jest . fn ( ) . mockRejectedValue ( new Error ( 'Unexpected error' ) )
276+ ; ( OpenAI as jest . MockedClass < typeof OpenAI > ) . prototype . chat = {
277+ completions : { create : mockCreate }
278+ } as any
279+
280+ await expect ( handler . completePrompt ( 'test prompt' ) )
281+ . rejects . toThrow ( 'OpenRouter completion error: Unexpected error' )
282+ } )
121283} )
0 commit comments