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

47 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 Is, snapshot 1abdc

7 

8from ...utils import needs_py310 1abdc

9 

10UNTYPED_LIST_SCHEMA = {"type": "array", "items": {}} 1abdc

11 

12LIST_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}} 1abdc

13 

14SET_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}, "uniqueItems": True} 1abdc

15 

16 

17@pytest.fixture( 1abdc

18 name="mod_name", 

19 params=[ 

20 pytest.param("tutorial001_py310", marks=needs_py310), 

21 pytest.param("tutorial002_py310", marks=needs_py310), 

22 pytest.param("tutorial003_py310", marks=needs_py310), 

23 ], 

24) 

25def get_mod_name(request: pytest.FixtureRequest): 1abdc

26 return request.param 1abc

27 

28 

29@pytest.fixture(name="client") 1abdc

30def get_client(mod_name: str): 1abdc

31 mod = importlib.import_module(f"docs_src.body_nested_models.{mod_name}") 1abc

32 

33 client = TestClient(mod.app) 1abc

34 return client 1abc

35 

36 

37def test_put_all(client: TestClient, mod_name: str): 1abdc

38 if mod_name.startswith("tutorial003"): 1hij

39 tags_expected = IsList("foo", "bar", check_order=False) 1hij

40 else: 

41 tags_expected = ["foo", "bar", "foo"] 1hij

42 

43 response = client.put( 1hij

44 "/items/123", 

45 json={ 

46 "name": "Foo", 

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

48 "price": 35.4, 

49 "tax": 3.2, 

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

51 }, 

52 ) 

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

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

55 "item_id": 123, 

56 "item": { 

57 "name": "Foo", 

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

59 "price": 35.4, 

60 "tax": 3.2, 

61 "tags": tags_expected, 

62 }, 

63 } 

64 

65 

66def test_put_only_required(client: TestClient): 1abdc

67 response = client.put( 1klm

68 "/items/5", 

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

70 ) 

71 assert response.status_code == 200, response.text 1klm

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

73 "item_id": 5, 

74 "item": { 

75 "name": "Foo", 

76 "description": None, 

77 "price": 35.4, 

78 "tax": None, 

79 "tags": [], 

80 }, 

81 } 

82 

83 

84def test_put_empty_body(client: TestClient): 1abdc

85 response = client.put( 1nop

86 "/items/5", 

87 json={}, 

88 ) 

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

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

91 "detail": [ 

92 { 

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

94 "input": {}, 

95 "msg": "Field required", 

96 "type": "missing", 

97 }, 

98 { 

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

100 "input": {}, 

101 "msg": "Field required", 

102 "type": "missing", 

103 }, 

104 ] 

105 } 

106 

107 

108def test_put_missing_required(client: TestClient): 1abdc

109 response = client.put( 1qrs

110 "/items/5", 

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

112 ) 

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

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

115 "detail": [ 

116 { 

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

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

119 "msg": "Field required", 

120 "type": "missing", 

121 }, 

122 { 

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

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

125 "msg": "Field required", 

126 "type": "missing", 

127 }, 

128 ] 

129 } 

130 

131 

132def test_openapi_schema(client: TestClient, mod_name: str): 1abdc

133 tags_schema = {"default": [], "title": "Tags"} 1efg

134 if mod_name.startswith("tutorial001"): 1efg

135 tags_schema.update(UNTYPED_LIST_SCHEMA) 1efg

136 elif mod_name.startswith("tutorial002"): 1efg

137 tags_schema.update(LIST_OF_STR_SCHEMA) 1efg

138 elif mod_name.startswith("tutorial003"): 1efg

139 tags_schema.update(SET_OF_STR_SCHEMA) 1efg

140 

141 response = client.get("/openapi.json") 1efg

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

143 assert response.json() == snapshot( 1efg

144 { 

145 "openapi": "3.1.0", 

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

147 "paths": { 

148 "/items/{item_id}": { 

149 "put": { 

150 "parameters": [ 

151 { 

152 "in": "path", 

153 "name": "item_id", 

154 "required": True, 

155 "schema": { 

156 "title": "Item Id", 

157 "type": "integer", 

158 }, 

159 }, 

160 ], 

161 "responses": { 

162 "200": { 

163 "description": "Successful Response", 

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

165 }, 

166 "422": { 

167 "description": "Validation Error", 

168 "content": { 

169 "application/json": { 

170 "schema": { 

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

172 } 

173 } 

174 }, 

175 }, 

176 }, 

177 "summary": "Update Item", 

178 "operationId": "update_item_items__item_id__put", 

179 "requestBody": { 

180 "content": { 

181 "application/json": { 

182 "schema": { 

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

184 } 

185 } 

186 }, 

187 "required": True, 

188 }, 

189 } 

190 } 

191 }, 

192 "components": { 

193 "schemas": { 

194 "Item": { 

195 "properties": { 

196 "name": { 

197 "title": "Name", 

198 "type": "string", 

199 }, 

200 "description": { 

201 "title": "Description", 

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

203 }, 

204 "price": { 

205 "title": "Price", 

206 "type": "number", 

207 }, 

208 "tax": { 

209 "title": "Tax", 

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

211 }, 

212 "tags": Is(tags_schema), 

213 }, 

214 "required": [ 

215 "name", 

216 "price", 

217 ], 

218 "title": "Item", 

219 "type": "object", 

220 }, 

221 "ValidationError": { 

222 "title": "ValidationError", 

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

224 "type": "object", 

225 "properties": { 

226 "loc": { 

227 "title": "Location", 

228 "type": "array", 

229 "items": { 

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

231 }, 

232 }, 

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

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

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

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

237 }, 

238 }, 

239 "HTTPValidationError": { 

240 "title": "HTTPValidationError", 

241 "type": "object", 

242 "properties": { 

243 "detail": { 

244 "title": "Detail", 

245 "type": "array", 

246 "items": { 

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

248 }, 

249 } 

250 }, 

251 }, 

252 } 

253 }, 

254 } 

255 )