Coverage for tests/test_filter_pydantic_sub_model_pv2.py: 100%
46 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1from typing import Optional 1abcde
3import pytest 1abcde
4from dirty_equals import HasRepr, IsDict, IsOneOf 1abcde
5from fastapi import Depends, FastAPI 1abcde
6from fastapi.exceptions import ResponseValidationError 1abcde
7from fastapi.testclient import TestClient 1abcde
9from .utils import needs_pydanticv2 1abcde
12@pytest.fixture(name="client") 1abcde
13def get_client(): 1abcde
14 from pydantic import BaseModel, ValidationInfo, field_validator 1abcde
16 app = FastAPI() 1abcde
18 class ModelB(BaseModel): 1abcde
19 username: str 1abcde
21 class ModelC(ModelB): 1abcde
22 password: str 1abcde
24 class ModelA(BaseModel): 1abcde
25 name: str 1abcde
26 description: Optional[str] = None 1abcde
27 foo: ModelB 1abcde
29 @field_validator("name") 1abcde
30 def lower_username(cls, name: str, info: ValidationInfo): 1abcde
31 if not name.endswith("A"): 1abcde
32 raise ValueError("name must end in A") 1abcde
33 return name 1abcde
35 async def get_model_c() -> ModelC: 1abcde
36 return ModelC(username="test-user", password="test-password") 1abcde
38 @app.get("/model/{name}", response_model=ModelA) 1abcde
39 async def get_model_a(name: str, model_c=Depends(get_model_c)): 1abcde
40 return {"name": name, "description": "model-a-desc", "foo": model_c} 1abcde
42 client = TestClient(app) 1abcde
43 return client 1abcde
46@needs_pydanticv2 1abcde
47def test_filter_sub_model(client: TestClient): 1abcde
48 response = client.get("/model/modelA") 1abcde
49 assert response.status_code == 200, response.text 1abcde
50 assert response.json() == { 1abcde
51 "name": "modelA",
52 "description": "model-a-desc",
53 "foo": {"username": "test-user"},
54 }
57@needs_pydanticv2 1abcde
58def test_validator_is_cloned(client: TestClient): 1abcde
59 with pytest.raises(ResponseValidationError) as err: 1abcde
60 client.get("/model/modelX") 1abcde
61 assert err.value.errors() == [ 1abcde
62 IsDict(
63 {
64 "type": "value_error",
65 "loc": ("response", "name"),
66 "msg": "Value error, name must end in A",
67 "input": "modelX",
68 "ctx": {"error": HasRepr("ValueError('name must end in A')")},
69 }
70 )
71 | IsDict(
72 # TODO remove when deprecating Pydantic v1
73 {
74 "loc": ("response", "name"),
75 "msg": "name must end in A",
76 "type": "value_error",
77 }
78 )
79 ]
82@needs_pydanticv2 1abcde
83def test_openapi_schema(client: TestClient): 1abcde
84 response = client.get("/openapi.json") 1abcde
85 assert response.status_code == 200, response.text 1abcde
86 assert response.json() == { 1abcde
87 "openapi": "3.1.0",
88 "info": {"title": "FastAPI", "version": "0.1.0"},
89 "paths": {
90 "/model/{name}": {
91 "get": {
92 "summary": "Get Model A",
93 "operationId": "get_model_a_model__name__get",
94 "parameters": [
95 {
96 "required": True,
97 "schema": {"title": "Name", "type": "string"},
98 "name": "name",
99 "in": "path",
100 }
101 ],
102 "responses": {
103 "200": {
104 "description": "Successful Response",
105 "content": {
106 "application/json": {
107 "schema": {"$ref": "#/components/schemas/ModelA"}
108 }
109 },
110 },
111 "422": {
112 "description": "Validation Error",
113 "content": {
114 "application/json": {
115 "schema": {
116 "$ref": "#/components/schemas/HTTPValidationError"
117 }
118 }
119 },
120 },
121 },
122 }
123 }
124 },
125 "components": {
126 "schemas": {
127 "HTTPValidationError": {
128 "title": "HTTPValidationError",
129 "type": "object",
130 "properties": {
131 "detail": {
132 "title": "Detail",
133 "type": "array",
134 "items": {"$ref": "#/components/schemas/ValidationError"},
135 }
136 },
137 },
138 "ModelA": {
139 "title": "ModelA",
140 "required": IsOneOf(
141 ["name", "description", "foo"],
142 # TODO remove when deprecating Pydantic v1
143 ["name", "foo"],
144 ),
145 "type": "object",
146 "properties": {
147 "name": {"title": "Name", "type": "string"},
148 "description": IsDict(
149 {
150 "title": "Description",
151 "anyOf": [{"type": "string"}, {"type": "null"}],
152 }
153 )
154 |
155 # TODO remove when deprecating Pydantic v1
156 IsDict({"title": "Description", "type": "string"}),
157 "foo": {"$ref": "#/components/schemas/ModelB"},
158 },
159 },
160 "ModelB": {
161 "title": "ModelB",
162 "required": ["username"],
163 "type": "object",
164 "properties": {"username": {"title": "Username", "type": "string"}},
165 },
166 "ValidationError": {
167 "title": "ValidationError",
168 "required": ["loc", "msg", "type"],
169 "type": "object",
170 "properties": {
171 "loc": {
172 "title": "Location",
173 "type": "array",
174 "items": {
175 "anyOf": [{"type": "string"}, {"type": "integer"}]
176 },
177 },
178 "msg": {"title": "Message", "type": "string"},
179 "type": {"title": "Error Type", "type": "string"},
180 },
181 },
182 }
183 },
184 }