Coverage for .tox/coverage/lib/python3.11/site-packages/wuttjamaican/testing.py: 100%

53 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-12-31 19:12 -0600

1# -*- coding: utf-8; -*- 

2################################################################################ 

3# 

4# WuttJamaican -- Base package for Wutta Framework 

5# Copyright © 2023-2025 Lance Edgar 

6# 

7# This file is part of Wutta Framework. 

8# 

9# Wutta Framework is free software: you can redistribute it and/or modify it 

10# under the terms of the GNU General Public License as published by the Free 

11# Software Foundation, either version 3 of the License, or (at your option) any 

12# later version. 

13# 

14# Wutta Framework is distributed in the hope that it will be useful, but 

15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 

16# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 

17# more details. 

18# 

19# You should have received a copy of the GNU General Public License along with 

20# Wutta Framework. If not, see <http://www.gnu.org/licenses/>. 

21# 

22################################################################################ 

23""" 

24WuttJamaican - test utilities 

25""" 

26 

27import os 

28import shutil 

29import tempfile 

30import warnings 

31from unittest import TestCase 

32 

33from wuttjamaican.conf import WuttaConfig 

34 

35 

36class FileTestCase(TestCase): 

37 """ 

38 Base class for test suites which (may) write temporary files, for 

39 sake of testing the config constructor etc. It inherits from 

40 :class:`python:unittest.TestCase`. 

41 

42 This class creates a temporary folder on setup, and removes it on 

43 teardown. See below for features exposed to work with the folder. 

44 

45 .. attribute:: tempdir 

46 

47 Path to the temporary folder created during setup. 

48 

49 .. note:: 

50 

51 If you subclass this and need to override setup/teardown, 

52 please be sure to call the corresponding methods for this 

53 class. 

54 """ 

55 

56 def setUp(self): # pylint: disable=empty-docstring 

57 """ """ 

58 self.setup_files() 

59 

60 def setup_files(self): 

61 """ 

62 This creates the temporary folder. 

63 """ 

64 self.tempdir = tempfile.mkdtemp() 

65 

66 def setup_file_config(self): # pragma: no cover; pylint: disable=empty-docstring 

67 """ """ 

68 warnings.warn( 

69 "FileTestCase.setup_file_config() is deprecated; " 

70 "please use setup_files() instead", 

71 DeprecationWarning, 

72 stacklevel=2, 

73 ) 

74 self.setup_files() 

75 

76 def tearDown(self): # pylint: disable=empty-docstring 

77 """ """ 

78 self.teardown_files() 

79 

80 def teardown_files(self): 

81 """ 

82 This removes the temporary folder. 

83 """ 

84 shutil.rmtree(self.tempdir) 

85 

86 def teardown_file_config(self): # pragma: no cover; pylint: disable=empty-docstring 

87 """ """ 

88 warnings.warn( 

89 "FileTestCase.teardown_file_config() is deprecated; " 

90 "please use teardown_files() instead", 

91 DeprecationWarning, 

92 stacklevel=2, 

93 ) 

94 self.teardown_files() 

95 

96 def write_file(self, filename, content): 

97 """ 

98 Write a new file (in temporary folder) with the given filename 

99 and content, and return its full path. For instance:: 

100 

101 myconf = self.write_file('my.conf', '<file contents>') 

102 """ 

103 path = os.path.join(self.tempdir, filename) 

104 with open(path, "wt", encoding="utf_8") as f: 

105 f.write(content) 

106 return path 

107 

108 def mkdir( 

109 self, dirname 

110 ): # pragma: no cover; pylint: disable=unused-argument,empty-docstring 

111 """ """ 

112 warnings.warn( 

113 "FileTestCase.mkdir() is deprecated; " 

114 "please use FileTestCase.mkdtemp() instead", 

115 DeprecationWarning, 

116 stacklevel=2, 

117 ) 

118 return self.mkdtemp() 

119 

120 def mkdtemp(self): 

121 """ 

122 Make a new temporary folder and return its path. 

123 

124 Note that this will be created *underneath* :attr:`tempdir`. 

125 """ 

126 return tempfile.mkdtemp(dir=self.tempdir) 

127 

128 

129# TODO: deprecate / remove this 

130FileConfigTestCase = FileTestCase 

131 

132 

133class ConfigTestCase(FileTestCase): 

134 """ 

135 Base class for test suites requiring a config object. 

136 

137 It inherits from :class:`FileTestCase` so also has the 

138 file-related methods. 

139 

140 The running test has these attributes: 

141 

142 .. attribute:: config 

143 

144 Reference to the config object. 

145 

146 .. attribute:: app 

147 

148 Reference to the app handler. 

149 

150 .. note:: 

151 

152 If you subclass this directly and need to override 

153 setup/teardown, please be sure to call the corresponding 

154 methods for this class. 

155 """ 

156 

157 def setUp(self): # pylint: disable=empty-docstring 

158 """ """ 

159 self.setup_config() 

160 

161 def setup_config(self): 

162 """ 

163 Perform config setup operations for the test. 

164 """ 

165 self.setup_files() 

166 self.config = self.make_config() 

167 self.app = self.config.get_app() 

168 

169 def tearDown(self): # pylint: disable=empty-docstring 

170 """ """ 

171 self.teardown_config() 

172 

173 def teardown_config(self): 

174 """ 

175 Perform config teardown operations for the test. 

176 """ 

177 self.teardown_files() 

178 

179 def make_config( # pylint: disable=missing-function-docstring 

180 self, files=None, **kwargs 

181 ): 

182 return WuttaConfig(files, **kwargs) 

183 

184 

185class DataTestCase(ConfigTestCase): 

186 """ 

187 Base class for test suites requiring a full (typical) database. 

188 

189 It inherits from :class:`FileTestCase` so also has the 

190 file-related methods. 

191 

192 This uses a SQLite in-memory database and creates all tables for 

193 the app model. The running test has these attributes: 

194 

195 .. attribute:: config 

196 

197 Reference to the config object. 

198 

199 .. attribute:: app 

200 

201 Reference to the app handler. 

202 

203 .. attribute:: session 

204 

205 Open session for the test DB. 

206 

207 .. note:: 

208 

209 If you subclass this and need to override setup/teardown, 

210 please be sure to call the corresponding methods for this 

211 class. 

212 

213 However you do *not* need to call the file-related setup or 

214 teardown methods, as this class handles that automatically. 

215 """ 

216 

217 sqlite_engine_url = "sqlite://" 

218 

219 def setUp(self): # pylint: disable=empty-docstring 

220 """ """ 

221 self.setup_db() 

222 

223 def setup_db(self): 

224 """ 

225 Perform config/app/db setup operations for the test. 

226 """ 

227 self.setup_config() 

228 

229 model = self.app.model 

230 model.Base.metadata.create_all(bind=self.config.appdb_engine) 

231 self.session = self.app.make_session() 

232 

233 def tearDown(self): # pylint: disable=empty-docstring 

234 """ """ 

235 self.teardown_db() 

236 

237 def teardown_db(self): 

238 """ 

239 Perform config/app/db teardown operations for the test. 

240 """ 

241 self.teardown_config() 

242 

243 def make_config(self, files=None, **kwargs): 

244 defaults = kwargs.setdefault("defaults", {}) 

245 defaults.setdefault("wutta.db.default.url", self.sqlite_engine_url) 

246 return super().make_config(files, **kwargs)