99from  httpx  import  Timeout 
1010from  inline_snapshot  import  Is , snapshot 
1111from  pydantic  import  BaseModel 
12+ from  pytest_mock  import  MockerFixture 
1213from  typing_extensions  import  TypedDict 
1314
1415from  pydantic_ai  import  (
4142)
4243from  pydantic_ai .agent  import  Agent 
4344from  pydantic_ai .builtin_tools  import  CodeExecutionTool , ImageGenerationTool , UrlContextTool , WebSearchTool 
44- from  pydantic_ai .exceptions  import  ModelRetry , UnexpectedModelBehavior , UserError 
45+ from  pydantic_ai .exceptions  import  ModelHTTPError ,  ModelRetry , UnexpectedModelBehavior , UserError 
4546from  pydantic_ai .messages  import  (
4647    BuiltinToolCallEvent ,  # pyright: ignore[reportDeprecated] 
4748    BuiltinToolResultEvent ,  # pyright: ignore[reportDeprecated] 
5455from  ..parts_from_messages  import  part_types_from_messages 
5556
5657with  try_import () as  imports_successful :
58+     from  google .genai  import  errors 
5759    from  google .genai .types  import  (
5860        GenerateContentResponse ,
5961        GenerateContentResponseUsageMetadata ,
@@ -2929,3 +2931,96 @@ async def test_google_vertexai_image_generation(allow_model_requests: None, vert
29292931            identifier = 'f3edd8' ,
29302932        )
29312933    )
2934+ 
2935+ 
2936+ # API 에러 테스트 데이터 
2937+ @pytest .mark .parametrize ( 
2938+     'error_class,error_response,expected_status' , 
2939+     [( 
2940+         errors .ServerError , 
2941+         { 
2942+             'error' : { 
2943+                 'code' : 503 , 
2944+                 'message' : 'The service is currently unavailable.' , 
2945+                 'status' : 'UNAVAILABLE'  
2946+             } 
2947+         }, 
2948+         503  
2949+     ), 
2950+     ( 
2951+         errors .ClientError , 
2952+         { 
2953+             'error' : { 
2954+                 'code' : 400 , 
2955+                 'message' : 'Invalid request parameters' , 
2956+                 'status' : 'INVALID_ARGUMENT'  
2957+             } 
2958+         }, 
2959+         400  
2960+     ), 
2961+     ( 
2962+         errors .ClientError , 
2963+         { 
2964+             'error' : { 
2965+                 'code' : 429 , 
2966+                 'message' : 'Rate limit exceeded' , 
2967+                 'status' : 'RESOURCE_EXHAUSTED'  
2968+             } 
2969+         }, 
2970+         429  
2971+     ), 
2972+ ]) 
2973+ async  def  test_google_api_errors_are_handled (
2974+     allow_model_requests : None ,
2975+     google_provider : GoogleProvider ,
2976+     mocker : MockerFixture ,
2977+     error_class : type [errors .APIError ],
2978+     error_response : dict ,
2979+     expected_status : int ,
2980+ ):
2981+     model  =  GoogleModel ('gemini-1.5-flash' , provider = google_provider )
2982+     mocked_error  =  error_class (expected_status , error_response )
2983+     mocker .patch .object (
2984+         model .client .aio .models ,
2985+         'generate_content' ,
2986+         side_effect = mocked_error 
2987+     )
2988+     
2989+     agent  =  Agent (model = model )
2990+     
2991+     with  pytest .raises (ModelHTTPError ) as  exc_info :
2992+         await  agent .run ('This prompt will trigger the mocked error.' )
2993+         
2994+     assert  exc_info .value .status_code  ==  expected_status 
2995+     assert  error_response ['error' ]['message' ] in  str (exc_info .value .body )
2996+ 
2997+ 
2998+ @pytest .mark .parametrize ( 
2999+     'error_class,expected_status' ,  
3000+     [ 
3001+         (errors .UnknownFunctionCallArgumentError , 400 ), 
3002+         (errors .UnsupportedFunctionError , 404 ), 
3003+         (errors .FunctionInvocationError , 400 ), 
3004+         (errors .UnknownApiResponseError , 422 ), 
3005+ ]) 
3006+ async  def  test_google_specific_errors_are_handled (
3007+     allow_model_requests : None ,
3008+     google_provider : GoogleProvider ,
3009+     mocker : MockerFixture ,
3010+     error_class : type [errors .APIError ],
3011+     expected_status : int ,
3012+ ):
3013+     
3014+     model  =  GoogleModel ('gemini-1.5-flash' , provider = google_provider )
3015+     mocked_error  =  error_class 
3016+     mocker .patch .object (
3017+         model .client .aio .models ,
3018+         'generate_content' ,
3019+         side_effect = mocked_error 
3020+     )
3021+     
3022+     agent  =  Agent (model = model )
3023+     
3024+     with  pytest .raises (ModelHTTPError ) as  exc_info :
3025+         await  agent .run ('This prompt will trigger the mocked error.' )
3026+     assert  exc_info .value .status_code  ==  expected_status         
0 commit comments