11from __future__ import annotations
22
3+ import importlib
4+ from collections .abc import Callable
5+ from types import ModuleType
6+
37import httpx
48import pytest
5- from vercel import oidc as vercel_oidc
69
710import ai
811from ai .providers .ai_gateway .client import errors
912
1013
14+ def _set_oidc_token (
15+ monkeypatch : pytest .MonkeyPatch ,
16+ get_token : Callable [[], str ],
17+ ) -> None :
18+ real_import_module = importlib .import_module
19+ oidc = ModuleType ("vercel.oidc" )
20+ oidc .__dict__ ["get_vercel_oidc_token" ] = get_token
21+
22+ def _import_module (name : str , package : str | None = None ) -> ModuleType :
23+ if name == "vercel.oidc" :
24+ return oidc
25+ return real_import_module (name , package )
26+
27+ monkeypatch .setattr (importlib , "import_module" , _import_module )
28+
29+
30+ def _fail_oidc_import (monkeypatch : pytest .MonkeyPatch ) -> None :
31+ real_import_module = importlib .import_module
32+
33+ def _import_module (name : str , package : str | None = None ) -> ModuleType :
34+ if name == "vercel.oidc" :
35+ pytest .fail ("OIDC should not be imported when an API key is set" )
36+ return real_import_module (name , package )
37+
38+ monkeypatch .setattr (importlib , "import_module" , _import_module )
39+
40+
1141async def test_list_models_gets_config_with_gateway_headers_and_sorts_ids () -> (
1242 None
1343):
@@ -79,11 +109,7 @@ async def test_list_models_uses_oidc_on_vercel_when_no_api_key(
79109) -> None :
80110 monkeypatch .delenv ("AI_GATEWAY_API_KEY" , raising = False )
81111 monkeypatch .setenv ("VERCEL" , "1" )
82- monkeypatch .setattr (
83- vercel_oidc ,
84- "get_vercel_oidc_token" ,
85- lambda : "oidc-test-token" ,
86- )
112+ _set_oidc_token (monkeypatch , lambda : "oidc-test-token" )
87113 captured_headers : dict [str , str ] = {}
88114
89115 def _handler (request : httpx .Request ) -> httpx .Response :
@@ -115,11 +141,7 @@ async def test_list_models_uses_oidc_token_env_without_vercel_flag(
115141 monkeypatch .delenv ("AI_GATEWAY_API_KEY" , raising = False )
116142 monkeypatch .delenv ("VERCEL" , raising = False )
117143 monkeypatch .setenv ("VERCEL_OIDC_TOKEN" , "pulled-oidc-token" )
118- monkeypatch .setattr (
119- vercel_oidc ,
120- "get_vercel_oidc_token" ,
121- lambda : "pulled-oidc-token" ,
122- )
144+ _set_oidc_token (monkeypatch , lambda : "pulled-oidc-token" )
123145 captured_headers : dict [str , str ] = {}
124146
125147 def _handler (request : httpx .Request ) -> httpx .Response :
@@ -141,20 +163,45 @@ def _handler(request: httpx.Request) -> httpx.Response:
141163 assert captured_headers ["ai-gateway-auth-method" ] == "oidc"
142164
143165
144- async def test_api_key_env_takes_precedence_over_oidc (
166+ async def test_oidc_expected_without_vercel_extra_raises_installation_error (
145167 monkeypatch : pytest .MonkeyPatch ,
146168) -> None :
147- monkeypatch .setenv ("AI_GATEWAY_API_KEY" , "env-test-key" )
169+ monkeypatch .delenv ("AI_GATEWAY_API_KEY" , raising = False )
148170 monkeypatch .setenv ("VERCEL" , "1" )
171+ real_import_module = importlib .import_module
172+
173+ def _import_module (name : str , package : str | None = None ) -> ModuleType :
174+ if name == "vercel.oidc" :
175+ raise ModuleNotFoundError (name = "vercel" )
176+ return real_import_module (name , package )
149177
150- def _fail_oidc ( ) -> str :
151- pytest .fail ("OIDC should not be fetched when an API key is set " )
178+ def _handler ( request : httpx . Request ) -> httpx . Response :
179+ pytest .fail ("Gateway should not be called without the OIDC helper " )
152180
153- monkeypatch .setattr (
154- vercel_oidc ,
155- "get_vercel_oidc_token" ,
156- _fail_oidc ,
181+ monkeypatch .setattr (importlib , "import_module" , _import_module )
182+ provider = ai .get_provider (
183+ "vercel" ,
184+ base_url = "https://gateway.test/v3/ai" ,
185+ client = httpx .AsyncClient (transport = httpx .MockTransport (_handler )),
157186 )
187+
188+ try :
189+ with pytest .raises (ai .InstallationError ) as exc_info :
190+ await provider .list_models ()
191+ finally :
192+ await provider .aclose ()
193+
194+ assert "AI Gateway OIDC authentication requires" in str (exc_info .value )
195+ assert "ai[vercel]" in str (exc_info .value )
196+ assert "AI_GATEWAY_API_KEY" in str (exc_info .value )
197+
198+
199+ async def test_api_key_env_takes_precedence_over_oidc (
200+ monkeypatch : pytest .MonkeyPatch ,
201+ ) -> None :
202+ monkeypatch .setenv ("AI_GATEWAY_API_KEY" , "env-test-key" )
203+ monkeypatch .setenv ("VERCEL" , "1" )
204+ _fail_oidc_import (monkeypatch )
158205 captured_headers : dict [str , str ] = {}
159206
160207 def _handler (request : httpx .Request ) -> httpx .Response :
@@ -181,15 +228,7 @@ async def test_explicit_api_key_takes_precedence_over_oidc(
181228) -> None :
182229 monkeypatch .setenv ("AI_GATEWAY_API_KEY" , "env-test-key" )
183230 monkeypatch .setenv ("VERCEL" , "1" )
184-
185- def _fail_oidc () -> str :
186- pytest .fail ("OIDC should not be fetched when an API key is set" )
187-
188- monkeypatch .setattr (
189- vercel_oidc ,
190- "get_vercel_oidc_token" ,
191- _fail_oidc ,
192- )
231+ _fail_oidc_import (monkeypatch )
193232 captured_headers : dict [str , str ] = {}
194233
195234 def _handler (request : httpx .Request ) -> httpx .Response :
0 commit comments