apache_test.py
1 """Tests for letshelp.letshelp_letsencrypt_apache.py""" 2 import argparse 3 import functools 4 import os 5 import pkg_resources 6 import subprocess 7 import tarfile 8 import tempfile 9 import unittest 10 11 import mock 12 13 import letshelp_letsencrypt.apache as letshelp_le_apache 14 15 16 _PARTIAL_CONF_PATH = os.path.join("mods-available", "ssl.load") 17 _PARTIAL_LINK_PATH = os.path.join("mods-enabled", "ssl.load") 18 _CONFIG_FILE = pkg_resources.resource_filename( 19 __name__, os.path.join("testdata", _PARTIAL_CONF_PATH)) 20 _PASSWD_FILE = pkg_resources.resource_filename( 21 __name__, os.path.join("testdata", "uncommonly_named_p4sswd")) 22 _KEY_FILE = pkg_resources.resource_filename( 23 __name__, os.path.join("testdata", "uncommonly_named_k3y")) 24 _SECRET_FILE = pkg_resources.resource_filename( 25 __name__, os.path.join("testdata", "super_secret_file.txt")) 26 27 28 _MODULE_NAME = "letshelp_letsencrypt.apache" 29 30 31 _COMPILE_SETTINGS = """Server version: Apache/2.4.10 (Debian) 32 Server built: Mar 15 2015 09:51:43 33 Server's Module Magic Number: 20120211:37 34 Server loaded: APR 1.5.1, APR-UTIL 1.5.4 35 Compiled using: APR 1.5.1, APR-UTIL 1.5.4 36 Architecture: 64-bit 37 Server MPM: event 38 threaded: yes (fixed thread count) 39 forked: yes (variable process count) 40 Server compiled with.... 41 -D APR_HAS_SENDFILE 42 -D APR_HAS_MMAP 43 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) 44 -D APR_USE_SYSVSEM_SERIALIZE 45 -D APR_USE_PTHREAD_SERIALIZE 46 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT 47 -D APR_HAS_OTHER_CHILD 48 -D AP_HAVE_RELIABLE_PIPED_LOGS 49 -D DYNAMIC_MODULE_LIMIT=256 50 -D HTTPD_ROOT="/etc/apache2" 51 -D SUEXEC_BIN="/usr/lib/apache2/suexec" 52 -D DEFAULT_PIDLOG="/var/run/apache2.pid" 53 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" 54 -D DEFAULT_ERRORLOG="logs/error_log" 55 -D AP_TYPES_CONFIG_FILE="mime.types" 56 -D SERVER_CONFIG_FILE="apache2.conf" 57 58 """ 59 60 61 class LetsHelpApacheTest(unittest.TestCase): 62 @mock.patch(_MODULE_NAME + ".copy_config") 63 def test_make_and_verify_selection(self, mock_copy_config): 64 mock_copy_config.return_value = (["apache2.conf"], ["apache2"]) 65 66 with mock.patch("__builtin__.raw_input") as mock_input: 67 with mock.patch(_MODULE_NAME + ".sys.stdout"): 68 mock_input.side_effect = ["Yes", "No"] 69 letshelp_le_apache.make_and_verify_selection("root", "temp") 70 self.assertRaises( 71 SystemExit, letshelp_le_apache.make_and_verify_selection, 72 "server_root", "temp_dir") 73 74 def test_copy_config(self): 75 tempdir = tempfile.mkdtemp() 76 server_root = pkg_resources.resource_filename(__name__, "testdata") 77 letshelp_le_apache.copy_config(server_root, tempdir) 78 79 temp_testdata = os.path.join(tempdir, "testdata") 80 self.assertFalse(os.path.exists(os.path.join( 81 temp_testdata, os.path.basename(_PASSWD_FILE)))) 82 self.assertFalse(os.path.exists(os.path.join( 83 temp_testdata, os.path.basename(_KEY_FILE)))) 84 self.assertFalse(os.path.exists(os.path.join( 85 temp_testdata, os.path.basename(_SECRET_FILE)))) 86 self.assertTrue(os.path.exists(os.path.join( 87 temp_testdata, _PARTIAL_CONF_PATH))) 88 self.assertTrue(os.path.exists(os.path.join( 89 temp_testdata, _PARTIAL_LINK_PATH))) 90 91 def test_copy_file_without_comments(self): 92 dest = tempfile.mkstemp()[1] 93 letshelp_le_apache.copy_file_without_comments(_PASSWD_FILE, dest) 94 95 with open(_PASSWD_FILE) as original: 96 with open(dest) as copy: 97 for original_line, copied_line in zip(original, copy): 98 self.assertEqual(original_line, copied_line) 99 100 @mock.patch(_MODULE_NAME + ".subprocess.Popen") 101 def test_safe_config_file(self, mock_popen): 102 mock_popen().communicate.return_value = ("PEM RSA private key", None) 103 self.assertFalse(letshelp_le_apache.safe_config_file("filename")) 104 105 mock_popen().communicate.return_value = ("ASCII text", None) 106 self.assertFalse(letshelp_le_apache.safe_config_file(_PASSWD_FILE)) 107 self.assertFalse(letshelp_le_apache.safe_config_file(_KEY_FILE)) 108 self.assertFalse(letshelp_le_apache.safe_config_file(_SECRET_FILE)) 109 self.assertTrue(letshelp_le_apache.safe_config_file(_CONFIG_FILE)) 110 111 @mock.patch(_MODULE_NAME + ".subprocess.Popen") 112 def test_tempdir(self, mock_popen): 113 mock_popen().communicate.side_effect = [ 114 ("version", None), ("modules", None), ("vhosts", None)] 115 args = _get_args() 116 117 tempdir = letshelp_le_apache.setup_tempdir(args) 118 119 with open(os.path.join(tempdir, "config_file")) as config_fd: 120 self.assertEqual(config_fd.read(), args.config_file + "\n") 121 122 with open(os.path.join(tempdir, "version")) as version_fd: 123 self.assertEqual(version_fd.read(), "version") 124 125 with open(os.path.join(tempdir, "modules")) as modules_fd: 126 self.assertEqual(modules_fd.read(), "modules") 127 128 with open(os.path.join(tempdir, "vhosts")) as vhosts_fd: 129 self.assertEqual(vhosts_fd.read(), "vhosts") 130 131 @mock.patch(_MODULE_NAME + ".subprocess.check_call") 132 def test_verify_config(self, mock_check_call): 133 args = _get_args() 134 mock_check_call.side_effect = [ 135 None, OSError, subprocess.CalledProcessError(1, "apachectl")] 136 137 letshelp_le_apache.verify_config(args) 138 self.assertRaises(SystemExit, letshelp_le_apache.verify_config, args) 139 self.assertRaises(SystemExit, letshelp_le_apache.verify_config, args) 140 141 @mock.patch(_MODULE_NAME + ".subprocess.Popen") 142 def test_locate_config(self, mock_popen): 143 mock_popen().communicate.side_effect = [ 144 OSError, ("bad_output", None), (_COMPILE_SETTINGS, None)] 145 146 self.assertRaises( 147 SystemExit, letshelp_le_apache.locate_config, "ctl") 148 self.assertRaises( 149 SystemExit, letshelp_le_apache.locate_config, "ctl") 150 server_root, config_file = letshelp_le_apache.locate_config("ctl") 151 self.assertEqual(server_root, "/etc/apache2") 152 self.assertEqual(config_file, "apache2.conf") 153 154 @mock.patch(_MODULE_NAME + ".argparse") 155 def test_get_args(self, mock_argparse): 156 argv = ["-d", "/etc/apache2"] 157 mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv) 158 self.assertRaises(SystemExit, letshelp_le_apache.get_args) 159 160 server_root = "/etc/apache2" 161 config_file = server_root + "/apache2.conf" 162 argv = ["-d", server_root, "-f", config_file] 163 mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv) 164 args = letshelp_le_apache.get_args() 165 self.assertEqual(args.apache_ctl, "apachectl") 166 self.assertEqual(args.server_root, server_root) 167 self.assertEqual(args.config_file, os.path.basename(config_file)) 168 169 server_root = "/etc/apache2" 170 config_file = "/etc/httpd/httpd.conf" 171 argv = ["-d", server_root, "-f", config_file] 172 mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv) 173 self.assertRaises(SystemExit, letshelp_le_apache.get_args) 174 175 def test_main_with_args(self): 176 with mock.patch(_MODULE_NAME + ".get_args"): 177 self._test_main_common() 178 179 def test_main_without_args(self): 180 with mock.patch(_MODULE_NAME + ".get_args") as get_args: 181 args = _get_args() 182 server_root, config_file = args.server_root, args.config_file 183 args.server_root = args.config_file = None 184 get_args.return_value = args 185 with mock.patch(_MODULE_NAME + ".locate_config") as locate: 186 locate.return_value = (server_root, config_file) 187 self._test_main_common() 188 189 def _test_main_common(self): 190 with mock.patch(_MODULE_NAME + ".verify_config"): 191 with mock.patch(_MODULE_NAME + ".setup_tempdir") as mock_setup: 192 tempdir_path = tempfile.mkdtemp() 193 mock_setup.return_value = tempdir_path 194 with mock.patch(_MODULE_NAME + ".make_and_verify_selection"): 195 testdir_basename = "test" 196 os.mkdir(os.path.join(tempdir_path, testdir_basename)) 197 198 letshelp_le_apache.main() 199 200 tar = tarfile.open(os.path.join( 201 tempdir_path, "config.tar.gz")) 202 203 tempdir = tar.next() 204 self.assertTrue(tempdir.isdir()) 205 self.assertEqual(tempdir.name, ".") 206 207 testdir = tar.next() 208 self.assertTrue(testdir.isdir()) 209 self.assertEqual(os.path.basename(testdir.name), 210 testdir_basename) 211 212 self.assertEqual(tar.next(), None) 213 214 215 def _create_mock_parser(argv): 216 parser = argparse.ArgumentParser() 217 mock_parser = mock.MagicMock() 218 mock_parser.add_argument = parser.add_argument 219 mock_parser.parse_args = functools.partial(parser.parse_args, argv) 220 221 return mock_parser 222 223 224 def _get_args(): 225 args = argparse.Namespace() 226 args.apache_ctl = "apache_ctl" 227 args.config_file = "config_file" 228 args.server_root = "server_root" 229 230 return args 231 232 233 if __name__ == "__main__": 234 unittest.main() # pragma: no cover