Coverage for tests/test_apdbSql.py: 44%
130 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-24 09:59 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-24 09:59 +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, ApdbTables
35from lsst.dax.apdb.sql import ApdbSql
36from lsst.dax.apdb.tests import ApdbSchemaUpdateTest, ApdbTest
38try:
39 import testing.postgresql
40except ImportError:
41 testing = None
43TEST_SCHEMA = os.path.join(os.path.abspath(os.path.dirname(__file__)), "config/schema.yaml")
46class ApdbSQLiteTestCase(ApdbTest, unittest.TestCase):
47 """A test case for ApdbSql class using SQLite backend."""
49 fsrc_requires_id_list = True
50 dia_object_index = "baseline"
51 allow_visit_query = False
52 schema_path = TEST_SCHEMA
54 def setUp(self) -> None:
55 self.tempdir = tempfile.mkdtemp()
56 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
58 def tearDown(self) -> None:
59 shutil.rmtree(self.tempdir, ignore_errors=True)
61 def make_instance(self, **kwargs: Any) -> ApdbConfig:
62 """Make config class instance used in all tests."""
63 kw = {
64 "db_url": self.db_url,
65 "schema_file": TEST_SCHEMA,
66 "dia_object_index": self.dia_object_index,
67 "use_insert_id": self.enable_replica,
68 }
69 kw.update(kwargs)
70 return ApdbSql.init_database(**kw) # type: ignore[arg-type]
72 def getDiaObjects_table(self) -> ApdbTables:
73 """Return type of table returned from getDiaObjects method."""
74 return ApdbTables.DiaObject
77class ApdbSQLiteTestCaseLastObject(ApdbSQLiteTestCase):
78 """A test case for ApdbSql class using SQLite backend and DiaObjectLast
79 table.
80 """
82 dia_object_index = "last_object_table"
84 def getDiaObjects_table(self) -> ApdbTables:
85 """Return type of table returned from getDiaObjects method."""
86 return ApdbTables.DiaObjectLast
89class ApdbSQLiteTestCasePixIdIovIndex(ApdbSQLiteTestCase):
90 """A test case for ApdbSql class using SQLite backend with pix_id_iov
91 indexing.
92 """
94 dia_object_index = "pix_id_iov"
97class ApdbSQLiteTestCaseReplica(ApdbSQLiteTestCase):
98 """Test case for ApdbSql class using SQLite backend with replica tables."""
100 enable_replica = True
103@unittest.skipUnless(testing is not None, "testing.postgresql module not found")
104class ApdbPostgresTestCase(ApdbTest, unittest.TestCase):
105 """A test case for ApdbSql class using Postgres backend."""
107 fsrc_requires_id_list = True
108 dia_object_index = "last_object_table"
109 postgresql: Any
110 enable_replica = True
111 allow_visit_query = False
112 schema_path = TEST_SCHEMA
114 @classmethod
115 def setUpClass(cls) -> None:
116 # Create the postgres test server.
117 cls.postgresql = testing.postgresql.PostgresqlFactory(cache_initialized_db=True)
118 super().setUpClass()
120 @classmethod
121 def tearDownClass(cls) -> None:
122 # Clean up any lingering SQLAlchemy engines/connections
123 # so they're closed before we shut down the server.
124 gc.collect()
125 cls.postgresql.clear_cache()
126 super().tearDownClass()
128 def setUp(self) -> None:
129 self.server = self.postgresql()
131 def tearDown(self) -> None:
132 self.server = self.postgresql()
134 def make_instance(self, **kwargs: Any) -> ApdbConfig:
135 """Make config class instance used in all tests."""
136 kw = {
137 "db_url": self.server.url(),
138 "schema_file": TEST_SCHEMA,
139 "dia_object_index": self.dia_object_index,
140 "use_insert_id": self.enable_replica,
141 }
142 kw.update(kwargs)
143 return ApdbSql.init_database(**kw)
145 def getDiaObjects_table(self) -> ApdbTables:
146 """Return type of table returned from getDiaObjects method."""
147 return ApdbTables.DiaObjectLast
150@unittest.skipUnless(testing is not None, "testing.postgresql module not found")
151class ApdbPostgresNamespaceTestCase(ApdbPostgresTestCase):
152 """A test case for ApdbSql class using Postgres backend with schema name"""
154 # use mixed case to trigger quoting
155 namespace = "ApdbSchema"
157 def make_instance(self, **kwargs: Any) -> ApdbConfig:
158 """Make config class instance used in all tests."""
159 return super().make_instance(namespace=self.namespace, **kwargs)
162class ApdbSchemaUpdateSQLiteTestCase(ApdbSchemaUpdateTest, unittest.TestCase):
163 """A test case for schema updates using SQLite backend."""
165 def setUp(self) -> None:
166 self.tempdir = tempfile.mkdtemp()
167 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
169 def tearDown(self) -> None:
170 shutil.rmtree(self.tempdir, ignore_errors=True)
172 def make_instance(self, **kwargs: Any) -> ApdbConfig:
173 """Make config class instance used in all tests."""
174 kw = {
175 "db_url": self.db_url,
176 "schema_file": TEST_SCHEMA,
177 }
178 kw.update(kwargs)
179 return ApdbSql.init_database(**kw) # type: ignore[arg-type]
182class ApdbSQLiteFromUriTestCase(unittest.TestCase):
183 """A test case for for instantiating ApdbSql via URI."""
185 def setUp(self) -> None:
186 self.tempdir = tempfile.mkdtemp()
187 self.addCleanup(shutil.rmtree, self.tempdir, ignore_errors=True)
188 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
189 config = ApdbSql.init_database(db_url=self.db_url, schema_file=TEST_SCHEMA)
190 # TODO: This will need update when we switch to pydantic configs.
191 self.config_path = os.path.join(self.tempdir, "apdb-config.py")
192 config.save(self.config_path)
193 self.bad_config_path = os.path.join(self.tempdir, "not-config.py")
194 self.index_path = os.path.join(self.tempdir, "apdb-index.yaml")
195 with open(self.index_path, "w") as index_file:
196 print(f'label1: "{self.config_path}"', file=index_file)
197 print(f'"label2/pex_config": "{self.config_path}"', file=index_file)
198 print(f'bad-label: "{self.bad_config_path}"', file=index_file)
199 # File with incorrect format.
200 self.bad_index_path = os.path.join(self.tempdir, "apdb-index-bad.yaml")
201 with open(self.bad_index_path, "w") as index_file:
202 print(f'label1: ["{self.config_path}"]', file=index_file)
203 self.missing_index_path = os.path.join(self.tempdir, "no-apdb-index.yaml")
205 def test_make_apdb_from_path(self) -> None:
206 """Check that we can make APDB instance from config URI."""
207 Apdb.from_uri(self.config_path)
208 with self.assertRaises(FileNotFoundError):
209 Apdb.from_uri(self.bad_config_path)
211 def test_make_apdb_from_labels(self) -> None:
212 """Check that we can make APDB instance from config URI."""
213 # Replace DAX_APDB_INDEX_URI value
214 new_env = {"DAX_APDB_INDEX_URI": self.index_path}
215 with patch.dict(os.environ, new_env, clear=True):
216 Apdb.from_uri("label:label1")
217 Apdb.from_uri("label:label2")
218 # Label does not exist.
219 with self.assertRaises(ValueError):
220 Apdb.from_uri("label:not-a-label")
221 # Label exists but points to a missing config.
222 with self.assertRaises(FileNotFoundError):
223 Apdb.from_uri("label:bad-label")
225 def test_make_apdb_bad_index(self) -> None:
226 """Check what happens when DAX_APDB_INDEX_URI is broken."""
227 # envvar is set but empty.
228 new_env = {"DAX_APDB_INDEX_URI": ""}
229 with patch.dict(os.environ, new_env, clear=True):
230 with self.assertRaises(RuntimeError):
231 Apdb.from_uri("label:label")
233 # envvar is set to something non-existing.
234 new_env = {"DAX_APDB_INDEX_URI": self.missing_index_path}
235 with patch.dict(os.environ, new_env, clear=True):
236 with self.assertRaises(FileNotFoundError):
237 Apdb.from_uri("label:label")
239 # envvar points to an incorrect file.
240 new_env = {"DAX_APDB_INDEX_URI": self.bad_index_path}
241 with patch.dict(os.environ, new_env, clear=True):
242 with self.assertRaises(TypeError):
243 Apdb.from_uri("label:label")
246class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
247 """Run file leak tests."""
250def setup_module(module: Any) -> None:
251 """Configure pytest."""
252 lsst.utils.tests.init()
255if __name__ == "__main__":
256 lsst.utils.tests.init()
257 unittest.main()