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
« 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/>.
22"""Unit test for Apdb class.
23"""
25import gc
26import os
27import shutil
28import tempfile
29import unittest
30from typing import Any
31from unittest.mock import patch
33import lsst.utils.tests
34from lsst.dax.apdb import Apdb, ApdbConfig, ApdbSql, ApdbTables
35from lsst.dax.apdb.tests import ApdbSchemaUpdateTest, ApdbTest
37try:
38 import testing.postgresql
39except ImportError:
40 testing = None
42TEST_SCHEMA = os.path.join(os.path.abspath(os.path.dirname(__file__)), "config/schema.yaml")
45class ApdbSQLiteTestCase(ApdbTest, unittest.TestCase):
46 """A test case for ApdbSql class using SQLite backend."""
48 fsrc_requires_id_list = True
49 dia_object_index = "baseline"
50 allow_visit_query = False
51 schema_path = TEST_SCHEMA
53 def setUp(self) -> None:
54 self.tempdir = tempfile.mkdtemp()
55 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
57 def tearDown(self) -> None:
58 shutil.rmtree(self.tempdir, ignore_errors=True)
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]
71 def getDiaObjects_table(self) -> ApdbTables:
72 """Return type of table returned from getDiaObjects method."""
73 return ApdbTables.DiaObject
76class ApdbSQLiteTestCaseLastObject(ApdbSQLiteTestCase):
77 """A test case for ApdbSql class using SQLite backend and DiaObjectLast
78 table.
79 """
81 dia_object_index = "last_object_table"
83 def getDiaObjects_table(self) -> ApdbTables:
84 """Return type of table returned from getDiaObjects method."""
85 return ApdbTables.DiaObjectLast
88class ApdbSQLiteTestCasePixIdIovIndex(ApdbSQLiteTestCase):
89 """A test case for ApdbSql class using SQLite backend with pix_id_iov
90 indexing.
91 """
93 dia_object_index = "pix_id_iov"
96class ApdbSQLiteTestCaseInsertIds(ApdbSQLiteTestCase):
97 """Test case for ApdbSql class using SQLite backend with use_insert_id."""
99 use_insert_id = True
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."""
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
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()
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()
127 def setUp(self) -> None:
128 self.server = self.postgresql()
130 def tearDown(self) -> None:
131 self.server = self.postgresql()
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)
144 def getDiaObjects_table(self) -> ApdbTables:
145 """Return type of table returned from getDiaObjects method."""
146 return ApdbTables.DiaObjectLast
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"""
153 # use mixed case to trigger quoting
154 namespace = "ApdbSchema"
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)
161class ApdbSchemaUpdateSQLiteTestCase(ApdbSchemaUpdateTest, unittest.TestCase):
162 """A test case for schema updates using SQLite backend."""
164 def setUp(self) -> None:
165 self.tempdir = tempfile.mkdtemp()
166 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
168 def tearDown(self) -> None:
169 shutil.rmtree(self.tempdir, ignore_errors=True)
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]
181class ApdbSQLiteFromUriTestCase(unittest.TestCase):
182 """A test case for for instantiating ApdbSql via URI."""
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")
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)
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")
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")
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")
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")
245class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
246 """Run file leak tests."""
249def setup_module(module: Any) -> None:
250 """Configure pytest."""
251 lsst.utils.tests.init()
254if __name__ == "__main__":
255 lsst.utils.tests.init()
256 unittest.main()