Coverage for tests / test_tuples.py: 100%
57 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1from fastapi import FastAPI, Form 1adbc
2from fastapi.testclient import TestClient 1adbc
3from inline_snapshot import snapshot 1adbc
4from pydantic import BaseModel 1adbc
6app = FastAPI() 1adbc
9class ItemGroup(BaseModel): 1adbc
10 items: list[tuple[str, str]] 1abc
13class Coordinate(BaseModel): 1adbc
14 x: float 1abc
15 y: float 1abc
18@app.post("/model-with-tuple/") 1adbc
19def post_model_with_tuple(item_group: ItemGroup): 1adbc
20 return item_group 1mnop
23@app.post("/tuple-of-models/") 1adbc
24def post_tuple_of_models(square: tuple[Coordinate, Coordinate]): 1adbc
25 return square 1qrst
28@app.post("/tuple-form/") 1adbc
29def hello(values: tuple[int, int] = Form()): 1adbc
30 return values 1uvwx
33client = TestClient(app) 1adbc
36def test_model_with_tuple_valid(): 1adbc
37 data = {"items": [["foo", "bar"], ["baz", "whatelse"]]} 1mnop
38 response = client.post("/model-with-tuple/", json=data) 1mnop
39 assert response.status_code == 200, response.text 1mnop
40 assert response.json() == data 1mnop
43def test_model_with_tuple_invalid(): 1adbc
44 data = {"items": [["foo", "bar"], ["baz", "whatelse", "too", "much"]]} 1efgh
45 response = client.post("/model-with-tuple/", json=data) 1efgh
46 assert response.status_code == 422, response.text 1efgh
48 data = {"items": [["foo", "bar"], ["baz"]]} 1efgh
49 response = client.post("/model-with-tuple/", json=data) 1efgh
50 assert response.status_code == 422, response.text 1efgh
53def test_tuple_with_model_valid(): 1adbc
54 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}] 1qrst
55 response = client.post("/tuple-of-models/", json=data) 1qrst
56 assert response.status_code == 200, response.text 1qrst
57 assert response.json() == data 1qrst
60def test_tuple_with_model_invalid(): 1adbc
61 data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}] 1ijkl
62 response = client.post("/tuple-of-models/", json=data) 1ijkl
63 assert response.status_code == 422, response.text 1ijkl
65 data = [{"x": 1, "y": 2}] 1ijkl
66 response = client.post("/tuple-of-models/", json=data) 1ijkl
67 assert response.status_code == 422, response.text 1ijkl
70def test_tuple_form_valid(): 1adbc
71 response = client.post("/tuple-form/", data={"values": ("1", "2")}) 1uvwx
72 assert response.status_code == 200, response.text 1uvwx
73 assert response.json() == [1, 2] 1uvwx
76def test_tuple_form_invalid(): 1adbc
77 response = client.post("/tuple-form/", data={"values": ("1", "2", "3")}) 1yzAB
78 assert response.status_code == 422, response.text 1yzAB
80 response = client.post("/tuple-form/", data={"values": ("1")}) 1yzAB
81 assert response.status_code == 422, response.text 1yzAB
84def test_openapi_schema(): 1adbc
85 response = client.get("/openapi.json") 1CDEF
86 assert response.status_code == 200, response.text 1CDEF
87 assert response.json() == snapshot( 1CDEF
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 )