Hide keyboard shortcuts

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/>. 

21 

22import os 

23from contextlib import contextmanager 

24import secrets 

25import unittest 

26import gc 

27 

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 

36 

37import sqlalchemy 

38 

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 

43 

44 

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

46class PostgresqlDatabaseTestCase(unittest.TestCase, DatabaseTests): 

47 

48 @classmethod 

49 def setUpClass(cls): 

50 cls.server = testing.postgresql.Postgresql() 

51 

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() 

58 

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) 

62 

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) 

66 

67 @contextmanager 

68 def asReadOnly(self, database: PostgresqlDatabase) -> PostgresqlDatabase: 

69 yield self.getNewConnection(database, writeable=False) 

70 

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 ) 

127 

128 

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

130class PostgresqlRegistryTests(RegistryTests): 

131 """Tests for `Registry` backed by a PostgreSQL database. 

132 

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 """ 

139 

140 @classmethod 

141 def setUpClass(cls): 

142 cls.server = testing.postgresql.Postgresql() 

143 

144 @classmethod 

145 def tearDownClass(cls): 

146 cls.server.stop() 

147 

148 @classmethod 

149 def getDataDir(cls) -> str: 

150 return os.path.normpath(os.path.join(os.path.dirname(__file__), "data", "registry")) 

151 

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) 

158 

159 

160class PostgresqlRegistryNameKeyCollMgrTestCase(PostgresqlRegistryTests, unittest.TestCase): 

161 """Tests for `Registry` backed by a PostgreSQL database. 

162 

163 This test case uses NameKeyCollectionManager. 

164 """ 

165 collectionsManager = "lsst.daf.butler.registry.collections.nameKey.NameKeyCollectionManager" 

166 

167 

168class PostgresqlRegistrySynthIntKeyCollMgrTestCase(PostgresqlRegistryTests, unittest.TestCase): 

169 """Tests for `Registry` backed by a PostgreSQL database. 

170 

171 This test case uses SynthIntKeyCollectionManager. 

172 """ 

173 collectionsManager = "lsst.daf.butler.registry.collections.synthIntKey.SynthIntKeyCollectionManager" 

174 

175 

176if __name__ == "__main__": 176 ↛ 177line 176 didn't jump to line 177, because the condition on line 176 was never true

177 unittest.main()