Coverage for tests / test_tutorial / test_fastapi / test_multiple_models / test_tutorial002.py: 100%

52 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-06-10 09:40 +0000

1import importlib 1ijklmnop

2from types import ModuleType 1ijklmnop

3 

4import pytest 1ijklmnop

5from dirty_equals import IsOneOf 1ijklmnop

6from fastapi.testclient import TestClient 1ijklmnop

7from sqlalchemy import inspect 1ijklmnop

8from sqlalchemy.engine.reflection import Inspector 1ijklmnop

9from sqlmodel import create_engine 1ijklmnop

10from sqlmodel.pool import StaticPool 1ijklmnop

11 

12 

13@pytest.fixture( 1ijklmnop

14 name="module", 

15 params=[ 

16 pytest.param("tutorial002_py310"), 

17 ], 

18) 

19def get_module(request: pytest.FixtureRequest) -> ModuleType: 1ijklmnop

20 mod = importlib.import_module( 1ijklmnop

21 f"docs_src.tutorial.fastapi.multiple_models.{request.param}" 

22 ) 

23 mod.sqlite_url = "sqlite://" 1ijklmnop

24 mod.engine = create_engine( 1ijklmnop

25 mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool 

26 ) 

27 return mod 1ijklmnop

28 

29 

30def test_tutorial(module: ModuleType): 1ijklmnop

31 with TestClient(module.app) as client: 1abcdefgh

32 hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} 1abcdefgh

33 hero2_data = { 1abcdefgh

34 "name": "Spider-Boy", 

35 "secret_name": "Pedro Parqueador", 

36 "id": 9000, 

37 } 

38 response = client.post("/heroes/", json=hero1_data) 1abcdefgh

39 data = response.json() 1abcdefgh

40 

41 assert response.status_code == 200, response.text 1abcdefgh

42 assert data["name"] == hero1_data["name"] 1abcdefgh

43 assert data["secret_name"] == hero1_data["secret_name"] 1abcdefgh

44 assert data["id"] is not None 1abcdefgh

45 assert data["age"] is None 1abcdefgh

46 

47 response = client.post("/heroes/", json=hero2_data) 1abcdefgh

48 data = response.json() 1abcdefgh

49 

50 assert response.status_code == 200, response.text 1abcdefgh

51 assert data["name"] == hero2_data["name"] 1abcdefgh

52 assert data["secret_name"] == hero2_data["secret_name"] 1abcdefgh

53 assert data["id"] != hero2_data["id"], ( 1abcdefgh

54 "Now it's not possible to predefine the ID from the request, " 

55 "it's now set by the database" 

56 ) 

57 assert data["age"] is None 1abcdefgh

58 

59 response = client.get("/heroes/") 1abcdefgh

60 data = response.json() 1abcdefgh

61 

62 assert response.status_code == 200, response.text 1abcdefgh

63 assert len(data) == 2 1abcdefgh

64 assert data[0]["name"] == hero1_data["name"] 1abcdefgh

65 assert data[0]["secret_name"] == hero1_data["secret_name"] 1abcdefgh

66 assert data[1]["name"] == hero2_data["name"] 1abcdefgh

67 assert data[1]["secret_name"] == hero2_data["secret_name"] 1abcdefgh

68 assert data[1]["id"] != hero2_data["id"] 1abcdefgh

69 

70 response = client.get("/openapi.json") 1abcdefgh

71 

72 assert response.status_code == 200, response.text 1abcdefgh

73 

74 assert response.json() == { 1abcdefgh

75 "openapi": "3.1.0", 

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

77 "paths": { 

78 "/heroes/": { 

79 "get": { 

80 "summary": "Read Heroes", 

81 "operationId": "read_heroes_heroes__get", 

82 "responses": { 

83 "200": { 

84 "description": "Successful Response", 

85 "content": { 

86 "application/json": { 

87 "schema": { 

88 "title": "Response Read Heroes Heroes Get", 

89 "type": "array", 

90 "items": { 

91 "$ref": "#/components/schemas/HeroPublic" 

92 }, 

93 } 

94 } 

95 }, 

96 } 

97 }, 

98 }, 

99 "post": { 

100 "summary": "Create Hero", 

101 "operationId": "create_hero_heroes__post", 

102 "requestBody": { 

103 "content": { 

104 "application/json": { 

105 "schema": { 

106 "$ref": "#/components/schemas/HeroCreate" 

107 } 

108 } 

109 }, 

110 "required": True, 

111 }, 

112 "responses": { 

113 "200": { 

114 "description": "Successful Response", 

115 "content": { 

116 "application/json": { 

117 "schema": { 

118 "$ref": "#/components/schemas/HeroPublic" 

119 } 

120 } 

121 }, 

122 }, 

123 "422": { 

124 "description": "Validation Error", 

125 "content": { 

126 "application/json": { 

127 "schema": { 

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

129 } 

130 } 

131 }, 

132 }, 

133 }, 

134 }, 

135 } 

136 }, 

137 "components": { 

138 "schemas": { 

139 "HTTPValidationError": { 

140 "title": "HTTPValidationError", 

141 "type": "object", 

142 "properties": { 

143 "detail": { 

144 "title": "Detail", 

145 "type": "array", 

146 "items": { 

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

148 }, 

149 } 

150 }, 

151 }, 

152 "HeroCreate": { 

153 "title": "HeroCreate", 

154 "required": ["name", "secret_name"], 

155 "type": "object", 

156 "properties": { 

157 "name": {"title": "Name", "type": "string"}, 

158 "secret_name": {"title": "Secret Name", "type": "string"}, 

159 "age": { 

160 "title": "Age", 

161 "anyOf": [{"type": "integer"}, {"type": "null"}], 

162 }, 

163 }, 

164 }, 

165 "HeroPublic": { 

166 "title": "HeroPublic", 

167 "required": ["name", "secret_name", "id"], 

168 "type": "object", 

169 "properties": { 

170 "name": {"title": "Name", "type": "string"}, 

171 "secret_name": {"title": "Secret Name", "type": "string"}, 

172 "age": { 

173 "title": "Age", 

174 "anyOf": [{"type": "integer"}, {"type": "null"}], 

175 }, 

176 "id": {"title": "Id", "type": "integer"}, 

177 }, 

178 }, 

179 "ValidationError": { 

180 "title": "ValidationError", 

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

182 "type": "object", 

183 "properties": IsOneOf( 

184 { 

185 "loc": { 

186 "title": "Location", 

187 "type": "array", 

188 "items": { 

189 "anyOf": [ 

190 {"type": "string"}, 

191 {"type": "integer"}, 

192 ] 

193 }, 

194 }, 

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

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

197 }, 

198 { 

199 "loc": { 

200 "title": "Location", 

201 "type": "array", 

202 "items": { 

203 "anyOf": [ 

204 {"type": "string"}, 

205 {"type": "integer"}, 

206 ] 

207 }, 

208 }, 

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

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

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

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

213 }, 

214 ), 

215 }, 

216 } 

217 }, 

218 } 

219 

220 # Test inherited indexes 

221 insp: Inspector = inspect(module.engine) 1abcdefgh

222 indexes = insp.get_indexes(str(module.Hero.__tablename__)) 1abcdefgh

223 expected_indexes = [ 1abcdefgh

224 { 

225 "name": "ix_hero_age", 

226 "dialect_options": {}, 

227 "column_names": ["age"], 

228 "unique": 0, 

229 }, 

230 { 

231 "name": "ix_hero_name", 

232 "dialect_options": {}, 

233 "column_names": ["name"], 

234 "unique": 0, 

235 }, 

236 ] 

237 for index in expected_indexes: 1abcdefgh

238 assert index in indexes, "This expected index should be in the indexes in DB" 1abcdefgh

239 # Now that this index was checked, remove it from the list of indexes 

240 indexes.pop(indexes.index(index)) 1abcdefgh

241 assert len(indexes) == 0, "The database should only have the expected indexes" 1abcdefgh