diff --git a/tests/test_pydantic_v1_v2_01.py b/tests/test_pydantic_v1_v2_01.py index d4548696c..ae893eeb5 100644 --- a/tests/test_pydantic_v1_v2_01.py +++ b/tests/test_pydantic_v1_v2_01.py @@ -1,9 +1,11 @@ from typing import Any, List, Union from fastapi import FastAPI +from fastapi._compat.v1 import BaseModel from fastapi.testclient import TestClient from inline_snapshot import snapshot -from pydantic.v1 import BaseModel + +from tests.utils import pydantic_snapshot class SubItem(BaseModel): @@ -94,7 +96,7 @@ def test_item_model(): "size": 100, "description": "This is a test item", "sub": {"name": "SubItem1"}, - "multi": [{"name": "Multi1"}, {"name": "Multi2"}] + "multi": [{"name": "Multi1"}, {"name": "Multi2"}], }, ) assert response.status_code == 200, response.text @@ -103,18 +105,14 @@ def test_item_model(): "size": 100, "description": "This is a test item", "sub": {"name": "SubItem1"}, - "multi": [{"name": "Multi1"}, {"name": "Multi2"}] + "multi": [{"name": "Multi1"}, {"name": "Multi2"}], } def test_item_model_minimal(): response = client.post( "/item", - json={ - "title": "Minimal Item", - "size": 50, - "sub": {"name": "SubMin"} - }, + json={"title": "Minimal Item", "size": 50, "sub": {"name": "SubMin"}}, ) assert response.status_code == 200, response.text assert response.json() == { @@ -122,32 +120,34 @@ def test_item_model_minimal(): "size": 50, "description": None, "sub": {"name": "SubMin"}, - "multi": [] + "multi": [], } def test_item_model_validation_errors(): response = client.post( "/item", - json={ - "title": "Missing fields" - }, + json={"title": "Missing fields"}, ) assert response.status_code == 422, response.text error_detail = response.json()["detail"] assert len(error_detail) == 2 - assert {"loc": ["body", "size"], "msg": "field required", "type": "value_error.missing"} in error_detail - assert {"loc": ["body", "sub"], "msg": "field required", "type": "value_error.missing"} in error_detail + assert { + "loc": ["body", "size"], + "msg": "field required", + "type": "value_error.missing", + } in error_detail + assert { + "loc": ["body", "sub"], + "msg": "field required", + "type": "value_error.missing", + } in error_detail def test_item_model_nested_validation_error(): response = client.post( "/item", - json={ - "title": "Test Item", - "size": 100, - "sub": {"wrong_field": "test"} - }, + json={"title": "Test Item", "size": 100, "sub": {"wrong_field": "test"}}, ) assert response.status_code == 422, response.text assert response.json() == snapshot( @@ -166,11 +166,7 @@ def test_item_model_nested_validation_error(): def test_item_model_invalid_type(): response = client.post( "/item", - json={ - "title": "Test Item", - "size": "not_a_number", - "sub": {"name": "SubItem"} - }, + json={"title": "Test Item", "size": "not_a_number", "sub": {"name": "SubItem"}}, ) assert response.status_code == 422, response.text assert response.json() == snapshot( @@ -194,7 +190,7 @@ def test_item_filter(): "size": 200, "description": "Test filtering", "sub": {"name": "SubFiltered"}, - "multi": [] + "multi": [], }, ) assert response.status_code == 200, response.text @@ -204,7 +200,7 @@ def test_item_filter(): "size": 200, "description": "Test filtering", "sub": {"name": "SubFiltered"}, - "multi": [] + "multi": [], } assert "secret_data" not in result assert "internal_id" not in result @@ -217,143 +213,168 @@ def test_openapi_schema(): { "openapi": "3.1.0", "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": {"/simple-model": { - "post": { - "summary": "Handle Simple Model", - "operationId": "handle_simple_model_simple_model_post", - "requestBody": { - "content": { - "application/json": { - "schema": { - "allOf": [{"$ref": "#/components/schemas/SubItem"}], - "title": "Data", - } - } - }, - "required": True, - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/SubItem"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/HTTPValidationError"} - } - }, - }, - }, - } -}, "/simple-model-filter": { - "post": { - "summary": "Handle Simple Model Filter", - "operationId": "handle_simple_model_filter_simple_model_filter_post", - "requestBody": { - "content": { - "application/json": { - "schema": { - "allOf": [{"$ref": "#/components/schemas/SubItem"}], - "title": "Data", - } - } - }, - "required": True, - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/SubItem"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/HTTPValidationError"} - } - }, - }, - }, - } -}, "/item": { - "post": { - "summary": "Handle Item", - "operationId": "handle_item_item_post", - "requestBody": { - "content": { - "application/json": { - "schema": { - "allOf": [{"$ref": "#/components/schemas/Item"}], - "title": "Data", - } - } - }, - "required": True, - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} + "paths": { + "/simple-model": { + "post": { + "summary": "Handle Simple Model", + "operationId": "handle_simple_model_simple_model_post", + "requestBody": { + "content": { + "application/json": { + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"$ref": "#/components/schemas/SubItem"} + ), + ) + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubItem" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, } }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/HTTPValidationError"} + "/simple-model-filter": { + "post": { + "summary": "Handle Simple Model Filter", + "operationId": "handle_simple_model_filter_simple_model_filter_post", + "requestBody": { + "content": { + "application/json": { + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"$ref": "#/components/schemas/SubItem"} + ), + ) + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubItem" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, } }, - }, - }, - } -}, "/item-filter": { - "post": { - "summary": "Handle Item Filter", - "operationId": "handle_item_filter_item_filter_post", - "requestBody": { - "content": { - "application/json": { - "schema": { - "allOf": [{"$ref": "#/components/schemas/Item"}], - "title": "Data", - } - } - }, - "required": True, - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} + "/item": { + "post": { + "summary": "Handle Item", + "operationId": "handle_item_item_post", + "requestBody": { + "content": { + "application/json": { + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"$ref": "#/components/schemas/Item"} + ), + ) + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, } }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/HTTPValidationError"} + "/item-filter": { + "post": { + "summary": "Handle Item Filter", + "operationId": "handle_item_filter_item_filter_post", + "requestBody": { + "content": { + "application/json": { + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"$ref": "#/components/schemas/Item"} + ), + ) + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, } }, }, - }, - } -}}, "components": { "schemas": { "HTTPValidationError": { @@ -368,23 +389,25 @@ def test_openapi_schema(): }, "type": "object", "title": "HTTPValidationError", - }, "Item": { - "properties": { - "title": {"type": "string", "title": "Title"}, - "size": {"type": "integer", "title": "Size"}, - "description": {"type": "string", "title": "Description"}, - "sub": {"$ref": "#/components/schemas/SubItem"}, - "multi": { - "items": {"$ref": "#/components/schemas/SubItem"}, - "type": "array", - "title": "Multi", - "default": [], - }, - }, - "type": "object", - "required": ["title", "size", "sub"], - "title": "Item", -}, "SubItem": { + }, + "Item": { + "properties": { + "title": {"type": "string", "title": "Title"}, + "size": {"type": "integer", "title": "Size"}, + "description": {"type": "string", "title": "Description"}, + "sub": {"$ref": "#/components/schemas/SubItem"}, + "multi": { + "items": {"$ref": "#/components/schemas/SubItem"}, + "type": "array", + "title": "Multi", + "default": [], + }, + }, + "type": "object", + "required": ["title", "size", "sub"], + "title": "Item", + }, + "SubItem": { "properties": {"name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], diff --git a/tests/test_pydantic_v1_v2_list.py b/tests/test_pydantic_v1_v2_list.py index 625ef1aae..73a50b3b4 100644 --- a/tests/test_pydantic_v1_v2_list.py +++ b/tests/test_pydantic_v1_v2_list.py @@ -1,9 +1,11 @@ from typing import Any, List, Union from fastapi import FastAPI +from fastapi._compat.v1 import BaseModel from fastapi.testclient import TestClient from inline_snapshot import snapshot -from pydantic.v1 import BaseModel + +from tests.utils import pydantic_snapshot class SubItem(BaseModel): @@ -348,12 +350,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, @@ -393,12 +395,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, diff --git a/tests/test_pydantic_v1_v2_mixed.py b/tests/test_pydantic_v1_v2_mixed.py index e8506069a..cce1d873b 100644 --- a/tests/test_pydantic_v1_v2_mixed.py +++ b/tests/test_pydantic_v1_v2_mixed.py @@ -1,10 +1,12 @@ from typing import Any, List, Union from fastapi import FastAPI +from fastapi._compat.v1 import BaseModel from fastapi.testclient import TestClient from inline_snapshot import snapshot from pydantic import BaseModel as NewBaseModel -from pydantic.v1 import BaseModel + +from tests.utils import pydantic_snapshot class SubItem(BaseModel): @@ -662,20 +664,23 @@ def test_v2_to_v1_validation_error(): assert response.status_code == 422, response.text assert response.json() == snapshot( { - "detail": [ - { - "type": "missing", - "loc": ["body", "new_size"], - "msg": "Field required", - "input": {"new_title": "Missing fields"}, - }, - { - "type": "missing", - "loc": ["body", "new_sub"], - "msg": "Field required", - "input": {"new_title": "Missing fields"}, - }, - ] + "detail": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + [ + { + "loc": ["body", "new_size"], + "msg": "field required", + "type": "value_error.missing", + }, + { + "loc": ["body", "new_sub"], + "msg": "field required", + "type": "value_error.missing", + }, + ] + ), + ) } ) @@ -693,12 +698,16 @@ def test_v2_to_v1_nested_validation_error(): assert response.json() == snapshot( { "detail": [ - { - "type": "missing", - "loc": ["body", "new_sub", "new_sub_name"], - "msg": "Field required", - "input": {"wrong_field": "value"}, - } + pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + { + "loc": ["body", "new_sub", "new_sub_name"], + "msg": "field required", + "type": "value_error.missing", + } + ), + ) ] } ) @@ -742,20 +751,23 @@ def test_v2_list_validation_error(): assert response.status_code == 422, response.text assert response.json() == snapshot( { - "detail": [ - { - "type": "missing", - "loc": ["body", 1, "new_size"], - "msg": "Field required", - "input": {"new_title": "Invalid"}, - }, - { - "type": "missing", - "loc": ["body", 1, "new_sub"], - "msg": "Field required", - "input": {"new_title": "Invalid"}, - }, - ] + "detail": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + [ + { + "loc": ["body", 1, "new_size"], + "msg": "field required", + "type": "value_error.missing", + }, + { + "loc": ["body", 1, "new_sub"], + "msg": "field required", + "type": "value_error.missing", + }, + ] + ), + ) } ) @@ -791,18 +803,18 @@ def test_invalid_list_structure_v2(): assert response.status_code == 422, response.text assert response.json() == snapshot( { - "detail": [ - { - "type": "list_type", - "loc": ["body"], - "msg": "Input should be a valid list", - "input": { - "new_title": "Not a list", - "new_size": 100, - "new_sub": {"new_sub_name": "Sub"}, - }, - } - ] + "detail": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + [ + { + "loc": ["body"], + "msg": "value is not a valid list", + "type": "type_error.list", + } + ] + ), + ) } ) @@ -822,12 +834,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, @@ -863,12 +875,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, @@ -972,12 +984,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, @@ -1347,10 +1359,12 @@ def test_openapi_schema(): "properties": { "new_title": {"type": "string", "title": "New Title"}, "new_size": {"type": "integer", "title": "New Size"}, - "new_description": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "title": "New Description", - }, + "new_description": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"type": "string", "title": "New Description"} + ), + ), "new_sub": {"$ref": "#/components/schemas/NewSubItem"}, "new_multi": { "items": {"$ref": "#/components/schemas/NewSubItem"}, diff --git a/tests/test_pydantic_v1_v2_noneable.py b/tests/test_pydantic_v1_v2_noneable.py index 4e2adcb37..9e1f1fce1 100644 --- a/tests/test_pydantic_v1_v2_noneable.py +++ b/tests/test_pydantic_v1_v2_noneable.py @@ -1,10 +1,12 @@ from typing import Any, List, Union from fastapi import FastAPI +from fastapi._compat.v1 import BaseModel from fastapi.testclient import TestClient from inline_snapshot import snapshot from pydantic import BaseModel as NewBaseModel -from pydantic.v1 import BaseModel + +from tests.utils import pydantic_snapshot class SubItem(BaseModel): @@ -306,20 +308,23 @@ def test_v2_to_v1_validation_error(): assert response.status_code == 422, response.text assert response.json() == snapshot( { - "detail": [ - { - "type": "missing", - "loc": ["body", "new_size"], - "msg": "Field required", - "input": {"new_title": "Missing fields"}, - }, - { - "type": "missing", - "loc": ["body", "new_sub"], - "msg": "Field required", - "input": {"new_title": "Missing fields"}, - }, - ] + "detail": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + [ + { + "loc": ["body", "new_size"], + "msg": "field required", + "type": "value_error.missing", + }, + { + "loc": ["body", "new_sub"], + "msg": "field required", + "type": "value_error.missing", + }, + ] + ), + ) } ) @@ -337,12 +342,16 @@ def test_v2_to_v1_nested_validation_error(): assert response.json() == snapshot( { "detail": [ - { - "type": "missing", - "loc": ["body", "new_sub", "new_sub_name"], - "msg": "Field required", - "input": {"wrong_field": "value"}, - } + pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + { + "loc": ["body", "new_sub", "new_sub_name"], + "msg": "field required", + "type": "value_error.missing", + } + ), + ) ] } ) @@ -361,12 +370,16 @@ def test_v2_to_v1_type_validation_error(): assert response.json() == snapshot( { "detail": [ - { - "type": "int_parsing", - "loc": ["body", "new_size"], - "msg": "Input should be a valid integer, unable to parse string as an integer", - "input": "not_a_number", - } + pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + { + "loc": ["body", "new_size"], + "msg": "value is not a valid integer", + "type": "type_error.integer", + } + ), + ) ] } ) @@ -437,12 +450,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, @@ -452,15 +465,12 @@ def test_openapi_schema(): "description": "Successful Response", "content": { "application/json": { - "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/NewItem" - }, - {"type": "null"}, - ], - "title": "Response Handle V1 Item To V2 V1 To V2 Post", - } + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"$ref": "#/components/schemas/NewItem"} + ), + ) } }, }, @@ -484,12 +494,12 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": { - "allOf": [ + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( {"$ref": "#/components/schemas/Item"} - ], - "title": "Data", - } + ), + ) } }, "required": True, @@ -499,15 +509,12 @@ def test_openapi_schema(): "description": "Successful Response", "content": { "application/json": { - "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/NewItem" - }, - {"type": "null"}, - ], - "title": "Response Handle V1 Item To V2 Filter V1 To V2 Item Filter Post", - } + "schema": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"$ref": "#/components/schemas/NewItem"} + ), + ) } }, }, @@ -629,10 +636,12 @@ def test_openapi_schema(): "properties": { "new_title": {"type": "string", "title": "New Title"}, "new_size": {"type": "integer", "title": "New Size"}, - "new_description": { - "anyOf": [{"type": "string"}, {"type": "null"}], - "title": "New Description", - }, + "new_description": pydantic_snapshot( + v2=snapshot(), + v1=snapshot( + {"type": "string", "title": "New Description"} + ), + ), "new_sub": {"$ref": "#/components/schemas/NewSubItem"}, "new_multi": { "items": {"$ref": "#/components/schemas/NewSubItem"},