@@ -94,6 +94,47 @@ def search_handler(params: Annotated[QueryParams, Query()]):
9494 assert any ("limit" in str (error ) for error in body ["detail" ])
9595
9696
97+ def test_validate_pydantic_query_params_detailed_errors (gw_event ):
98+ """Test that Pydantic validation errors include detailed field-level information"""
99+ app = APIGatewayRestResolver (enable_validation = True )
100+
101+ class QueryParams (BaseModel ):
102+ full_name : str = Field (..., min_length = 5 , description = "Full name with minimum 5 characters" )
103+ age : int = Field (..., ge = 18 , le = 100 , description = "Age between 18 and 100" )
104+
105+ @app .get ("/query-model" )
106+ def query_model (params : Annotated [QueryParams , Query ()]):
107+ return {"full_name" : params .full_name , "age" : params .age }
108+
109+ # Test validation error with detailed field information
110+ gw_event ["path" ] = "/query-model"
111+ gw_event ["queryStringParameters" ] = {"full_name" : "Jo" , "age" : "15" } # Both invalid
112+
113+ result = app (gw_event , {})
114+ assert result ["statusCode" ] == 422
115+
116+ body = json .loads (result ["body" ])
117+ assert "detail" in body
118+
119+ # Check that we get detailed field-level errors
120+ errors = body ["detail" ]
121+
122+ # Should have errors for both fields
123+ full_name_error = next ((e for e in errors if "full_name" in e ["loc" ]), None )
124+ age_error = next ((e for e in errors if "age" in e ["loc" ]), None )
125+
126+ assert full_name_error is not None , "Should have error for full_name field"
127+ assert age_error is not None , "Should have error for age field"
128+
129+ # Check error details for full_name
130+ assert full_name_error ["loc" ] == ["query" , "full_name" ]
131+ assert full_name_error ["type" ] == "string_too_short"
132+
133+ # Check error details for age
134+ assert age_error ["loc" ] == ["query" , "age" ]
135+ assert age_error ["type" ] == "greater_than_equal"
136+
137+
97138def test_validate_pydantic_header_params (gw_event ):
98139 """Test that Pydantic models in Header parameters are validated correctly"""
99140 app = APIGatewayRestResolver (enable_validation = True )
@@ -141,6 +182,49 @@ def protected_handler(headers: Annotated[HeaderParams, Header()]):
141182 assert any ("authorization" in str (error ) for error in body ["detail" ])
142183
143184
185+ def test_validate_pydantic_header_snake_case_to_kebab_case_schema (gw_event ):
186+ """Test that snake_case header fields are converted to kebab-case in OpenAPI schema and validation"""
187+ app = APIGatewayRestResolver (enable_validation = True )
188+ app .enable_swagger ()
189+
190+ class HeaderParams (BaseModel ):
191+ correlation_id : str = Field (description = "Correlation ID header" )
192+ user_agent : str = Field (default = "PowerTools/1.0" , description = "User agent header" )
193+
194+ @app .get ("/kebab-headers" )
195+ def kebab_handler (headers : Annotated [HeaderParams , Header ()]):
196+ return {
197+ "correlation_id" : headers .correlation_id ,
198+ "user_agent" : headers .user_agent ,
199+ }
200+
201+ # Test that OpenAPI schema uses kebab-case for headers
202+ openapi_schema = app .get_openapi_schema ()
203+ operation = openapi_schema ["paths" ]["/kebab-headers" ]["get" ]
204+ parameters = operation ["parameters" ]
205+
206+ # Find the correlation_id parameter
207+ correlation_param = next ((p for p in parameters if p ["name" ] == "correlation-id" ), None )
208+ assert correlation_param is not None , "Should have correlation-id parameter in kebab-case"
209+ assert correlation_param ["in" ] == "header"
210+
211+ # Find the user_agent parameter
212+ user_agent_param = next ((p for p in parameters if p ["name" ] == "user-agent" ), None )
213+ assert user_agent_param is not None , "Should have user-agent parameter in kebab-case"
214+ assert user_agent_param ["in" ] == "header"
215+
216+ # Test validation with kebab-case headers
217+ gw_event ["path" ] = "/kebab-headers"
218+ gw_event ["headers" ] = {"correlation-id" : "test-123" , "user-agent" : "TestClient/1.0" }
219+
220+ result = app (gw_event , {})
221+ assert result ["statusCode" ] == 200
222+
223+ body = json .loads (result ["body" ])
224+ assert body ["correlation_id" ] == "test-123"
225+ assert body ["user_agent" ] == "TestClient/1.0"
226+
227+
144228def test_validate_pydantic_mixed_params (gw_event ):
145229 """Test that mixed Pydantic models (Query + Header) are validated correctly"""
146230 app = APIGatewayRestResolver (enable_validation = True )
0 commit comments