Coverage for tests/test_server.py: 37%

71 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-03 16:25 +0000

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 software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <http://www.gnu.org/licenses/>. 

27 

28import os.path 

29import unittest 

30import uuid 

31 

32try: 

33 # Failing to import any of these should disable the tests. 

34 from fastapi.testclient import TestClient 

35 from lsst.daf.butler.remote_butler import RemoteButler 

36 from lsst.daf.butler.remote_butler.server import Factory, app, factory_dependency 

37except ImportError: 

38 TestClient = None 

39 app = None 

40 

41from lsst.daf.butler import Butler, DataCoordinate, DatasetRef, MissingDatasetTypeError, StorageClassFactory 

42from lsst.daf.butler.tests import DatastoreMock 

43from lsst.daf.butler.tests.utils import MetricTestRepo, makeTestTempDir, removeTestTempDir 

44 

45TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

46 

47 

48def _make_remote_butler(http_client): 

49 return RemoteButler( 

50 config={ 

51 "remote_butler": { 

52 # This URL is ignored because we override the HTTP client, but 

53 # must be valid to satisfy the config validation 

54 "url": "https://test.example" 

55 } 

56 }, 

57 http_client=http_client, 

58 ) 

59 

60 

61@unittest.skipIf(TestClient is None or app is None, "FastAPI not installed.") 

62class ButlerClientServerTestCase(unittest.TestCase): 

63 """Test for Butler client/server.""" 

64 

65 @classmethod 

66 def setUpClass(cls): 

67 cls.storageClassFactory = StorageClassFactory() 

68 

69 # First create a butler and populate it. 

70 cls.root = makeTestTempDir(TESTDIR) 

71 cls.repo = MetricTestRepo(root=cls.root, configFile=os.path.join(TESTDIR, "config/basic/butler.yaml")) 

72 # Override the server's Butler initialization to point at our test repo 

73 server_butler = Butler.from_config(cls.root, writeable=True) 

74 

75 # Not yet testing butler.get() 

76 DatastoreMock.apply(server_butler) 

77 

78 def create_factory_dependency(): 

79 return Factory(butler=server_butler) 

80 

81 app.dependency_overrides[factory_dependency] = create_factory_dependency 

82 

83 # Set up the RemoteButler that will connect to the server 

84 cls.client = TestClient(app) 

85 cls.butler = _make_remote_butler(cls.client) 

86 

87 # Populate the test server. 

88 server_butler.import_(filename=os.path.join(TESTDIR, "data", "registry", "base.yaml")) 

89 server_butler.import_(filename=os.path.join(TESTDIR, "data", "registry", "datasets-uuid.yaml")) 

90 

91 @classmethod 

92 def tearDownClass(cls): 

93 del app.dependency_overrides[factory_dependency] 

94 removeTestTempDir(cls.root) 

95 

96 def test_simple(self): 

97 response = self.client.get("/butler/v1/universe") 

98 self.assertEqual(response.status_code, 200) 

99 self.assertIn("namespace", response.json()) 

100 

101 def test_remote_butler(self): 

102 universe = self.butler.dimensions 

103 self.assertEqual(universe.namespace, "daf_butler") 

104 self.assertFalse(self.butler.isWriteable()) 

105 

106 def test_get_dataset_type(self): 

107 bias_type = self.butler.get_dataset_type("bias") 

108 self.assertEqual(bias_type.name, "bias") 

109 

110 with self.assertRaises(MissingDatasetTypeError): 

111 self.butler.get_dataset_type("not_bias") 

112 

113 def test_find_dataset(self): 

114 storage_class = self.storageClassFactory.getStorageClass("Exposure") 

115 

116 ref = self.butler.find_dataset("bias", collections="imported_g", detector=1, instrument="Cam1") 

117 self.assertIsInstance(ref, DatasetRef) 

118 self.assertEqual(ref.id, uuid.UUID("e15ab039-bc8b-4135-87c5-90902a7c0b22")) 

119 self.assertFalse(ref.dataId.hasRecords()) 

120 

121 # Try again with variation of parameters. 

122 ref_new = self.butler.find_dataset( 

123 "bias", 

124 {"detector": 1}, 

125 collections="imported_g", 

126 instrument="Cam1", 

127 dimension_records=True, 

128 ) 

129 self.assertEqual(ref_new, ref) 

130 self.assertTrue(ref_new.dataId.hasRecords()) 

131 

132 ref_new = self.butler.find_dataset( 

133 ref.datasetType, 

134 DataCoordinate.standardize(detector=1, instrument="Cam1", universe=self.butler.dimensions), 

135 collections="imported_g", 

136 storage_class=storage_class, 

137 ) 

138 self.assertEqual(ref_new, ref) 

139 

140 ref2 = self.butler.get_dataset(ref.id) 

141 self.assertEqual(ref2, ref) 

142 

143 # Use detector name to find it. 

144 ref3 = self.butler.find_dataset( 

145 ref.datasetType, 

146 collections="imported_g", 

147 instrument="Cam1", 

148 full_name="Aa", 

149 ) 

150 self.assertEqual(ref2, ref3) 

151 

152 # Try expanded refs. 

153 self.assertFalse(ref.dataId.hasRecords()) 

154 expanded = self.butler.get_dataset(ref.id, dimension_records=True) 

155 self.assertTrue(expanded.dataId.hasRecords()) 

156 

157 # The test datasets are all Exposure so storage class conversion 

158 # can not be tested until we fix that. For now at least test the 

159 # code paths. 

160 bias = self.butler.get_dataset(ref.id, storage_class=storage_class) 

161 self.assertEqual(bias.datasetType.storageClass, storage_class) 

162 

163 # Unknown dataset should not fail. 

164 self.assertIsNone(self.butler.get_dataset(uuid.uuid4())) 

165 self.assertIsNone(self.butler.get_dataset(uuid.uuid4(), storage_class="NumpyArray")) 

166 

167 

168if __name__ == "__main__": 

169 unittest.main()