Coverage for tests/test_tuples.py: 100%
58 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 List, Tuple 1abcde
3from dirty_equals import IsDict 1abcde
4from fastapi import FastAPI, Form 1abcde
5from fastapi.testclient import TestClient 1abcde
6from pydantic import BaseModel 1abcde
8app = FastAPI() 1abcde
11class ItemGroup(BaseModel): 1abcde
12 items: List[Tuple[str, str]] 1abcde
15class Coordinate(BaseModel): 1abcde
16 x: float 1abcde
17 y: float 1abcde
20@app.post("/model-with-tuple/") 1abcde
21def post_model_with_tuple(item_group: ItemGroup): 1abcde
22 return item_group 1abcde
25@app.post("/tuple-of-models/") 1abcde
26def post_tuple_of_models(square: Tuple[Coordinate, Coordinate]): 1abcde
27 return square 1abcde
30@app.post("/tuple-form/") 1abcde
31def hello(values: Tuple[int, int] = Form()): 1abcde
32 return values 1abcde
35client = TestClient(app) 1abcde
38def test_model_with_tuple_valid(): 1abcde
39 data = {"items": [["foo", "bar"], ["baz", "whatelse"]]} 1abcde
40 response = client.post("/model-with-tuple/", json=data) 1abcde
41 assert response.status_code == 200, response.text 1abcde
42 assert response.json() == data 1abcde
45def test_model_with_tuple_invalid(): 1abcde
46 data = {"items": [["foo", "bar"], ["baz", "whatelse", "too", "much"]]} 1abcde
47 response = client.post("/model-with-tuple/", json=data) 1abcde
48 assert response.status_code == 422, response.text 1abcde
50 data = {"items": [["foo", "bar"], ["baz"]]} 1abcde
51 response = client.post("/model-with-tuple/", json=data) 1abcde
52 assert response.status_code == 422, response.text 1abcde
55def test_tuple_with_model_valid(): 1abcde
56 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}] 1abcde
57 response = client.post("/tuple-of-models/", json=data) 1abcde
58 assert response.status_code == 200, response.text 1abcde
59 assert response.json() == data 1abcde
62def test_tuple_with_model_invalid(): 1abcde
63 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}] 1abcde
64 response = client.post("/tuple-of-models/", json=data) 1abcde
65 assert response.status_code == 422, response.text 1abcde
67 data = [{"x": 1, "y": 2}] 1abcde
68 response = client.post("/tuple-of-models/", json=data) 1abcde
69 assert response.status_code == 422, response.text 1abcde
72def test_tuple_form_valid(): 1abcde
73 response = client.post("/tuple-form/", data={"values": ("1", "2")}) 1abcde
74 assert response.status_code == 200, response.text 1abcde
75 assert response.json() == [1, 2] 1abcde
78def test_tuple_form_invalid(): 1abcde
79 response = client.post("/tuple-form/", data={"values": ("1", "2", "3")}) 1abcde
80 assert response.status_code == 422, response.text 1abcde
82 response = client.post("/tuple-form/", data={"values": ("1")}) 1abcde
83 assert response.status_code == 422, response.text 1abcde
86def test_openapi_schema(): 1abcde
87 response = client.get("/openapi.json") 1abcde
88 assert response.status_code == 200, response.text 1abcde
89 assert response.json() == { 1abcde
90 "openapi": "3.1.0",
91 "info": {"title": "FastAPI", "version": "0.1.0"},
92 "paths": {
93 "/model-with-tuple/": {
94 "post": {
95 "summary": "Post Model With Tuple",
96 "operationId": "post_model_with_tuple_model_with_tuple__post",
97 "requestBody": {
98 "content": {
99 "application/json": {
100 "schema": {"$ref": "#/components/schemas/ItemGroup"}
101 }
102 },
103 "required": True,
104 },
105 "responses": {
106 "200": {
107 "description": "Successful Response",
108 "content": {"application/json": {"schema": {}}},
109 },
110 "422": {
111 "description": "Validation Error",
112 "content": {
113 "application/json": {
114 "schema": {
115 "$ref": "#/components/schemas/HTTPValidationError"
116 }
117 }
118 },
119 },
120 },
121 }
122 },
123 "/tuple-of-models/": {
124 "post": {
125 "summary": "Post Tuple Of Models",
126 "operationId": "post_tuple_of_models_tuple_of_models__post",
127 "requestBody": {
128 "content": {
129 "application/json": {
130 "schema": IsDict(
131 {
132 "title": "Square",
133 "maxItems": 2,
134 "minItems": 2,
135 "type": "array",
136 "prefixItems": [
137 {"$ref": "#/components/schemas/Coordinate"},
138 {"$ref": "#/components/schemas/Coordinate"},
139 ],
140 }
141 )
142 | IsDict(
143 # TODO: remove when deprecating Pydantic v1
144 {
145 "title": "Square",
146 "maxItems": 2,
147 "minItems": 2,
148 "type": "array",
149 "items": [
150 {"$ref": "#/components/schemas/Coordinate"},
151 {"$ref": "#/components/schemas/Coordinate"},
152 ],
153 }
154 )
155 }
156 },
157 "required": True,
158 },
159 "responses": {
160 "200": {
161 "description": "Successful Response",
162 "content": {"application/json": {"schema": {}}},
163 },
164 "422": {
165 "description": "Validation Error",
166 "content": {
167 "application/json": {
168 "schema": {
169 "$ref": "#/components/schemas/HTTPValidationError"
170 }
171 }
172 },
173 },
174 },
175 }
176 },
177 "/tuple-form/": {
178 "post": {
179 "summary": "Hello",
180 "operationId": "hello_tuple_form__post",
181 "requestBody": {
182 "content": {
183 "application/x-www-form-urlencoded": {
184 "schema": {
185 "$ref": "#/components/schemas/Body_hello_tuple_form__post"
186 }
187 }
188 },
189 "required": True,
190 },
191 "responses": {
192 "200": {
193 "description": "Successful Response",
194 "content": {"application/json": {"schema": {}}},
195 },
196 "422": {
197 "description": "Validation Error",
198 "content": {
199 "application/json": {
200 "schema": {
201 "$ref": "#/components/schemas/HTTPValidationError"
202 }
203 }
204 },
205 },
206 },
207 }
208 },
209 },
210 "components": {
211 "schemas": {
212 "Body_hello_tuple_form__post": {
213 "title": "Body_hello_tuple_form__post",
214 "required": ["values"],
215 "type": "object",
216 "properties": {
217 "values": IsDict(
218 {
219 "title": "Values",
220 "maxItems": 2,
221 "minItems": 2,
222 "type": "array",
223 "prefixItems": [
224 {"type": "integer"},
225 {"type": "integer"},
226 ],
227 }
228 )
229 | IsDict(
230 # TODO: remove when deprecating Pydantic v1
231 {
232 "title": "Values",
233 "maxItems": 2,
234 "minItems": 2,
235 "type": "array",
236 "items": [{"type": "integer"}, {"type": "integer"}],
237 }
238 )
239 },
240 },
241 "Coordinate": {
242 "title": "Coordinate",
243 "required": ["x", "y"],
244 "type": "object",
245 "properties": {
246 "x": {"title": "X", "type": "number"},
247 "y": {"title": "Y", "type": "number"},
248 },
249 },
250 "HTTPValidationError": {
251 "title": "HTTPValidationError",
252 "type": "object",
253 "properties": {
254 "detail": {
255 "title": "Detail",
256 "type": "array",
257 "items": {"$ref": "#/components/schemas/ValidationError"},
258 }
259 },
260 },
261 "ItemGroup": {
262 "title": "ItemGroup",
263 "required": ["items"],
264 "type": "object",
265 "properties": {
266 "items": {
267 "title": "Items",
268 "type": "array",
269 "items": IsDict(
270 {
271 "maxItems": 2,
272 "minItems": 2,
273 "type": "array",
274 "prefixItems": [
275 {"type": "string"},
276 {"type": "string"},
277 ],
278 }
279 )
280 | IsDict(
281 # TODO: remove when deprecating Pydantic v1
282 {
283 "maxItems": 2,
284 "minItems": 2,
285 "type": "array",
286 "items": [{"type": "string"}, {"type": "string"}],
287 }
288 ),
289 }
290 },
291 },
292 "ValidationError": {
293 "title": "ValidationError",
294 "required": ["loc", "msg", "type"],
295 "type": "object",
296 "properties": {
297 "loc": {
298 "title": "Location",
299 "type": "array",
300 "items": {
301 "anyOf": [{"type": "string"}, {"type": "integer"}]
302 },
303 },
304 "msg": {"title": "Message", "type": "string"},
305 "type": {"title": "Error Type", "type": "string"},
306 },
307 },
308 }
309 },
310 }