Coverage for tests/test_cli.py: 28%

60 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-28 02:59 -0700

1# This file is part of astro_metadata_translator. 

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 LICENSE file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12import logging 

13import os.path 

14import tempfile 

15import traceback 

16import unittest 

17 

18import click.testing 

19import yaml 

20 

21from astro_metadata_translator import ObservationGroup 

22from astro_metadata_translator.cli.astrometadata import main as astrometadata 

23from astro_metadata_translator.indexing import read_index, read_sidecar 

24 

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

26 

27 

28def click_result_msg(result: click.testing.Result) -> str: 

29 """Get a standard assert message from a click result. 

30 

31 Parameters 

32 ---------- 

33 result : click.testing.Result 

34 The result object returned from `click.testing.CliRunner.invoke`. 

35 

36 Returns 

37 ------- 

38 msg : `str` 

39 The message string. 

40 """ 

41 msg = f"""\noutput: {result.output}\nexception: {result.exception}""" 

42 if result.exception: 

43 msg += f"""\ntraceback: {"".join(traceback.format_tb(result.exception.__traceback__))}""" 

44 return msg 

45 

46 

47class TestCLI(unittest.TestCase): 

48 """Test astrometadata command line.""" 

49 

50 def setUp(self) -> None: 

51 self.runner = click.testing.CliRunner() 

52 

53 def test_package_load(self) -> None: 

54 """Test that -p option fails properly.""" 

55 # For unknown reasons the click runner does not capture the log 

56 # message from this so use the unittest capture instead. 

57 with self.assertLogs(level=logging.WARNING) as cm: 

58 result = self.runner.invoke( 

59 astrometadata, 

60 [ 

61 "--packages", 

62 "lsst.not.found", 

63 "dump", 

64 os.path.join(TESTDIR, "data", "fitsheader-hsc-HSCA04090107.json"), 

65 ], 

66 ) 

67 self.assertEqual(result.exit_code, 0, click_result_msg(result)) 

68 self.assertIn("Failed to import translator module", cm.output[0], click_result_msg(result)) 

69 

70 def test_translate(self) -> None: 

71 """Test astrometadata translate.""" 

72 result = self.runner.invoke( 

73 astrometadata, ["translate", os.path.join(TESTDIR, "data", "fitsheader-decam.yaml")] 

74 ) 

75 self.assertEqual(result.exit_code, 0, click_result_msg(result)) 

76 self.assertIn("instrument: DECam", result.output, click_result_msg(result)) 

77 

78 result = self.runner.invoke( 

79 astrometadata, 

80 ["--traceback", "translate", os.path.join(TESTDIR, "data", "bad-sidecar.json")], 

81 ) 

82 self.assertEqual(result.exit_code, 1, click_result_msg(result)) 

83 self.assertIn("ValueError", result.output, click_result_msg(result)) 

84 

85 def test_dump(self) -> None: 

86 """Test that YAML is dumped.""" 

87 # Use warning log level to hide the INFO message. 

88 result = self.runner.invoke( 

89 astrometadata, 

90 [ 

91 "--log-level", 

92 "WARNING", 

93 "dump", 

94 os.path.join(TESTDIR, "data", "fitsheader-hsc-HSCA04090107.json"), 

95 ], 

96 ) 

97 self.assertEqual(result.exit_code, 0, click_result_msg(result)) 

98 parsed = yaml.safe_load(result.output) 

99 self.assertEqual(parsed["T_EFMN32"], 50) 

100 

101 def test_write_sidecar(self): 

102 """Write a simple sidecar file.""" 

103 # Sidecar files are written next to file. 

104 with self.runner.isolated_filesystem(): 

105 # Test that we set exit status to 1 if we find no files. 

106 result = self.runner.invoke(astrometadata, ["write-sidecar", "-c", "metadata", os.path.curdir]) 

107 self.assertEqual(result.exit_code, 1, click_result_msg(result)) 

108 self.assertIn("Found no files", result.output, click_result_msg(result)) 

109 

110 # Test that we can write a sidecar file. 

111 infile = "fitsheader-decam.yaml" 

112 os.symlink(os.path.join(TESTDIR, "data", infile), infile) 

113 result = self.runner.invoke( 

114 astrometadata, ["write-sidecar", "-c", "metadata", "--regex", r".*\.yaml", os.path.curdir] 

115 ) 

116 self.assertEqual(result.exit_code, 0, click_result_msg(result)) 

117 

118 jfile = os.path.splitext(infile)[0] + ".json" 

119 content = read_sidecar(jfile) 

120 self.assertEqual(content["PROGRAM"], "supernova") 

121 

122 # Test that we set bad status if a file can not be translated. 

123 os.symlink(os.path.join(TESTDIR, "data", "small.fits"), "bad.fits") 

124 result = self.runner.invoke(astrometadata, ["write-sidecar", os.path.curdir]) 

125 self.assertEqual(result.exit_code, 1, click_result_msg(result)) 

126 self.assertIn("No files processed", result.output, click_result_msg(result)) 

127 

128 def test_write_index(self): 

129 """Write a simple index file.""" 

130 with tempfile.NamedTemporaryFile(suffix=".json") as temp: 

131 temp.close() 

132 result = self.runner.invoke( 

133 astrometadata, 

134 [ 

135 "write-index", 

136 "-o", 

137 temp.name, 

138 "--regex", 

139 r"fitsheader.*\.yaml", 

140 os.path.join(TESTDIR, "data"), 

141 ], 

142 ) 

143 self.assertEqual(result.exit_code, 0, click_result_msg(result)) 

144 

145 ind = read_index(temp.name) 

146 self.assertIsInstance(ind, ObservationGroup) 

147 self.assertGreaterEqual(len(ind), 10) 

148 

149 

150if __name__ == "__main__": 

151 unittest.main()