Coverage for tests / test_tuples.py: 100%
57 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1from fastapi import FastAPI, Form 1abcd
2from fastapi.testclient import TestClient 1abcd
3from inline_snapshot import snapshot 1abcd
4from pydantic import BaseModel 1abcd
6app = FastAPI() 1abcd
9class ItemGroup(BaseModel): 1abcd
10 items: list[tuple[str, str]] 1abcd
13class Coordinate(BaseModel): 1abcd
14 x: float 1abcd
15 y: float 1abcd
18@app.post("/model-with-tuple/") 1abcd
19def post_model_with_tuple(item_group: ItemGroup): 1abcd
20 return item_group 1klm
23@app.post("/tuple-of-models/") 1abcd
24def post_tuple_of_models(square: tuple[Coordinate, Coordinate]): 1abcd
25 return square 1nop
28@app.post("/tuple-form/") 1abcd
29def hello(values: tuple[int, int] = Form()): 1abcd
30 return values 1qrs
33client = TestClient(app) 1abcd
36def test_model_with_tuple_valid(): 1abcd
37 data = {"items": [["foo", "bar"], ["baz", "whatelse"]]} 1klm
38 response = client.post("/model-with-tuple/", json=data) 1klm
39 assert response.status_code == 200, response.text 1klm
40 assert response.json() == data 1klm
43def test_model_with_tuple_invalid(): 1abcd
44 data = {"items": [["foo", "bar"], ["baz", "whatelse", "too", "much"]]} 1efg
45 response = client.post("/model-with-tuple/", json=data) 1efg
46 assert response.status_code == 422, response.text 1efg
48 data = {"items": [["foo", "bar"], ["baz"]]} 1efg
49 response = client.post("/model-with-tuple/", json=data) 1efg
50 assert response.status_code == 422, response.text 1efg
53def test_tuple_with_model_valid(): 1abcd
54 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}] 1nop
55 response = client.post("/tuple-of-models/", json=data) 1nop
56 assert response.status_code == 200, response.text 1nop
57 assert response.json() == data 1nop
60def test_tuple_with_model_invalid(): 1abcd
61 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}] 1hij
62 response = client.post("/tuple-of-models/", json=data) 1hij
63 assert response.status_code == 422, response.text 1hij
65 data = [{"x": 1, "y": 2}] 1hij
66 response = client.post("/tuple-of-models/", json=data) 1hij
67 assert response.status_code == 422, response.text 1hij
70def test_tuple_form_valid(): 1abcd
71 response = client.post("/tuple-form/", data={"values": ("1", "2")}) 1qrs
72 assert response.status_code == 200, response.text 1qrs
73 assert response.json() == [1, 2] 1qrs
76def test_tuple_form_invalid(): 1abcd
77 response = client.post("/tuple-form/", data={"values": ("1", "2", "3")}) 1tuv
78 assert response.status_code == 422, response.text 1tuv
80 response = client.post("/tuple-form/", data={"values": ("1")}) 1tuv
81 assert response.status_code == 422, response.text 1tuv
84def test_openapi_schema(): 1abcd
85 response = client.get("/openapi.json") 1wxy
86 assert response.status_code == 200, response.text 1wxy
87 assert response.json() == snapshot( 1wxy
88 {
89 "openapi": "3.1.0",
90 "info": {"title": "FastAPI", "version": "0.1.0"},
91 "paths": {
92 "/model-with-tuple/": {
93 "post": {
94 "summary": "Post Model With Tuple",
95 "operationId": "post_model_with_tuple_model_with_tuple__post",
96 "requestBody": {
97 "content": {
98 "application/json": {
99 "schema": {"$ref": "#/components/schemas/ItemGroup"}
100 }
101 },
102 "required": True,
103 },
104 "responses": {
105 "200": {
106 "description": "Successful Response",
107 "content": {"application/json": {"schema": {}}},
108 },
109 "422": {
110 "description": "Validation Error",
111 "content": {
112 "application/json": {
113 "schema": {
114 "$ref": "#/components/schemas/HTTPValidationError"
115 }
116 }
117 },
118 },
119 },
120 }
121 },
122 "/tuple-of-models/": {
123 "post": {
124 "summary": "Post Tuple Of Models",
125 "operationId": "post_tuple_of_models_tuple_of_models__post",
126 "requestBody": {
127 "content": {
128 "application/json": {
129 "schema": {
130 "title": "Square",
131 "maxItems": 2,
132 "minItems": 2,
133 "type": "array",
134 "prefixItems": [
135 {"$ref": "#/components/schemas/Coordinate"},
136 {"$ref": "#/components/schemas/Coordinate"},
137 ],
138 }
139 }
140 },
141 "required": True,
142 },
143 "responses": {
144 "200": {
145 "description": "Successful Response",
146 "content": {"application/json": {"schema": {}}},
147 },
148 "422": {
149 "description": "Validation Error",
150 "content": {
151 "application/json": {
152 "schema": {
153 "$ref": "#/components/schemas/HTTPValidationError"
154 }
155 }
156 },
157 },
158 },
159 }
160 },
161 "/tuple-form/": {
162 "post": {
163 "summary": "Hello",
164 "operationId": "hello_tuple_form__post",
165 "requestBody": {
166 "content": {
167 "application/x-www-form-urlencoded": {
168 "schema": {
169 "$ref": "#/components/schemas/Body_hello_tuple_form__post"
170 }
171 }
172 },
173 "required": True,
174 },
175 "responses": {
176 "200": {
177 "description": "Successful Response",
178 "content": {"application/json": {"schema": {}}},
179 },
180 "422": {
181 "description": "Validation Error",
182 "content": {
183 "application/json": {
184 "schema": {
185 "$ref": "#/components/schemas/HTTPValidationError"
186 }
187 }
188 },
189 },
190 },
191 }
192 },
193 },
194 "components": {
195 "schemas": {
196 "Body_hello_tuple_form__post": {
197 "title": "Body_hello_tuple_form__post",
198 "required": ["values"],
199 "type": "object",
200 "properties": {
201 "values": {
202 "title": "Values",
203 "maxItems": 2,
204 "minItems": 2,
205 "type": "array",
206 "prefixItems": [
207 {"type": "integer"},
208 {"type": "integer"},
209 ],
210 }
211 },
212 },
213 "Coordinate": {
214 "title": "Coordinate",
215 "required": ["x", "y"],
216 "type": "object",
217 "properties": {
218 "x": {"title": "X", "type": "number"},
219 "y": {"title": "Y", "type": "number"},
220 },
221 },
222 "HTTPValidationError": {
223 "title": "HTTPValidationError",
224 "type": "object",
225 "properties": {
226 "detail": {
227 "title": "Detail",
228 "type": "array",
229 "items": {
230 "$ref": "#/components/schemas/ValidationError"
231 },
232 }
233 },
234 },
235 "ItemGroup": {
236 "title": "ItemGroup",
237 "required": ["items"],
238 "type": "object",
239 "properties": {
240 "items": {
241 "title": "Items",
242 "type": "array",
243 "items": {
244 "maxItems": 2,
245 "minItems": 2,
246 "type": "array",
247 "prefixItems": [
248 {"type": "string"},
249 {"type": "string"},
250 ],
251 },
252 }
253 },
254 },
255 "ValidationError": {
256 "title": "ValidationError",
257 "required": ["loc", "msg", "type"],
258 "type": "object",
259 "properties": {
260 "loc": {
261 "title": "Location",
262 "type": "array",
263 "items": {
264 "anyOf": [{"type": "string"}, {"type": "integer"}]
265 },
266 },
267 "msg": {"title": "Message", "type": "string"},
268 "type": {"title": "Error Type", "type": "string"},
269 "input": {"title": "Input"},
270 "ctx": {"title": "Context", "type": "object"},
271 },
272 },
273 }
274 },
275 }
276 )