Skip to content

Commit e5815fd

Browse files
committed
Added tests to increase coverage
1 parent e4aebaf commit e5815fd

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

tests/models/test_openai.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,36 @@ class MyModel(BaseModel):
19151915
)
19161916

19171917

1918+
def test_openai_transformer_fallback_when_defs_missing() -> None:
1919+
"""Test that OpenAIJsonSchemaTransformer falls back to original defs when root_key not in $defs."""
1920+
from unittest.mock import patch
1921+
1922+
from pydantic_ai.profiles.openai import OpenAIJsonSchemaTransformer
1923+
1924+
# Create a schema with $ref pointing to a key that exists in original
1925+
schema: dict[str, Any] = {
1926+
'$ref': '#/$defs/MyModel',
1927+
'$defs': {
1928+
'MyModel': {
1929+
'type': 'object',
1930+
'properties': {'foo': {'type': 'string'}},
1931+
'required': ['foo'],
1932+
},
1933+
},
1934+
}
1935+
1936+
transformer = OpenAIJsonSchemaTransformer(schema, strict=True)
1937+
1938+
# Mock super().walk() to return a result without $defs to test the fallback path
1939+
with patch.object(transformer.__class__.__bases__[0], 'walk', return_value={'$ref': '#/$defs/MyModel'}):
1940+
result = transformer.walk()
1941+
# The fallback should use self.defs.get(root_key) or {}
1942+
# Since we mocked to return just $ref, the fallback should trigger
1943+
assert isinstance(result, dict)
1944+
# Result should have been updated with the original defs content
1945+
assert 'properties' in result or 'type' in result
1946+
1947+
19181948
def test_native_output_strict_mode(allow_model_requests: None):
19191949
class CityLocation(BaseModel):
19201950
city: str

tests/test_json_schema_flattener.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,145 @@ def test_flatten_allof_non_object_members_are_left_as_is() -> None:
108108
# Expect: we cannot sensibly merge non-object members; keep allOf
109109
flattened = flatten_allof(copy.deepcopy(schema))
110110
assert 'allOf' in flattened
111+
112+
113+
def test_flatten_allof_object_like_without_type() -> None:
114+
"""Test that object-like schemas without explicit type are recognized."""
115+
from pydantic_ai._json_schema import flatten_allof
116+
117+
schema: dict[str, Any] = {
118+
'type': 'object',
119+
'allOf': [
120+
{
121+
# No type, but has properties - should be recognized as object-like
122+
'properties': {'a': {'type': 'string'}},
123+
'required': ['a'],
124+
},
125+
{
126+
'type': 'object',
127+
'properties': {'b': {'type': 'integer'}},
128+
'required': ['b'],
129+
},
130+
],
131+
}
132+
133+
flattened = flatten_allof(copy.deepcopy(schema))
134+
assert 'allOf' not in flattened
135+
assert set(flattened['required']) == {'a', 'b'}
136+
137+
138+
def test_flatten_allof_with_dict_additional_properties() -> None:
139+
"""Test merging when additionalProperties is a dict schema."""
140+
from pydantic_ai._json_schema import flatten_allof
141+
142+
schema: dict[str, Any] = {
143+
'type': 'object',
144+
'allOf': [
145+
{
146+
'type': 'object',
147+
'properties': {'a': {'type': 'string'}},
148+
'additionalProperties': {'type': 'string'}, # dict schema
149+
},
150+
{
151+
'type': 'object',
152+
'properties': {'b': {'type': 'integer'}},
153+
'additionalProperties': False,
154+
},
155+
],
156+
}
157+
158+
flattened = flatten_allof(copy.deepcopy(schema))
159+
assert 'allOf' not in flattened
160+
# When any member has dict additionalProperties, result should be True
161+
assert flattened.get('additionalProperties') is True
162+
163+
164+
def test_flatten_allof_with_non_dict_member() -> None:
165+
"""Test that allOf with non-dict members is left untouched."""
166+
from pydantic_ai._json_schema import flatten_allof
167+
168+
schema: dict[str, Any] = {
169+
'type': 'object',
170+
'allOf': [
171+
{'type': 'object', 'properties': {'a': {'type': 'string'}}},
172+
'not a dict', # Non-dict member
173+
],
174+
}
175+
176+
flattened = flatten_allof(copy.deepcopy(schema))
177+
# Should be left untouched because one member is not a dict
178+
assert 'allOf' in flattened
179+
180+
181+
def test_flatten_allof_no_initial_properties() -> None:
182+
"""Test flattening when root schema has no initial properties."""
183+
from pydantic_ai._json_schema import flatten_allof
184+
185+
schema: dict[str, Any] = {
186+
'type': 'object',
187+
'allOf': [
188+
{
189+
'type': 'object',
190+
'properties': {'a': {'type': 'string'}},
191+
'required': ['a'],
192+
},
193+
],
194+
}
195+
196+
flattened = flatten_allof(copy.deepcopy(schema))
197+
assert 'allOf' not in flattened
198+
assert flattened['properties']['a']['type'] == 'string'
199+
assert flattened['required'] == ['a']
200+
201+
202+
def test_flatten_allof_members_without_properties() -> None:
203+
"""Test flattening when some members don't have properties/required/patternProperties."""
204+
from pydantic_ai._json_schema import flatten_allof
205+
206+
schema: dict[str, Any] = {
207+
'type': 'object',
208+
'allOf': [
209+
{
210+
'type': 'object',
211+
'properties': {'a': {'type': 'string'}},
212+
'required': ['a'],
213+
},
214+
{
215+
'type': 'object',
216+
# No properties, required, or patternProperties
217+
'additionalProperties': False,
218+
},
219+
{
220+
'type': 'object',
221+
'properties': {'b': {'type': 'integer'}},
222+
# No required
223+
},
224+
],
225+
}
226+
227+
flattened = flatten_allof(copy.deepcopy(schema))
228+
assert 'allOf' not in flattened
229+
assert set(flattened['properties'].keys()) == {'a', 'b'}
230+
assert flattened['required'] == ['a'] # Only from first member
231+
assert flattened.get('additionalProperties') is False
232+
233+
234+
def test_flatten_allof_empty_properties_after_merge() -> None:
235+
"""Test edge case where properties/required/patternProperties might be empty."""
236+
from pydantic_ai._json_schema import flatten_allof
237+
238+
schema: dict[str, Any] = {
239+
'type': 'object',
240+
'allOf': [
241+
{
242+
'type': 'object',
243+
# No properties, required, or patternProperties
244+
},
245+
],
246+
}
247+
248+
flattened = flatten_allof(copy.deepcopy(schema))
249+
assert 'allOf' not in flattened
250+
# Should not have properties/required if they're empty
251+
assert 'properties' not in flattened or not flattened.get('properties')
252+
assert 'required' not in flattened or not flattened.get('required')

0 commit comments

Comments
 (0)