Coverage for tests / test_tutorial / test_body_nested_models / test_tutorial005.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1import importlib 1abdc

2 

3import pytest 1abdc

4from dirty_equals import IsList 1abdc

5from fastapi.testclient import TestClient 1abdc

6from inline_snapshot import snapshot 1abdc

7 

8from ...utils import needs_py310 1abdc

9 

10 

11@pytest.fixture( 1abdc

12 name="client", 

13 params=[ 

14 pytest.param("tutorial005_py310", marks=needs_py310), 

15 ], 

16) 

17def get_client(request: pytest.FixtureRequest): 1abdc

18 mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}") 1abc

19 

20 client = TestClient(mod.app) 1abc

21 return client 1abc

22 

23 

24def test_put_all(client: TestClient): 1abdc

25 response = client.put( 1efg

26 "/items/123", 

27 json={ 

28 "name": "Foo", 

29 "description": "A very nice Item", 

30 "price": 35.4, 

31 "tax": 3.2, 

32 "tags": ["foo", "bar", "foo"], 

33 "image": {"url": "http://example.com/image.png", "name": "example image"}, 

34 }, 

35 ) 

36 assert response.status_code == 200, response.text 1efg

37 assert response.json() == { 1efg

38 "item_id": 123, 

39 "item": { 

40 "name": "Foo", 

41 "description": "A very nice Item", 

42 "price": 35.4, 

43 "tax": 3.2, 

44 "tags": IsList("foo", "bar", check_order=False), 

45 "image": {"url": "http://example.com/image.png", "name": "example image"}, 

46 }, 

47 } 

48 

49 

50def test_put_only_required(client: TestClient): 1abdc

51 response = client.put( 1hij

52 "/items/5", 

53 json={"name": "Foo", "price": 35.4}, 

54 ) 

55 assert response.status_code == 200, response.text 1hij

56 assert response.json() == { 1hij

57 "item_id": 5, 

58 "item": { 

59 "name": "Foo", 

60 "description": None, 

61 "price": 35.4, 

62 "tax": None, 

63 "tags": [], 

64 "image": None, 

65 }, 

66 } 

67 

68 

69def test_put_empty_body(client: TestClient): 1abdc

70 response = client.put( 1klm

71 "/items/5", 

72 json={}, 

73 ) 

74 assert response.status_code == 422, response.text 1klm

75 assert response.json() == { 1klm

76 "detail": [ 

77 { 

78 "loc": ["body", "name"], 

79 "input": {}, 

80 "msg": "Field required", 

81 "type": "missing", 

82 }, 

83 { 

84 "loc": ["body", "price"], 

85 "input": {}, 

86 "msg": "Field required", 

87 "type": "missing", 

88 }, 

89 ] 

90 } 

91 

92 

93def test_put_missing_required_in_item(client: TestClient): 1abdc

94 response = client.put( 1nop

95 "/items/5", 

96 json={"description": "A very nice Item"}, 

97 ) 

98 assert response.status_code == 422, response.text 1nop

99 assert response.json() == { 1nop

100 "detail": [ 

101 { 

102 "loc": ["body", "name"], 

103 "input": {"description": "A very nice Item"}, 

104 "msg": "Field required", 

105 "type": "missing", 

106 }, 

107 { 

108 "loc": ["body", "price"], 

109 "input": {"description": "A very nice Item"}, 

110 "msg": "Field required", 

111 "type": "missing", 

112 }, 

113 ] 

114 } 

115 

116 

117def test_put_missing_required_in_image(client: TestClient): 1abdc

118 response = client.put( 1qrs

119 "/items/5", 

120 json={ 

121 "name": "Foo", 

122 "price": 35.4, 

123 "image": {"url": "http://example.com/image.png"}, 

124 }, 

125 ) 

126 assert response.status_code == 422, response.text 1qrs

127 assert response.json() == { 1qrs

128 "detail": [ 

129 { 

130 "loc": ["body", "image", "name"], 

131 "input": {"url": "http://example.com/image.png"}, 

132 "msg": "Field required", 

133 "type": "missing", 

134 }, 

135 ] 

136 } 

137 

138 

139def test_put_wrong_url(client: TestClient): 1abdc

140 response = client.put( 1tuv

141 "/items/5", 

142 json={ 

143 "name": "Foo", 

144 "price": 35.4, 

145 "image": {"url": "not a valid url", "name": "example image"}, 

146 }, 

147 ) 

148 assert response.status_code == 422, response.text 1tuv

149 assert response.json() == { 1tuv

150 "detail": [ 

151 { 

152 "loc": ["body", "image", "url"], 

153 "input": "not a valid url", 

154 "msg": "Input should be a valid URL, relative URL without a base", 

155 "type": "url_parsing", 

156 "ctx": {"error": "relative URL without a base"}, 

157 }, 

158 ] 

159 } 

160 

161 

162def test_openapi_schema(client: TestClient): 1abdc

163 response = client.get("/openapi.json") 1wxy

164 assert response.status_code == 200, response.text 1wxy

165 assert response.json() == snapshot( 1wxy

166 { 

167 "openapi": "3.1.0", 

168 "info": {"title": "FastAPI", "version": "0.1.0"}, 

169 "paths": { 

170 "/items/{item_id}": { 

171 "put": { 

172 "parameters": [ 

173 { 

174 "in": "path", 

175 "name": "item_id", 

176 "required": True, 

177 "schema": { 

178 "title": "Item Id", 

179 "type": "integer", 

180 }, 

181 }, 

182 ], 

183 "responses": { 

184 "200": { 

185 "description": "Successful Response", 

186 "content": {"application/json": {"schema": {}}}, 

187 }, 

188 "422": { 

189 "description": "Validation Error", 

190 "content": { 

191 "application/json": { 

192 "schema": { 

193 "$ref": "#/components/schemas/HTTPValidationError" 

194 } 

195 } 

196 }, 

197 }, 

198 }, 

199 "summary": "Update Item", 

200 "operationId": "update_item_items__item_id__put", 

201 "requestBody": { 

202 "content": { 

203 "application/json": { 

204 "schema": { 

205 "$ref": "#/components/schemas/Item", 

206 } 

207 } 

208 }, 

209 "required": True, 

210 }, 

211 } 

212 } 

213 }, 

214 "components": { 

215 "schemas": { 

216 "Image": { 

217 "properties": { 

218 "url": { 

219 "title": "Url", 

220 "type": "string", 

221 "format": "uri", 

222 "maxLength": 2083, 

223 "minLength": 1, 

224 }, 

225 "name": { 

226 "title": "Name", 

227 "type": "string", 

228 }, 

229 }, 

230 "required": ["url", "name"], 

231 "title": "Image", 

232 "type": "object", 

233 }, 

234 "Item": { 

235 "properties": { 

236 "name": { 

237 "title": "Name", 

238 "type": "string", 

239 }, 

240 "description": { 

241 "title": "Description", 

242 "anyOf": [{"type": "string"}, {"type": "null"}], 

243 }, 

244 "price": { 

245 "title": "Price", 

246 "type": "number", 

247 }, 

248 "tax": { 

249 "title": "Tax", 

250 "anyOf": [{"type": "number"}, {"type": "null"}], 

251 }, 

252 "tags": { 

253 "title": "Tags", 

254 "default": [], 

255 "type": "array", 

256 "items": {"type": "string"}, 

257 "uniqueItems": True, 

258 }, 

259 "image": { 

260 "anyOf": [ 

261 {"$ref": "#/components/schemas/Image"}, 

262 {"type": "null"}, 

263 ], 

264 }, 

265 }, 

266 "required": [ 

267 "name", 

268 "price", 

269 ], 

270 "title": "Item", 

271 "type": "object", 

272 }, 

273 "ValidationError": { 

274 "title": "ValidationError", 

275 "required": ["loc", "msg", "type"], 

276 "type": "object", 

277 "properties": { 

278 "loc": { 

279 "title": "Location", 

280 "type": "array", 

281 "items": { 

282 "anyOf": [{"type": "string"}, {"type": "integer"}] 

283 }, 

284 }, 

285 "msg": {"title": "Message", "type": "string"}, 

286 "type": {"title": "Error Type", "type": "string"}, 

287 "input": {"title": "Input"}, 

288 "ctx": {"title": "Context", "type": "object"}, 

289 }, 

290 }, 

291 "HTTPValidationError": { 

292 "title": "HTTPValidationError", 

293 "type": "object", 

294 "properties": { 

295 "detail": { 

296 "title": "Detail", 

297 "type": "array", 

298 "items": { 

299 "$ref": "#/components/schemas/ValidationError" 

300 }, 

301 } 

302 }, 

303 }, 

304 } 

305 }, 

306 } 

307 )