Coverage for tests/test_pydantic_v1_v2_01.py: 100%
82 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1import sys 1abcdefy
2from typing import Any, List, Union 1abcdefy
4from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314 1abcdefy
6if sys.version_info >= (3, 14): 1abcdefy
7 skip_module_if_py_gte_314() 1y
9from fastapi import FastAPI 1abcdef
10from fastapi._compat.v1 import BaseModel 1abcdef
11from fastapi.testclient import TestClient 1abcdef
12from inline_snapshot import snapshot 1abcdef
15class SubItem(BaseModel): 1abcdef
16 name: str 1abcdef
19class Item(BaseModel): 1abcdef
20 title: str 1abcdef
21 size: int 1abcdef
22 description: Union[str, None] = None 1abcdef
23 sub: SubItem 1abcdef
24 multi: List[SubItem] = [] 1abcdef
27app = FastAPI() 1abcdef
30@app.post("/simple-model") 1abcdef
31def handle_simple_model(data: SubItem) -> SubItem: 1abcdef
32 return data 1zABCDE
35@app.post("/simple-model-filter", response_model=SubItem) 1abcdef
36def handle_simple_model_filter(data: SubItem) -> Any: 1abcdef
37 extended_data = data.dict() 1mnopqr
38 extended_data.update({"secret_price": 42}) 1mnopqr
39 return extended_data 1mnopqr
42@app.post("/item") 1abcdef
43def handle_item(data: Item) -> Item: 1abcdef
44 return data 1FGHIJKLMNOPQ
47@app.post("/item-filter", response_model=Item) 1abcdef
48def handle_item_filter(data: Item) -> Any: 1abcdef
49 extended_data = data.dict() 1ghijkl
50 extended_data.update({"secret_data": "classified", "internal_id": 12345}) 1ghijkl
51 extended_data["sub"].update({"internal_id": 67890}) 1ghijkl
52 return extended_data 1ghijkl
55client = TestClient(app) 1abcdef
58def test_old_simple_model(): 1abcdef
59 response = client.post( 1zABCDE
60 "/simple-model",
61 json={"name": "Foo"},
62 )
63 assert response.status_code == 200, response.text 1zABCDE
64 assert response.json() == {"name": "Foo"} 1zABCDE
67def test_old_simple_model_validation_error(): 1abcdef
68 response = client.post( 1RSTUVW
69 "/simple-model",
70 json={"wrong_name": "Foo"},
71 )
72 assert response.status_code == 422, response.text 1RSTUVW
73 assert response.json() == snapshot( 1RSTUVW
74 {
75 "detail": [
76 {
77 "loc": ["body", "name"],
78 "msg": "field required",
79 "type": "value_error.missing",
80 }
81 ]
82 }
83 )
86def test_old_simple_model_filter(): 1abcdef
87 response = client.post( 1mnopqr
88 "/simple-model-filter",
89 json={"name": "Foo"},
90 )
91 assert response.status_code == 200, response.text 1mnopqr
92 assert response.json() == {"name": "Foo"} 1mnopqr
95def test_item_model(): 1abcdef
96 response = client.post( 1FHJLNP
97 "/item",
98 json={
99 "title": "Test Item",
100 "size": 100,
101 "description": "This is a test item",
102 "sub": {"name": "SubItem1"},
103 "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
104 },
105 )
106 assert response.status_code == 200, response.text 1FHJLNP
107 assert response.json() == { 1FHJLNP
108 "title": "Test Item",
109 "size": 100,
110 "description": "This is a test item",
111 "sub": {"name": "SubItem1"},
112 "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
113 }
116def test_item_model_minimal(): 1abcdef
117 response = client.post( 1GIKMOQ
118 "/item",
119 json={"title": "Minimal Item", "size": 50, "sub": {"name": "SubMin"}},
120 )
121 assert response.status_code == 200, response.text 1GIKMOQ
122 assert response.json() == { 1GIKMOQ
123 "title": "Minimal Item",
124 "size": 50,
125 "description": None,
126 "sub": {"name": "SubMin"},
127 "multi": [],
128 }
131def test_item_model_validation_errors(): 1abcdef
132 response = client.post( 1stuvwx
133 "/item",
134 json={"title": "Missing fields"},
135 )
136 assert response.status_code == 422, response.text 1stuvwx
137 error_detail = response.json()["detail"] 1stuvwx
138 assert len(error_detail) == 2 1stuvwx
139 assert { 1stuvwx
140 "loc": ["body", "size"],
141 "msg": "field required",
142 "type": "value_error.missing",
143 } in error_detail
144 assert { 1stuvwx
145 "loc": ["body", "sub"],
146 "msg": "field required",
147 "type": "value_error.missing",
148 } in error_detail
151def test_item_model_nested_validation_error(): 1abcdef
152 response = client.post( 1XYZ012
153 "/item",
154 json={"title": "Test Item", "size": 100, "sub": {"wrong_field": "test"}},
155 )
156 assert response.status_code == 422, response.text 1XYZ012
157 assert response.json() == snapshot( 1XYZ012
158 {
159 "detail": [
160 {
161 "loc": ["body", "sub", "name"],
162 "msg": "field required",
163 "type": "value_error.missing",
164 }
165 ]
166 }
167 )
170def test_item_model_invalid_type(): 1abcdef
171 response = client.post( 1345678
172 "/item",
173 json={"title": "Test Item", "size": "not_a_number", "sub": {"name": "SubItem"}},
174 )
175 assert response.status_code == 422, response.text 1345678
176 assert response.json() == snapshot( 1345678
177 {
178 "detail": [
179 {
180 "loc": ["body", "size"],
181 "msg": "value is not a valid integer",
182 "type": "type_error.integer",
183 }
184 ]
185 }
186 )
189def test_item_filter(): 1abcdef
190 response = client.post( 1ghijkl
191 "/item-filter",
192 json={
193 "title": "Filtered Item",
194 "size": 200,
195 "description": "Test filtering",
196 "sub": {"name": "SubFiltered"},
197 "multi": [],
198 },
199 )
200 assert response.status_code == 200, response.text 1ghijkl
201 result = response.json() 1ghijkl
202 assert result == { 1ghijkl
203 "title": "Filtered Item",
204 "size": 200,
205 "description": "Test filtering",
206 "sub": {"name": "SubFiltered"},
207 "multi": [],
208 }
209 assert "secret_data" not in result 1ghijkl
210 assert "internal_id" not in result 1ghijkl
213def test_openapi_schema(): 1abcdef
214 response = client.get("/openapi.json") 19!#$%'
215 assert response.status_code == 200, response.text 19!#$%'
216 assert response.json() == snapshot( 19!#$%'
217 {
218 "openapi": "3.1.0",
219 "info": {"title": "FastAPI", "version": "0.1.0"},
220 "paths": {
221 "/simple-model": {
222 "post": {
223 "summary": "Handle Simple Model",
224 "operationId": "handle_simple_model_simple_model_post",
225 "requestBody": {
226 "content": {
227 "application/json": {
228 "schema": pydantic_snapshot(
229 v2=snapshot(
230 {
231 "allOf": [
232 {
233 "$ref": "#/components/schemas/SubItem"
234 }
235 ],
236 "title": "Data",
237 }
238 ),
239 v1=snapshot(
240 {"$ref": "#/components/schemas/SubItem"}
241 ),
242 )
243 }
244 },
245 "required": True,
246 },
247 "responses": {
248 "200": {
249 "description": "Successful Response",
250 "content": {
251 "application/json": {
252 "schema": {
253 "$ref": "#/components/schemas/SubItem"
254 }
255 }
256 },
257 },
258 "422": {
259 "description": "Validation Error",
260 "content": {
261 "application/json": {
262 "schema": {
263 "$ref": "#/components/schemas/HTTPValidationError"
264 }
265 }
266 },
267 },
268 },
269 }
270 },
271 "/simple-model-filter": {
272 "post": {
273 "summary": "Handle Simple Model Filter",
274 "operationId": "handle_simple_model_filter_simple_model_filter_post",
275 "requestBody": {
276 "content": {
277 "application/json": {
278 "schema": pydantic_snapshot(
279 v2=snapshot(
280 {
281 "allOf": [
282 {
283 "$ref": "#/components/schemas/SubItem"
284 }
285 ],
286 "title": "Data",
287 }
288 ),
289 v1=snapshot(
290 {"$ref": "#/components/schemas/SubItem"}
291 ),
292 )
293 }
294 },
295 "required": True,
296 },
297 "responses": {
298 "200": {
299 "description": "Successful Response",
300 "content": {
301 "application/json": {
302 "schema": {
303 "$ref": "#/components/schemas/SubItem"
304 }
305 }
306 },
307 },
308 "422": {
309 "description": "Validation Error",
310 "content": {
311 "application/json": {
312 "schema": {
313 "$ref": "#/components/schemas/HTTPValidationError"
314 }
315 }
316 },
317 },
318 },
319 }
320 },
321 "/item": {
322 "post": {
323 "summary": "Handle Item",
324 "operationId": "handle_item_item_post",
325 "requestBody": {
326 "content": {
327 "application/json": {
328 "schema": pydantic_snapshot(
329 v2=snapshot(
330 {
331 "allOf": [
332 {
333 "$ref": "#/components/schemas/Item"
334 }
335 ],
336 "title": "Data",
337 }
338 ),
339 v1=snapshot(
340 {"$ref": "#/components/schemas/Item"}
341 ),
342 )
343 }
344 },
345 "required": True,
346 },
347 "responses": {
348 "200": {
349 "description": "Successful Response",
350 "content": {
351 "application/json": {
352 "schema": {"$ref": "#/components/schemas/Item"}
353 }
354 },
355 },
356 "422": {
357 "description": "Validation Error",
358 "content": {
359 "application/json": {
360 "schema": {
361 "$ref": "#/components/schemas/HTTPValidationError"
362 }
363 }
364 },
365 },
366 },
367 }
368 },
369 "/item-filter": {
370 "post": {
371 "summary": "Handle Item Filter",
372 "operationId": "handle_item_filter_item_filter_post",
373 "requestBody": {
374 "content": {
375 "application/json": {
376 "schema": pydantic_snapshot(
377 v2=snapshot(
378 {
379 "allOf": [
380 {
381 "$ref": "#/components/schemas/Item"
382 }
383 ],
384 "title": "Data",
385 }
386 ),
387 v1=snapshot(
388 {"$ref": "#/components/schemas/Item"}
389 ),
390 )
391 }
392 },
393 "required": True,
394 },
395 "responses": {
396 "200": {
397 "description": "Successful Response",
398 "content": {
399 "application/json": {
400 "schema": {"$ref": "#/components/schemas/Item"}
401 }
402 },
403 },
404 "422": {
405 "description": "Validation Error",
406 "content": {
407 "application/json": {
408 "schema": {
409 "$ref": "#/components/schemas/HTTPValidationError"
410 }
411 }
412 },
413 },
414 },
415 }
416 },
417 },
418 "components": {
419 "schemas": {
420 "HTTPValidationError": {
421 "properties": {
422 "detail": {
423 "items": {
424 "$ref": "#/components/schemas/ValidationError"
425 },
426 "type": "array",
427 "title": "Detail",
428 }
429 },
430 "type": "object",
431 "title": "HTTPValidationError",
432 },
433 "Item": {
434 "properties": {
435 "title": {"type": "string", "title": "Title"},
436 "size": {"type": "integer", "title": "Size"},
437 "description": {"type": "string", "title": "Description"},
438 "sub": {"$ref": "#/components/schemas/SubItem"},
439 "multi": {
440 "items": {"$ref": "#/components/schemas/SubItem"},
441 "type": "array",
442 "title": "Multi",
443 "default": [],
444 },
445 },
446 "type": "object",
447 "required": ["title", "size", "sub"],
448 "title": "Item",
449 },
450 "SubItem": {
451 "properties": {"name": {"type": "string", "title": "Name"}},
452 "type": "object",
453 "required": ["name"],
454 "title": "SubItem",
455 },
456 "ValidationError": {
457 "properties": {
458 "loc": {
459 "items": {
460 "anyOf": [{"type": "string"}, {"type": "integer"}]
461 },
462 "type": "array",
463 "title": "Location",
464 },
465 "msg": {"type": "string", "title": "Message"},
466 "type": {"type": "string", "title": "Error Type"},
467 },
468 "type": "object",
469 "required": ["loc", "msg", "type"],
470 "title": "ValidationError",
471 },
472 }
473 },
474 }
475 )