Coverage for tests/test_cliCmdConfigDump.py: 21%

113 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-10-02 08:00 +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 

28"""Unit tests for daf_butler CLI config-dump command. 

29""" 

30 

31import os 

32import os.path 

33import unittest 

34 

35import click 

36import yaml 

37from lsst.daf.butler.cli import butler 

38from lsst.daf.butler.cli.cmd import config_dump 

39from lsst.daf.butler.cli.opt import options_file_option 

40from lsst.daf.butler.cli.utils import LogCliRunner, clickResultMsg 

41from lsst.daf.butler.tests import CliCmdTestBase 

42 

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

44 

45 

46class ConfigDumpTest(CliCmdTestBase, unittest.TestCase): 

47 """Test the butler config-dump command line.""" 

48 

49 mockFuncName = "lsst.daf.butler.cli.cmd.commands.script.configDump" 

50 

51 @staticmethod 

52 def defaultExpected(): 

53 return {} 

54 

55 @staticmethod 

56 def command(): 

57 return config_dump 

58 

59 

60class ConfigDumpUseTest(unittest.TestCase): 

61 """Test executing the command.""" 

62 

63 def setUp(self): 

64 self.runner = LogCliRunner() 

65 

66 def test_stdout(self): 

67 """Test dumping the config to stdout.""" 

68 with self.runner.isolated_filesystem(): 

69 result = self.runner.invoke(butler.cli, ["create", "here"]) 

70 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

71 

72 # test dumping to stdout: 

73 result = self.runner.invoke(butler.cli, ["config-dump", "here"]) 

74 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

75 # check for some expected keywords: 

76 cfg = yaml.safe_load(result.stdout) 

77 self.assertIn("datastore", cfg) 

78 self.assertIn("composites", cfg["datastore"]) 

79 self.assertIn("storageClasses", cfg) 

80 

81 def test_file(self): 

82 """Test dumping the config to a file.""" 

83 with self.runner.isolated_filesystem(): 

84 result = self.runner.invoke(butler.cli, ["create", "here"]) 

85 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

86 result = self.runner.invoke(butler.cli, ["config-dump", "here", "--file=there"]) 

87 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

88 # check for some expected keywords: 

89 with open("there") as f: 

90 cfg = yaml.safe_load(f) 

91 self.assertIn("datastore", cfg) 

92 self.assertIn("composites", cfg["datastore"]) 

93 self.assertIn("storageClasses", cfg) 

94 

95 def test_subset(self): 

96 """Test selecting a subset of the config.""" 

97 with self.runner.isolated_filesystem(): 

98 result = self.runner.invoke(butler.cli, ["create", "here"]) 

99 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

100 result = self.runner.invoke(butler.cli, ["config-dump", "here", "--subset", "datastore"]) 

101 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

102 cfg = yaml.safe_load(result.stdout) 

103 # count the keys in the datastore config 

104 self.assertEqual(len(cfg), 8) 

105 self.assertIn("cls", cfg) 

106 self.assertIn("create", cfg) 

107 self.assertIn("formatters", cfg) 

108 self.assertIn("records", cfg) 

109 self.assertIn("root", cfg) 

110 self.assertIn("templates", cfg) 

111 

112 # Test that a subset that returns a scalar quantity does work. 

113 result = self.runner.invoke(butler.cli, ["config-dump", "here", "--subset", ".datastore.root"]) 

114 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

115 self.assertEqual(result.stdout.strip(), ".datastore.root: <butlerRoot>") 

116 

117 def test_invalidSubset(self): 

118 """Test selecting a subset key that does not exist in the config.""" 

119 with self.runner.isolated_filesystem(): 

120 result = self.runner.invoke(butler.cli, ["create", "here"]) 

121 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

122 # test dumping to stdout: 

123 result = self.runner.invoke(butler.cli, ["config-dump", "here", "--subset", "foo"]) 

124 self.assertEqual(result.exit_code, 1) 

125 # exception type is click.Exit, and its argument is a return code 

126 self.assertEqual(result.exception.args, (1,)) 

127 

128 def test_presets(self): 

129 """Test that file overrides can set command line options in bulk.""" 

130 with self.runner.isolated_filesystem(): 

131 result = self.runner.invoke(butler.cli, ["create", "here"]) 

132 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

133 overrides_path = os.path.join(TESTDIR, "data", "config-overrides.yaml") 

134 

135 # Run with a presets file 

136 result = self.runner.invoke(butler.cli, ["config-dump", "here", "--options-file", overrides_path]) 

137 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

138 cfg = yaml.safe_load(result.stdout) 

139 # Look for datastore information 

140 self.assertIn("formatters", cfg) 

141 self.assertIn("root", cfg) 

142 

143 # Now run with an explicit subset and presets 

144 result = self.runner.invoke( 

145 butler.cli, ["config-dump", "here", f"-@{overrides_path}", "--subset", ".registry"] 

146 ) 

147 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

148 cfg = yaml.safe_load(result.stdout) 

149 # Look for datastore information 

150 self.assertNotIn("formatters", cfg) 

151 self.assertIn("managers", cfg) 

152 

153 # Now with subset before presets -- explicit always trumps 

154 # presets. 

155 result = self.runner.invoke( 

156 butler.cli, ["config-dump", "here", "--subset", ".registry", "--options-file", overrides_path] 

157 ) 

158 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

159 cfg = yaml.safe_load(result.stdout) 

160 # Look for datastore information 

161 self.assertNotIn("formatters", cfg) 

162 self.assertIn("managers", cfg) 

163 

164 configfile = "overrides.yaml" 

165 outfile = "repodef.yaml" 

166 # Check that a misspelled command option causes an error: 

167 with open(configfile, "w") as f: 

168 f.write(yaml.dump({"config-dump": {"fil": outfile}})) 

169 result = self.runner.invoke(butler.cli, ["config-dump", "here", f"-@{configfile}"]) 

170 self.assertNotEqual(result.exit_code, 0, clickResultMsg(result)) 

171 

172 # Check that an option that declares a different command argument 

173 # name is mapped correctly. 

174 # Note that the option `config-dump --file` 

175 # becomes the `outfile` argument in `def config_dump(..., outfile)` 

176 # and we use the option name "file" in the presets file. 

177 with open(configfile, "w") as f: 

178 f.write(yaml.dump({"config-dump": {"file": outfile}})) 

179 result = self.runner.invoke(butler.cli, ["config-dump", "here", f"-@{configfile}"]) 

180 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

181 self.assertTrue(os.path.exists(outfile)) 

182 

183 def test_presetsDashedName(self): 

184 """Test file overrides when the option has a dash in its name.""" 

185 

186 # Instead of using `butler config-dump` as we do in other tests, 

187 # create a small command for testing, because config-dump does 

188 # not have any options with dashes in the name. 

189 @click.command() 

190 @click.option("--test-option") 

191 @options_file_option() 

192 def cmd(test_option): 

193 print(test_option) 

194 

195 configfile = "overrides.yaml" 

196 val = "foo" 

197 with self.runner.isolated_filesystem(): 

198 with open(configfile, "w") as f: 

199 f.write(yaml.dump({"cmd": {"test-option": val}})) 

200 result = self.runner.invoke(cmd, ["-@", configfile]) 

201 self.assertEqual(result.exit_code, 0, clickResultMsg(result)) 

202 self.assertTrue(val in result.output) 

203 

204 

205if __name__ == "__main__": 

206 unittest.main()