Coverage for tests/test_apdbSql.py: 44%
128 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 02:52 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 02:52 -0700
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 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.enable_replica,
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 ApdbSQLiteTestCaseReplica(ApdbSQLiteTestCase):
97 """Test case for ApdbSql class using SQLite backend with replica tables."""
99 enable_replica = 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 enable_replica = True
110 schema_path = TEST_SCHEMA
112 @classmethod
113 def setUpClass(cls) -> None:
114 # Create the postgres test server.
115 cls.postgresql = testing.postgresql.PostgresqlFactory(cache_initialized_db=True)
116 super().setUpClass()
118 @classmethod
119 def tearDownClass(cls) -> None:
120 # Clean up any lingering SQLAlchemy engines/connections
121 # so they're closed before we shut down the server.
122 gc.collect()
123 cls.postgresql.clear_cache()
124 super().tearDownClass()
126 def setUp(self) -> None:
127 self.server = self.postgresql()
129 def tearDown(self) -> None:
130 self.server = self.postgresql()
132 def make_instance(self, **kwargs: Any) -> ApdbConfig:
133 """Make config class instance used in all tests."""
134 kw = {
135 "db_url": self.server.url(),
136 "schema_file": TEST_SCHEMA,
137 "dia_object_index": self.dia_object_index,
138 "use_insert_id": self.enable_replica,
139 }
140 kw.update(kwargs)
141 return ApdbSql.init_database(**kw)
143 def getDiaObjects_table(self) -> ApdbTables:
144 """Return type of table returned from getDiaObjects method."""
145 return ApdbTables.DiaObjectLast
148@unittest.skipUnless(testing is not None, "testing.postgresql module not found")
149class ApdbPostgresNamespaceTestCase(ApdbPostgresTestCase):
150 """A test case for ApdbSql class using Postgres backend with schema name"""
152 # use mixed case to trigger quoting
153 namespace = "ApdbSchema"
155 def make_instance(self, **kwargs: Any) -> ApdbConfig:
156 """Make config class instance used in all tests."""
157 return super().make_instance(namespace=self.namespace, **kwargs)
160class ApdbSchemaUpdateSQLiteTestCase(ApdbSchemaUpdateTest, unittest.TestCase):
161 """A test case for schema updates using SQLite backend."""
163 def setUp(self) -> None:
164 self.tempdir = tempfile.mkdtemp()
165 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
167 def tearDown(self) -> None:
168 shutil.rmtree(self.tempdir, ignore_errors=True)
170 def make_instance(self, **kwargs: Any) -> ApdbConfig:
171 """Make config class instance used in all tests."""
172 kw = {
173 "db_url": self.db_url,
174 "schema_file": TEST_SCHEMA,
175 }
176 kw.update(kwargs)
177 return ApdbSql.init_database(**kw) # type: ignore[arg-type]
180class ApdbSQLiteFromUriTestCase(unittest.TestCase):
181 """A test case for for instantiating ApdbSql via URI."""
183 def setUp(self) -> None:
184 self.tempdir = tempfile.mkdtemp()
185 self.addCleanup(shutil.rmtree, self.tempdir, ignore_errors=True)
186 self.db_url = f"sqlite:///{self.tempdir}/apdb.sqlite3"
187 config = ApdbSql.init_database(db_url=self.db_url, schema_file=TEST_SCHEMA)
188 # TODO: This will need update when we switch to pydantic configs.
189 self.config_path = os.path.join(self.tempdir, "apdb-config.py")
190 config.save(self.config_path)
191 self.bad_config_path = os.path.join(self.tempdir, "not-config.py")
192 self.index_path = os.path.join(self.tempdir, "apdb-index.yaml")
193 with open(self.index_path, "w") as index_file:
194 print(f'label1: "{self.config_path}"', file=index_file)
195 print(f'"label2/pex_config": "{self.config_path}"', file=index_file)
196 print(f'bad-label: "{self.bad_config_path}"', file=index_file)
197 # File with incorrect format.
198 self.bad_index_path = os.path.join(self.tempdir, "apdb-index-bad.yaml")
199 with open(self.bad_index_path, "w") as index_file:
200 print(f'label1: ["{self.config_path}"]', file=index_file)
201 self.missing_index_path = os.path.join(self.tempdir, "no-apdb-index.yaml")
203 def test_make_apdb_from_path(self) -> None:
204 """Check that we can make APDB instance from config URI."""
205 Apdb.from_uri(self.config_path)
206 with self.assertRaises(FileNotFoundError):
207 Apdb.from_uri(self.bad_config_path)
209 def test_make_apdb_from_labels(self) -> None:
210 """Check that we can make APDB instance from config URI."""
211 # Replace DAX_APDB_INDEX_URI value
212 new_env = {"DAX_APDB_INDEX_URI": self.index_path}
213 with patch.dict(os.environ, new_env, clear=True):
214 Apdb.from_uri("label:label1")
215 Apdb.from_uri("label:label2")
216 # Label does not exist.
217 with self.assertRaises(ValueError):
218 Apdb.from_uri("label:not-a-label")
219 # Label exists but points to a missing config.
220 with self.assertRaises(FileNotFoundError):
221 Apdb.from_uri("label:bad-label")
223 def test_make_apdb_bad_index(self) -> None:
224 """Check what happens when DAX_APDB_INDEX_URI is broken."""
225 # envvar is set but empty.
226 new_env = {"DAX_APDB_INDEX_URI": ""}
227 with patch.dict(os.environ, new_env, clear=True):
228 with self.assertRaises(RuntimeError):
229 Apdb.from_uri("label:label")
231 # envvar is set to something non-existing.
232 new_env = {"DAX_APDB_INDEX_URI": self.missing_index_path}
233 with patch.dict(os.environ, new_env, clear=True):
234 with self.assertRaises(FileNotFoundError):
235 Apdb.from_uri("label:label")
237 # envvar points to an incorrect file.
238 new_env = {"DAX_APDB_INDEX_URI": self.bad_index_path}
239 with patch.dict(os.environ, new_env, clear=True):
240 with self.assertRaises(TypeError):
241 Apdb.from_uri("label:label")
244class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
245 """Run file leak tests."""
248def setup_module(module: Any) -> None:
249 """Configure pytest."""
250 lsst.utils.tests.init()
253if __name__ == "__main__":
254 lsst.utils.tests.init()
255 unittest.main()