Coverage for tests/test_cli.py: 28%
60 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-06 03:48 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-06 03:48 -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.
12import logging
13import os.path
14import tempfile
15import traceback
16import unittest
18import click.testing
19import yaml
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
25TESTDIR = os.path.abspath(os.path.dirname(__file__))
28def click_result_msg(result: click.testing.Result) -> str:
29 """Get a standard assert message from a click result.
31 Parameters
32 ----------
33 result : click.testing.Result
34 The result object returned from `click.testing.CliRunner.invoke`.
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
47class TestCLI(unittest.TestCase):
48 """Test astrometadata command line."""
50 def setUp(self) -> None:
51 self.runner = click.testing.CliRunner()
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))
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))
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))
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)
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))
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))
118 jfile = os.path.splitext(infile)[0] + ".json"
119 content = read_sidecar(jfile)
120 self.assertEqual(content["PROGRAM"], "supernova")
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))
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))
145 ind = read_index(temp.name)
146 self.assertIsInstance(ind, ObservationGroup)
147 self.assertGreaterEqual(len(ind), 10)
150if __name__ == "__main__":
151 unittest.main()