@@ -409,6 +409,68 @@ def test_force_oauth_invalid_raises(self, monkeypatch, temp_dir):
409409 agent ._resolve_oauth_creds_path ()
410410
411411
412+ class TestResolveEnvAuthType :
413+ """Test _resolve_env_auth_type() credential detection."""
414+
415+ def test_api_key_selects_gemini_api_key (self , monkeypatch , temp_dir ):
416+ """GEMINI_API_KEY selects the gemini-api-key auth method."""
417+ monkeypatch .delenv ("GOOGLE_GENAI_USE_VERTEXAI" , raising = False )
418+ monkeypatch .delenv ("GOOGLE_API_KEY" , raising = False )
419+ monkeypatch .setenv ("GEMINI_API_KEY" , "test-key" )
420+
421+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
422+ assert agent ._resolve_env_auth_type () == "gemini-api-key"
423+
424+ def test_google_api_key_selects_gemini_api_key (self , monkeypatch , temp_dir ):
425+ """GOOGLE_API_KEY also selects the gemini-api-key auth method."""
426+ monkeypatch .delenv ("GOOGLE_GENAI_USE_VERTEXAI" , raising = False )
427+ monkeypatch .delenv ("GEMINI_API_KEY" , raising = False )
428+ monkeypatch .setenv ("GOOGLE_API_KEY" , "test-key" )
429+
430+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
431+ assert agent ._resolve_env_auth_type () == "gemini-api-key"
432+
433+ def test_vertex_flag_selects_vertex_ai (self , monkeypatch , temp_dir ):
434+ """A truthy GOOGLE_GENAI_USE_VERTEXAI selects the vertex-ai method."""
435+ monkeypatch .delenv ("GEMINI_API_KEY" , raising = False )
436+ monkeypatch .delenv ("GOOGLE_API_KEY" , raising = False )
437+ monkeypatch .setenv ("GOOGLE_GENAI_USE_VERTEXAI" , "true" )
438+
439+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
440+ assert agent ._resolve_env_auth_type () == "vertex-ai"
441+
442+ def test_vertex_takes_precedence_over_api_key (self , monkeypatch , temp_dir ):
443+ """Vertex wins when both the Vertex flag and an API key are present."""
444+ monkeypatch .delenv ("GOOGLE_API_KEY" , raising = False )
445+ monkeypatch .setenv ("GEMINI_API_KEY" , "test-key" )
446+ monkeypatch .setenv ("GOOGLE_GENAI_USE_VERTEXAI" , "true" )
447+
448+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
449+ assert agent ._resolve_env_auth_type () == "vertex-ai"
450+
451+ def test_api_key_via_extra_env (self , monkeypatch , temp_dir ):
452+ """GEMINI_API_KEY supplied via extra_env (--ae) is detected."""
453+ monkeypatch .delenv ("GOOGLE_GENAI_USE_VERTEXAI" , raising = False )
454+ monkeypatch .delenv ("GEMINI_API_KEY" , raising = False )
455+ monkeypatch .delenv ("GOOGLE_API_KEY" , raising = False )
456+
457+ agent = GeminiCli (
458+ logs_dir = temp_dir ,
459+ model_name = _OAUTH_MODEL ,
460+ extra_env = {"GEMINI_API_KEY" : "test-key" },
461+ )
462+ assert agent ._resolve_env_auth_type () == "gemini-api-key"
463+
464+ def test_no_credentials_returns_none (self , monkeypatch , temp_dir ):
465+ """With no recognized credentials, selection is left to the CLI."""
466+ monkeypatch .delenv ("GOOGLE_GENAI_USE_VERTEXAI" , raising = False )
467+ monkeypatch .delenv ("GEMINI_API_KEY" , raising = False )
468+ monkeypatch .delenv ("GOOGLE_API_KEY" , raising = False )
469+
470+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
471+ assert agent ._resolve_env_auth_type () is None
472+
473+
412474class TestGeminiRunAuth :
413475 """Test that run() wires auth correctly."""
414476
@@ -493,3 +555,50 @@ async def test_uses_api_key_when_no_oauth_creds(
493555 if "GEMINI_API_KEY" in c .kwargs ["env" ]
494556 )
495557 assert run_call .kwargs ["env" ]["GEMINI_API_KEY" ] == "test-key"
558+
559+ @pytest .mark .asyncio
560+ async def test_pins_api_key_auth_in_settings (self , tmp_path , monkeypatch , temp_dir ):
561+ """API-key runs pre-select gemini-api-key auth in settings.json so
562+ headless mode does not fail with "Invalid auth method selected"."""
563+ monkeypatch .setattr (Path , "home" , staticmethod (lambda : tmp_path ))
564+ monkeypatch .setenv ("GEMINI_API_KEY" , "test-key" )
565+ monkeypatch .delenv ("GOOGLE_GENAI_USE_VERTEXAI" , raising = False )
566+ monkeypatch .delenv ("GOOGLE_API_KEY" , raising = False )
567+ monkeypatch .delenv ("GEMINI_OAUTH_CREDS_PATH" , raising = False )
568+ monkeypatch .delenv ("GEMINI_FORCE_OAUTH" , raising = False )
569+
570+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
571+ mock_env = AsyncMock ()
572+ mock_env .default_user = "agent"
573+ mock_env .exec .return_value = AsyncMock (return_code = 0 , stdout = "" , stderr = "" )
574+ await agent .run ("do something" , mock_env , AsyncMock ())
575+
576+ settings_call = next (
577+ c
578+ for c in mock_env .exec .call_args_list
579+ if "settings.json" in c .kwargs ["command" ]
580+ )
581+ assert "gemini-api-key" in settings_call .kwargs ["command" ]
582+
583+ @pytest .mark .asyncio
584+ async def test_pins_oauth_personal_auth_in_settings (
585+ self , tmp_path , monkeypatch , temp_dir
586+ ):
587+ """OAuth runs pre-select oauth-personal auth in settings.json."""
588+ creds_file = tmp_path / "oauth_creds.json"
589+ creds_file .write_text (json .dumps ({"access_token" : "tok" }))
590+ monkeypatch .setenv ("GEMINI_OAUTH_CREDS_PATH" , str (creds_file ))
591+ monkeypatch .delenv ("GEMINI_FORCE_OAUTH" , raising = False )
592+
593+ agent = GeminiCli (logs_dir = temp_dir , model_name = _OAUTH_MODEL )
594+ mock_env = AsyncMock ()
595+ mock_env .default_user = "agent"
596+ mock_env .exec .return_value = AsyncMock (return_code = 0 , stdout = "" , stderr = "" )
597+ await agent .run ("do something" , mock_env , AsyncMock ())
598+
599+ settings_call = next (
600+ c
601+ for c in mock_env .exec .call_args_list
602+ if "settings.json" in c .kwargs ["command" ]
603+ )
604+ assert "oauth-personal" in settings_call .kwargs ["command" ]
0 commit comments