Coverage for tests/test_postgresql.py : 62%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
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/>.
22import os
23from contextlib import contextmanager
24import secrets
25import unittest
26import gc
28try:
29 # It's possible but silly to have testing.postgresql installed without
30 # having the postgresql server installed (because then nothing in
31 # testing.postgresql would work), so we use the presence of that module
32 # to test whether we can expect the server to be available.
33 import testing.postgresql
34except ImportError:
35 testing = None
37import sqlalchemy
39from lsst.daf.butler import ddl
40from lsst.daf.butler.registry.databases.postgresql import PostgresqlDatabase
41from lsst.daf.butler.registry import Registry
42from lsst.daf.butler.registry.tests import DatabaseTests, RegistryTests
45@unittest.skipUnless(testing is not None, "testing.postgresql module not found")
46class PostgresqlDatabaseTestCase(unittest.TestCase, DatabaseTests):
48 @classmethod
49 def setUpClass(cls):
50 cls.server = testing.postgresql.Postgresql()
52 @classmethod
53 def tearDownClass(cls):
54 # Clean up any lingering SQLAlchemy engines/connections
55 # so they're closed before we shut down the server.
56 gc.collect()
57 cls.server.stop()
59 def makeEmptyDatabase(self, origin: int = 0) -> PostgresqlDatabase:
60 namespace = f"namespace_{secrets.token_hex(8).lower()}"
61 return PostgresqlDatabase.fromUri(origin=origin, uri=self.server.url(), namespace=namespace)
63 def getNewConnection(self, database: PostgresqlDatabase, *, writeable: bool) -> PostgresqlDatabase:
64 return PostgresqlDatabase.fromUri(origin=database.origin, uri=self.server.url(),
65 namespace=database.namespace, writeable=writeable)
67 @contextmanager
68 def asReadOnly(self, database: PostgresqlDatabase) -> PostgresqlDatabase:
69 yield self.getNewConnection(database, writeable=False)
71 def testNameShrinking(self):
72 """Test that too-long names for database entities other than tables
73 and columns (which we preserve, and just expect to fit) are shrunk.
74 """
75 db = self.makeEmptyDatabase(origin=1)
76 with db.declareStaticTables(create=True) as context:
77 # Table and field names are each below the 63-char limit even when
78 # accounting for the prefix, but their combination (which will
79 # appear in sequences and constraints) is not.
80 tableName = "a_table_with_a_very_very_long_42_char_name"
81 fieldName1 = "a_column_with_a_very_very_long_43_char_name"
82 fieldName2 = "another_column_with_a_very_very_long_49_char_name"
83 context.addTable(
84 tableName,
85 ddl.TableSpec(
86 fields=[
87 ddl.FieldSpec(
88 fieldName1,
89 dtype=sqlalchemy.BigInteger,
90 autoincrement=True,
91 primaryKey=True
92 ),
93 ddl.FieldSpec(
94 fieldName2,
95 dtype=sqlalchemy.String,
96 length=16,
97 nullable=False,
98 ),
99 ],
100 unique={(fieldName2,)},
101 )
102 )
103 # Add another table, this time dynamically, with a foreign key to the
104 # first table.
105 db.ensureTableExists(
106 tableName + "_b",
107 ddl.TableSpec(
108 fields=[
109 ddl.FieldSpec(
110 fieldName1 + "_b",
111 dtype=sqlalchemy.BigInteger,
112 autoincrement=True,
113 primaryKey=True
114 ),
115 ddl.FieldSpec(
116 fieldName2 + "_b",
117 dtype=sqlalchemy.String,
118 length=16,
119 nullable=False,
120 ),
121 ],
122 foreignKeys=[
123 ddl.ForeignKeySpec(tableName, source=(fieldName2 + "_b",), target=(fieldName2,)),
124 ]
125 )
126 )
129@unittest.skipUnless(testing is not None, "testing.postgresql module not found")
130class PostgresqlRegistryTests(RegistryTests):
131 """Tests for `Registry` backed by a PostgreSQL database.
133 Note
134 ----
135 This is not a subclass of `unittest.TestCase` but to avoid repetition it
136 defines methods that override `unittest.TestCase` methods. To make this
137 work sublasses have to have this class first in the bases list.
138 """
140 @classmethod
141 def setUpClass(cls):
142 cls.server = testing.postgresql.Postgresql()
144 @classmethod
145 def tearDownClass(cls):
146 cls.server.stop()
148 @classmethod
149 def getDataDir(cls) -> str:
150 return os.path.normpath(os.path.join(os.path.dirname(__file__), "data", "registry"))
152 def makeRegistry(self) -> Registry:
153 namespace = f"namespace_{secrets.token_hex(8).lower()}"
154 config = self.makeRegistryConfig()
155 config["db"] = self.server.url()
156 config["namespace"] = namespace
157 return Registry.fromConfig(config, create=True)
160class PostgresqlRegistryNameKeyCollMgrTestCase(PostgresqlRegistryTests, unittest.TestCase):
161 """Tests for `Registry` backed by a PostgreSQL database.
163 This test case uses NameKeyCollectionManager.
164 """
165 collectionsManager = "lsst.daf.butler.registry.collections.nameKey.NameKeyCollectionManager"
168class PostgresqlRegistrySynthIntKeyCollMgrTestCase(PostgresqlRegistryTests, unittest.TestCase):
169 """Tests for `Registry` backed by a PostgreSQL database.
171 This test case uses SynthIntKeyCollectionManager.
172 """
173 collectionsManager = "lsst.daf.butler.registry.collections.synthIntKey.SynthIntKeyCollectionManager"
176if __name__ == "__main__": 176 ↛ 177line 176 didn't jump to line 177, because the condition on line 176 was never true
177 unittest.main()