@@ -53,19 +53,20 @@ describe('getApiKey', () => {
5353 module . require = originalRequire
5454 } )
5555
56- it ( 'should return user-provided key when not in hosted environment' , ( ) => {
56+ it . concurrent ( 'should return user-provided key when not in hosted environment' , ( ) => {
5757 isHostedSpy . mockReturnValue ( false )
5858
59- // For OpenAI
6059 const key1 = getApiKey ( 'openai' , 'gpt-4' , 'user-key-openai' )
6160 expect ( key1 ) . toBe ( 'user-key-openai' )
6261
63- // For Anthropic
6462 const key2 = getApiKey ( 'anthropic' , 'claude-3' , 'user-key-anthropic' )
6563 expect ( key2 ) . toBe ( 'user-key-anthropic' )
64+
65+ const key3 = getApiKey ( 'google' , 'gemini-2.5-flash' , 'user-key-google' )
66+ expect ( key3 ) . toBe ( 'user-key-google' )
6667 } )
6768
68- it ( 'should throw error if no key provided in non-hosted environment' , ( ) => {
69+ it . concurrent ( 'should throw error if no key provided in non-hosted environment' , ( ) => {
6970 isHostedSpy . mockReturnValue ( false )
7071
7172 expect ( ( ) => getApiKey ( 'openai' , 'gpt-4' ) ) . toThrow ( 'API key is required for openai gpt-4' )
@@ -74,63 +75,87 @@ describe('getApiKey', () => {
7475 )
7576 } )
7677
77- it ( 'should fall back to user key in hosted environment if rotation fails' , ( ) => {
78+ it . concurrent ( 'should fall back to user key in hosted environment if rotation fails' , ( ) => {
7879 isHostedSpy . mockReturnValue ( true )
7980
8081 module . require = vi . fn ( ( ) => {
8182 throw new Error ( 'Rotation failed' )
8283 } )
8384
84- // Use gpt-4o which IS in the hosted models list
8585 const key = getApiKey ( 'openai' , 'gpt-4o' , 'user-fallback-key' )
8686 expect ( key ) . toBe ( 'user-fallback-key' )
8787 } )
8888
89- it ( 'should throw error in hosted environment if rotation fails and no user key' , ( ) => {
90- isHostedSpy . mockReturnValue ( true )
89+ it . concurrent (
90+ 'should throw error in hosted environment if rotation fails and no user key' ,
91+ ( ) => {
92+ isHostedSpy . mockReturnValue ( true )
9193
92- module . require = vi . fn ( ( ) => {
93- throw new Error ( 'Rotation failed' )
94- } )
94+ module . require = vi . fn ( ( ) => {
95+ throw new Error ( 'Rotation failed' )
96+ } )
9597
96- // Use gpt-4o which IS in the hosted models list
97- expect ( ( ) => getApiKey ( 'openai' , 'gpt-4o' ) ) . toThrow ( 'No API key available for openai gpt-4o' )
98- } )
98+ expect ( ( ) => getApiKey ( 'openai' , ' gpt-4o' ) ) . toThrow ( 'No API key available for openai gpt-4o' )
99+ }
100+ )
99101
100- it ( 'should require user key for non-OpenAI/Anthropic providers even in hosted environment' , ( ) => {
101- isHostedSpy . mockReturnValue ( true )
102+ it . concurrent (
103+ 'should require user key for non-OpenAI/Anthropic providers even in hosted environment' ,
104+ ( ) => {
105+ isHostedSpy . mockReturnValue ( true )
102106
103- const key = getApiKey ( 'other-provider' , 'some-model' , 'user-key' )
104- expect ( key ) . toBe ( 'user-key' )
107+ const key = getApiKey ( 'other-provider' , 'some-model' , 'user-key' )
108+ expect ( key ) . toBe ( 'user-key' )
105109
106- expect ( ( ) => getApiKey ( 'other-provider' , 'some-model' ) ) . toThrow (
107- 'API key is required for other-provider some-model'
108- )
109- } )
110+ expect ( ( ) => getApiKey ( 'other-provider' , 'some-model' ) ) . toThrow (
111+ 'API key is required for other-provider some-model'
112+ )
113+ }
114+ )
110115
111- it ( 'should require user key for models NOT in hosted list even if provider matches' , ( ) => {
112- isHostedSpy . mockReturnValue ( true )
116+ it . concurrent (
117+ 'should require user key for models NOT in hosted list even if provider matches' ,
118+ ( ) => {
119+ isHostedSpy . mockReturnValue ( true )
113120
114- // Models with version suffixes that are NOT in the hosted list should require user API key
115- // even though they're from anthropic/openai providers
121+ const key1 = getApiKey ( 'anthropic' , 'claude-sonnet-4-20250514' , ' user- key-anthropic' )
122+ expect ( key1 ) . toBe ( 'user-key- anthropic' )
116123
117- // User provides their own key - should work
118- const key1 = getApiKey ( ' anthropic' , ' claude-sonnet-4-20250514', 'user-key-anthropic' )
119- expect ( key1 ) . toBe ( 'user-key-anthropic' )
124+ expect ( ( ) => getApiKey ( 'anthropic' , 'claude-sonnet-4-20250514' ) ) . toThrow (
125+ 'API key is required for anthropic claude-sonnet-4-20250514'
126+ )
120127
121- // No user key - should throw, NOT use server key
122- expect ( ( ) => getApiKey ( 'anthropic' , 'claude-sonnet-4-20250514' ) ) . toThrow (
123- 'API key is required for anthropic claude-sonnet-4-20250514'
124- )
128+ const key2 = getApiKey ( 'openai' , 'gpt-4o-2024-08-06' , 'user-key-openai' )
129+ expect ( key2 ) . toBe ( 'user-key-openai' )
130+
131+ expect ( ( ) => getApiKey ( 'openai' , 'gpt-4o-2024-08-06' ) ) . toThrow (
132+ 'API key is required for openai gpt-4o-2024-08-06'
133+ )
134+ }
135+ )
125136
126- // Same for OpenAI versioned models not in list
127- const key2 = getApiKey ( 'openai' , 'gpt-4o-2024-08-06' , 'user-key-openai' )
128- expect ( key2 ) . toBe ( 'user-key-openai' )
137+ it . concurrent ( 'should return empty for ollama provider without requiring API key' , ( ) => {
138+ isHostedSpy . mockReturnValue ( false )
129139
130- expect ( ( ) => getApiKey ( 'openai' , 'gpt-4o-2024-08-06' ) ) . toThrow (
131- 'API key is required for openai gpt-4o-2024-08-06'
132- )
140+ const key = getApiKey ( 'ollama' , 'llama2' )
141+ expect ( key ) . toBe ( 'empty' )
142+
143+ const key2 = getApiKey ( 'ollama' , 'codellama' , 'user-key' )
144+ expect ( key2 ) . toBe ( 'empty' )
133145 } )
146+
147+ it . concurrent (
148+ 'should return empty or user-provided key for vllm provider without requiring API key' ,
149+ ( ) => {
150+ isHostedSpy . mockReturnValue ( false )
151+
152+ const key = getApiKey ( 'vllm' , 'vllm/qwen-3' )
153+ expect ( key ) . toBe ( 'empty' )
154+
155+ const key2 = getApiKey ( 'vllm' , 'vllm/llama' , 'user-key' )
156+ expect ( key2 ) . toBe ( 'user-key' )
157+ }
158+ )
134159} )
135160
136161describe ( 'Model Capabilities' , ( ) => {
@@ -202,7 +227,6 @@ describe('Model Capabilities', () => {
202227 it . concurrent (
203228 'should inherit temperature support from provider for dynamically fetched models' ,
204229 ( ) => {
205- // OpenRouter models should inherit temperature support from provider capabilities
206230 expect ( supportsTemperature ( 'openrouter/anthropic/claude-3.5-sonnet' ) ) . toBe ( true )
207231 expect ( supportsTemperature ( 'openrouter/openai/gpt-4' ) ) . toBe ( true )
208232 }
0 commit comments