Coverage for tests/test_apdbSql.py: 44%

129 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-10 10:38 +0000

1# This file is part of dax_apdb. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

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

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

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

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

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

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21 

22"""Unit test for Apdb class. 

23""" 

24 

25import gc 

26import os 

27import shutil 

28import tempfile 

29import unittest 

30from typing import Any 

31from unittest.mock import patch 

32 

33import lsst.utils.tests 

34from lsst.dax.apdb import Apdb, ApdbConfig, ApdbSql, ApdbTables 

35from lsst.dax.apdb.tests import ApdbSchemaUpdateTest, ApdbTest 

36 

37try: 

38 import testing.postgresql 

39except ImportError: 

40 testing = None 

41 

42TEST_SCHEMA = os.path.join(os.path.abspath(os.path.dirname(__file__)), "config/schema.yaml") 

43 

44 

45class ApdbSQLiteTestCase(ApdbTest, unittest.TestCase): 

46 """A test case for ApdbSql class using SQLite backend.""" 

47 

48 fsrc_requires_id_list = True 

49 dia_object_index = "baseline" 

50 allow_visit_query = False 

51 schema_path = TEST_SCHEMA 

52 

53 def setUp(self) -> None: 

54 self.tempdir = tempfile.mkdtemp() 

55 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3" 

56 

57 def tearDown(self) -> None: 

58 shutil.rmtree(self.tempdir, ignore_errors=True) 

59 

60 def make_instance(self, **kwargs: Any) -> ApdbConfig: 

61 """Make config class instance used in all tests.""" 

62 kw = { 

63 "db_url": self.db_url, 

64 "schema_file": TEST_SCHEMA, 

65 "dia_object_index": self.dia_object_index, 

66 "use_insert_id": self.use_insert_id, 

67 } 

68 kw.update(kwargs) 

69 return ApdbSql.init_database(**kw) # type: ignore[arg-type] 

70 

71 def getDiaObjects_table(self) -> ApdbTables: 

72 """Return type of table returned from getDiaObjects method.""" 

73 return ApdbTables.DiaObject 

74 

75 

76class ApdbSQLiteTestCaseLastObject(ApdbSQLiteTestCase): 

77 """A test case for ApdbSql class using SQLite backend and DiaObjectLast 

78 table. 

79 """ 

80 

81 dia_object_index = "last_object_table" 

82 

83 def getDiaObjects_table(self) -> ApdbTables: 

84 """Return type of table returned from getDiaObjects method.""" 

85 return ApdbTables.DiaObjectLast 

86 

87 

88class ApdbSQLiteTestCasePixIdIovIndex(ApdbSQLiteTestCase): 

89 """A test case for ApdbSql class using SQLite backend with pix_id_iov 

90 indexing. 

91 """ 

92 

93 dia_object_index = "pix_id_iov" 

94 

95 

96class ApdbSQLiteTestCaseInsertIds(ApdbSQLiteTestCase): 

97 """Test case for ApdbSql class using SQLite backend with use_insert_id.""" 

98 

99 use_insert_id = True 

100 

101 

102@unittest.skipUnless(testing is not None, "testing.postgresql module not found") 

103class ApdbPostgresTestCase(ApdbTest, unittest.TestCase): 

104 """A test case for ApdbSql class using Postgres backend.""" 

105 

106 fsrc_requires_id_list = True 

107 dia_object_index = "last_object_table" 

108 postgresql: Any 

109 use_insert_id = True 

110 allow_visit_query = False 

111 schema_path = TEST_SCHEMA 

112 

113 @classmethod 

114 def setUpClass(cls) -> None: 

115 # Create the postgres test server. 

116 cls.postgresql = testing.postgresql.PostgresqlFactory(cache_initialized_db=True) 

117 super().setUpClass() 

118 

119 @classmethod 

120 def tearDownClass(cls) -> None: 

121 # Clean up any lingering SQLAlchemy engines/connections 

122 # so they're closed before we shut down the server. 

123 gc.collect() 

124 cls.postgresql.clear_cache() 

125 super().tearDownClass() 

126 

127 def setUp(self) -> None: 

128 self.server = self.postgresql() 

129 

130 def tearDown(self) -> None: 

131 self.server = self.postgresql() 

132 

133 def make_instance(self, **kwargs: Any) -> ApdbConfig: 

134 """Make config class instance used in all tests.""" 

135 kw = { 

136 "db_url": self.server.url(), 

137 "schema_file": TEST_SCHEMA, 

138 "dia_object_index": self.dia_object_index, 

139 "use_insert_id": self.use_insert_id, 

140 } 

141 kw.update(kwargs) 

142 return ApdbSql.init_database(**kw) 

143 

144 def getDiaObjects_table(self) -> ApdbTables: 

145 """Return type of table returned from getDiaObjects method.""" 

146 return ApdbTables.DiaObjectLast 

147 

148 

149@unittest.skipUnless(testing is not None, "testing.postgresql module not found") 

150class ApdbPostgresNamespaceTestCase(ApdbPostgresTestCase): 

151 """A test case for ApdbSql class using Postgres backend with schema name""" 

152 

153 # use mixed case to trigger quoting 

154 namespace = "ApdbSchema" 

155 

156 def make_instance(self, **kwargs: Any) -> ApdbConfig: 

157 """Make config class instance used in all tests.""" 

158 return super().make_instance(namespace=self.namespace, **kwargs) 

159 

160 

161class ApdbSchemaUpdateSQLiteTestCase(ApdbSchemaUpdateTest, unittest.TestCase): 

162 """A test case for schema updates using SQLite backend.""" 

163 

164 def setUp(self) -> None: 

165 self.tempdir = tempfile.mkdtemp() 

166 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3" 

167 

168 def tearDown(self) -> None: 

169 shutil.rmtree(self.tempdir, ignore_errors=True) 

170 

171 def make_instance(self, **kwargs: Any) -> ApdbConfig: 

172 """Make config class instance used in all tests.""" 

173 kw = { 

174 "db_url": self.db_url, 

175 "schema_file": TEST_SCHEMA, 

176 } 

177 kw.update(kwargs) 

178 return ApdbSql.init_database(**kw) # type: ignore[arg-type] 

179 

180 

181class ApdbSQLiteFromUriTestCase(unittest.TestCase): 

182 """A test case for for instantiating ApdbSql via URI.""" 

183 

184 def setUp(self) -> None: 

185 self.tempdir = tempfile.mkdtemp() 

186 self.addCleanup(shutil.rmtree, self.tempdir, ignore_errors=True) 

187 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3" 

188 config = ApdbSql.init_database(db_url=self.db_url, schema_file=TEST_SCHEMA) 

189 # TODO: This will need update when we switch to pydantic configs. 

190 self.config_path = os.path.join(self.tempdir, "apdb-config.py") 

191 config.save(self.config_path) 

192 self.bad_config_path = os.path.join(self.tempdir, "not-config.py") 

193 self.index_path = os.path.join(self.tempdir, "apdb-index.yaml") 

194 with open(self.index_path, "w") as index_file: 

195 print(f'label1: "{self.config_path}"', file=index_file) 

196 print(f'"label2/pex_config": "{self.config_path}"', file=index_file) 

197 print(f'bad-label: "{self.bad_config_path}"', file=index_file) 

198 # File with incorrect format. 

199 self.bad_index_path = os.path.join(self.tempdir, "apdb-index-bad.yaml") 

200 with open(self.bad_index_path, "w") as index_file: 

201 print(f'label1: ["{self.config_path}"]', file=index_file) 

202 self.missing_index_path = os.path.join(self.tempdir, "no-apdb-index.yaml") 

203 

204 def test_make_apdb_from_path(self) -> None: 

205 """Check that we can make APDB instance from config URI.""" 

206 Apdb.from_uri(self.config_path) 

207 with self.assertRaises(FileNotFoundError): 

208 Apdb.from_uri(self.bad_config_path) 

209 

210 def test_make_apdb_from_labels(self) -> None: 

211 """Check that we can make APDB instance from config URI.""" 

212 # Replace DAX_APDB_INDEX_URI value 

213 new_env = {"DAX_APDB_INDEX_URI": self.index_path} 

214 with patch.dict(os.environ, new_env, clear=True): 

215 Apdb.from_uri("label:label1") 

216 Apdb.from_uri("label:label2") 

217 # Label does not exist. 

218 with self.assertRaises(ValueError): 

219 Apdb.from_uri("label:not-a-label") 

220 # Label exists but points to a missing config. 

221 with self.assertRaises(FileNotFoundError): 

222 Apdb.from_uri("label:bad-label") 

223 

224 def test_make_apdb_bad_index(self) -> None: 

225 """Check what happens when DAX_APDB_INDEX_URI is broken.""" 

226 # envvar is set but empty. 

227 new_env = {"DAX_APDB_INDEX_URI": ""} 

228 with patch.dict(os.environ, new_env, clear=True): 

229 with self.assertRaises(RuntimeError): 

230 Apdb.from_uri("label:label") 

231 

232 # envvar is set to something non-existing. 

233 new_env = {"DAX_APDB_INDEX_URI": self.missing_index_path} 

234 with patch.dict(os.environ, new_env, clear=True): 

235 with self.assertRaises(FileNotFoundError): 

236 Apdb.from_uri("label:label") 

237 

238 # envvar points to an incorrect file. 

239 new_env = {"DAX_APDB_INDEX_URI": self.bad_index_path} 

240 with patch.dict(os.environ, new_env, clear=True): 

241 with self.assertRaises(TypeError): 

242 Apdb.from_uri("label:label") 

243 

244 

245class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

246 """Run file leak tests.""" 

247 

248 

249def setup_module(module: Any) -> None: 

250 """Configure pytest.""" 

251 lsst.utils.tests.init() 

252 

253 

254if __name__ == "__main__": 

255 lsst.utils.tests.init() 

256 unittest.main()