text: # coding=utf-8
  """Tests for certbot._internal.main."""
  # pylint: disable=too-many-lines
  from __future__ import print_function
  
  import datetime
  import itertools
  import json
  import shutil
  import sys
  import tempfile
  import traceback
  import unittest
  
  import josepy as jose
  try:
      import mock
  except ImportError: # pragma: no cover
      from unittest import mock
  import pytz
  import six
  from six.moves import reload_module  # pylint: disable=import-error
  
  from certbot import crypto_util
  from certbot import errors
  from certbot import interfaces  # pylint: disable=unused-import
  from certbot import util
  from certbot._internal import account
  from certbot._internal import cli
  from certbot._internal import configuration
  from certbot._internal import constants
  from certbot._internal import main
  from certbot._internal import updater
  from certbot._internal.plugins import disco
  from certbot._internal.plugins import manual
  from certbot._internal.plugins import null
  from certbot.compat import filesystem
  from certbot.compat import os
  from certbot.plugins import enhancements
  import certbot.tests.util as test_util
  
  
  CERT_PATH = test_util.vector_path('cert_512.pem')
  CERT = test_util.vector_path('cert_512.pem')
  CSR = test_util.vector_path('csr_512.der')
  KEY = test_util.vector_path('rsa256_key.pem')
  JWK = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem'))
  RSA2048_KEY_PATH = test_util.vector_path('rsa2048_key.pem')
  SS_CERT_PATH = test_util.vector_path('cert_2048.pem')
  
  
  class TestHandleIdenticalCerts(unittest.TestCase):
      """Test for certbot._internal.main._handle_identical_cert_request"""
      def test_handle_identical_cert_request_pending(self):
          mock_lineage = mock.Mock()
          mock_lineage.ensure_deployed.return_value = False
          # pylint: disable=protected-access
          ret = main._handle_identical_cert_request(mock.Mock(), mock_lineage)
          self.assertEqual(ret, ("reinstall", mock_lineage))
  
  
  class RunTest(test_util.ConfigTestCase):
      """Tests for certbot._internal.main.run."""
  
      def setUp(self):
          super(RunTest, self).setUp()
          self.domain = 'example.org'
          patches = [
              mock.patch('certbot._internal.main._get_and_save_cert'),
              mock.patch('certbot._internal.main.display_ops.success_installation'),
              mock.patch('certbot._internal.main.display_ops.success_renewal'),
              mock.patch('certbot._internal.main._init_le_client'),
              mock.patch('certbot._internal.main._suggest_donation_if_appropriate'),
              mock.patch('certbot._internal.main._report_new_cert'),
              mock.patch('certbot._internal.main._find_cert'),
              mock.patch('certbot._internal.eff.handle_subscription'),
          ]
  
          self.mock_auth = patches[0].start()
          self.mock_success_installation = patches[1].start()
          self.mock_success_renewal = patches[2].start()
          self.mock_init = patches[3].start()
          self.mock_suggest_donation = patches[4].start()
          self.mock_report_cert = patches[5].start()
          self.mock_find_cert = patches[6].start()
          self.mock_subscription = patches[7].start()
          for patch in patches:
              self.addCleanup(patch.stop)
  
      def _call(self):
          args = '-a webroot -i null -d {0}'.format(self.domain).split()
          plugins = disco.PluginsRegistry.find_all()
          config = configuration.NamespaceConfig(
              cli.prepare_and_parse_args(plugins, args))
  
          from certbot._internal.main import run
          run(config, plugins)
  
      def test_newcert_success(self):
          self.mock_auth.return_value = mock.Mock()
          self.mock_find_cert.return_value = True, None
          self._call()
          self.mock_success_installation.assert_called_once_with([self.domain])
  
      def test_reinstall_success(self):
          self.mock_auth.return_value = mock.Mock()
          self.mock_find_cert.return_value = False, mock.Mock()
          self._call()
          self.mock_success_installation.assert_called_once_with([self.domain])
  
      def test_renewal_success(self):
          self.mock_auth.return_value = mock.Mock()
          self.mock_find_cert.return_value = True, mock.Mock()
          self._call()
          self.mock_success_renewal.assert_called_once_with([self.domain])
  
      @mock.patch('certbot._internal.main.plug_sel.choose_configurator_plugins')
      def test_run_enhancement_not_supported(self, mock_choose):
          mock_choose.return_value = (null.Installer(self.config, "null"), None)
          plugins = disco.PluginsRegistry.find_all()
          self.config.auto_hsts = True
          self.assertRaises(errors.NotSupportedError,
                            main.run,
                            self.config, plugins)
  
  
  class CertonlyTest(unittest.TestCase):
      """Tests for certbot._internal.main.certonly."""
  
      def setUp(self):
          self.get_utility_patch = test_util.patch_get_utility()
          self.mock_get_utility = self.get_utility_patch.start()
  
      def tearDown(self):
          self.get_utility_patch.stop()
  
      def _call(self, args):
          plugins = disco.PluginsRegistry.find_all()
          config = configuration.NamespaceConfig(
              cli.prepare_and_parse_args(plugins, args))
  
          with mock.patch('certbot._internal.main._init_le_client') as mock_init:
              with mock.patch('certbot._internal.main._suggest_donation_if_appropriate'):
                  with mock.patch('certbot._internal.eff.handle_subscription'):
                      main.certonly(config, plugins)
  
          return mock_init()  # returns the client
  
      @mock.patch('certbot._internal.main._find_cert')
      @mock.patch('certbot._internal.main._get_and_save_cert')
      @mock.patch('certbot._internal.main._report_new_cert')
      def test_no_reinstall_text_pause(self, unused_report, mock_auth,
          mock_find_cert):
          mock_notification = self.mock_get_utility().notification
          mock_notification.side_effect = self._assert_no_pause
          mock_auth.return_value = mock.Mock()
          mock_find_cert.return_value = False, None
          self._call('certonly --webroot -d example.com'.split())
  
      def _assert_no_pause(self, message, pause=True):  # pylint: disable=unused-argument
          self.assertFalse(pause)
  
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.cert_manager.domains_for_certname')
      @mock.patch('certbot._internal.renewal.renew_cert')
      @mock.patch('certbot._internal.main._report_new_cert')
      def test_find_lineage_for_domains_and_certname(self, mock_report_cert,
          mock_renew_cert, mock_domains, mock_lineage):
          domains = ['example.com', 'test.org']
          mock_domains.return_value = domains
          mock_lineage.names.return_value = domains
          self._call(('certonly --webroot -d example.com -d test.org '
              '--cert-name example.com').split())
          self.assertTrue(mock_lineage.call_count == 1)
          self.assertTrue(mock_domains.call_count == 1)
          self.assertTrue(mock_renew_cert.call_count == 1)
          self.assertTrue(mock_report_cert.call_count == 1)
  
          # user confirms updating lineage with new domains
          self._call(('certonly --webroot -d example.com -d test.com '
              '--cert-name example.com').split())
          self.assertTrue(mock_lineage.call_count == 2)
          self.assertTrue(mock_domains.call_count == 2)
          self.assertTrue(mock_renew_cert.call_count == 2)
          self.assertTrue(mock_report_cert.call_count == 2)
  
          # error in _ask_user_to_confirm_new_names
          self.mock_get_utility().yesno.return_value = False
          self.assertRaises(errors.ConfigurationError, self._call,
              ('certonly --webroot -d example.com -d test.com --cert-name example.com').split())
  
      @mock.patch('certbot._internal.cert_manager.domains_for_certname')
      @mock.patch('certbot.display.ops.choose_names')
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main._report_new_cert')
      def test_find_lineage_for_domains_new_certname(self, mock_report_cert,
          mock_lineage, mock_choose_names, mock_domains_for_certname):
          mock_lineage.return_value = None
  
          # no lineage with this name but we specified domains so create a new cert
          self._call(('certonly --webroot -d example.com -d test.com '
              '--cert-name example.com').split())
          self.assertTrue(mock_lineage.call_count == 1)
          self.assertTrue(mock_report_cert.call_count == 1)
  
          # no lineage with this name and we didn't give domains
          mock_choose_names.return_value = ["somename"]
          mock_domains_for_certname.return_value = None
          self._call(('certonly --webroot --cert-name example.com').split())
          self.assertTrue(mock_choose_names.called)
  
  class FindDomainsOrCertnameTest(unittest.TestCase):
      """Tests for certbot._internal.main._find_domains_or_certname."""
  
      @mock.patch('certbot.display.ops.choose_names')
      def test_display_ops(self, mock_choose_names):
          mock_config = mock.Mock(domains=None, certname=None)
          mock_choose_names.return_value = "domainname"
          # pylint: disable=protected-access
          self.assertEqual(main._find_domains_or_certname(mock_config, None),
              ("domainname", None))
  
      @mock.patch('certbot.display.ops.choose_names')
      def test_no_results(self, mock_choose_names):
          mock_config = mock.Mock(domains=None, certname=None)
          mock_choose_names.return_value = []
          # pylint: disable=protected-access
          self.assertRaises(errors.Error, main._find_domains_or_certname, mock_config, None)
  
      @mock.patch('certbot._internal.cert_manager.domains_for_certname')
      def test_grab_domains(self, mock_domains):
          mock_config = mock.Mock(domains=None, certname="one.com")
          mock_domains.return_value = ["one.com", "two.com"]
          # pylint: disable=protected-access
          self.assertEqual(main._find_domains_or_certname(mock_config, None),
              (["one.com", "two.com"], "one.com"))
  
  
  class RevokeTest(test_util.TempDirTestCase):
      """Tests for certbot._internal.main.revoke."""
  
      def setUp(self):
          super(RevokeTest, self).setUp()
  
          shutil.copy(CERT_PATH, self.tempdir)
          self.tmp_cert_path = os.path.abspath(os.path.join(self.tempdir,
              'cert_512.pem'))
          with open(self.tmp_cert_path, 'r') as f:
              self.tmp_cert = (self.tmp_cert_path, f.read())
  
          patches = [
              mock.patch('acme.client.BackwardsCompatibleClientV2'),
              mock.patch('certbot._internal.client.Client'),
              mock.patch('certbot._internal.main._determine_account'),
              mock.patch('certbot._internal.main.display_ops.success_revocation')
          ]
          self.mock_acme_client = patches[0].start()
          patches[1].start()
          self.mock_determine_account = patches[2].start()
          self.mock_success_revoke = patches[3].start()
          for patch in patches:
              self.addCleanup(patch.stop)
  
          from certbot._internal.account import Account
  
          self.regr = mock.MagicMock()
          self.meta = Account.Meta(
              creation_host="test.certbot.org",
              creation_dt=datetime.datetime(
                  2015, 7, 4, 14, 4, 10, tzinfo=pytz.UTC))
          self.acc = Account(self.regr, JWK, self.meta)
  
          self.mock_determine_account.return_value = (self.acc, None)
  
      def _call(self, args=None):
          if not args:
              args = 'revoke --cert-path={0} '
              args = args.format(self.tmp_cert_path).split()
          plugins = disco.PluginsRegistry.find_all()
          config = configuration.NamespaceConfig(
              cli.prepare_and_parse_args(plugins, args))
  
          from certbot._internal.main import revoke
          revoke(config, plugins)
  
      @mock.patch('certbot._internal.main._delete_if_appropriate')
      @mock.patch('certbot._internal.main.client.acme_client')
      def test_revoke_with_reason(self, mock_acme_client,
              mock_delete_if_appropriate):
          mock_delete_if_appropriate.return_value = False
          mock_revoke = mock_acme_client.BackwardsCompatibleClientV2().revoke
          expected = []
          for reason, code in constants.REVOCATION_REASONS.items():
              args = 'revoke --cert-path={0} --reason {1}'.format(self.tmp_cert_path, reason).split()
              self._call(args)
              expected.append(mock.call(mock.ANY, code))
              args = 'revoke --cert-path={0} --reason {1}'.format(self.tmp_cert_path,
                      reason.upper()).split()
              self._call(args)
              expected.append(mock.call(mock.ANY, code))
          self.assertEqual(expected, mock_revoke.call_args_list)
  
      @mock.patch('certbot._internal.main._delete_if_appropriate')
      @mock.patch('certbot._internal.storage.cert_path_for_cert_name')
      def test_revoke_by_certname(self, mock_cert_path_for_cert_name,
              mock_delete_if_appropriate):
          args = 'revoke --cert-name=example.com'.split()
          mock_cert_path_for_cert_name.return_value = self.tmp_cert
          mock_delete_if_appropriate.return_value = False
          self._call(args)
          self.mock_success_revoke.assert_called_once_with(self.tmp_cert_path)
  
      @mock.patch('certbot._internal.main._delete_if_appropriate')
      def test_revocation_success(self, mock_delete_if_appropriate):
          self._call()
          mock_delete_if_appropriate.return_value = False
          self.mock_success_revoke.assert_called_once_with(self.tmp_cert_path)
  
      def test_revocation_error(self):
          from acme import errors as acme_errors
          self.mock_acme_client.side_effect = acme_errors.ClientError()
          self.assertRaises(acme_errors.ClientError, self._call)
          self.mock_success_revoke.assert_not_called()
  
      @mock.patch('certbot._internal.main._delete_if_appropriate')
      @mock.patch('certbot._internal.cert_manager.delete')
      @test_util.patch_get_utility()
      def test_revocation_with_prompt(self, mock_get_utility,
              mock_delete, mock_delete_if_appropriate):
          mock_get_utility().yesno.return_value = False
          mock_delete_if_appropriate.return_value = False
          self._call()
          self.assertFalse(mock_delete.called)
  
  class DeleteIfAppropriateTest(test_util.ConfigTestCase):
      """Tests for certbot._internal.main._delete_if_appropriate """
  
      def _call(self, mock_config):
          from certbot._internal.main import _delete_if_appropriate
          _delete_if_appropriate(mock_config)
  
      def _test_delete_opt_out_common(self, mock_get_utility):
          with mock.patch('certbot._internal.cert_manager.delete') as mock_delete:
              self._call(self.config)
          mock_delete.assert_not_called()
          self.assertTrue(mock_get_utility().add_message.called)
  
      @test_util.patch_get_utility()
      def test_delete_flag_opt_out(self, mock_get_utility):
          self.config.delete_after_revoke = False
          self._test_delete_opt_out_common(mock_get_utility)
  
      @test_util.patch_get_utility()
      def test_delete_prompt_opt_out(self, mock_get_utility):
          util_mock = mock_get_utility()
          util_mock.yesno.return_value = False
          self._test_delete_opt_out_common(mock_get_utility)
  
      @mock.patch('certbot._internal.storage.renewal_file_for_certname')
      @mock.patch('certbot._internal.cert_manager.delete')
      @mock.patch('certbot._internal.cert_manager.match_and_check_overlaps')
      @mock.patch('certbot._internal.storage.full_archive_path')
      @mock.patch('certbot._internal.cert_manager.cert_path_to_lineage')
      @test_util.patch_get_utility()
      def test_overlapping_archive_dirs(self, mock_get_utility,
              mock_cert_path_to_lineage, mock_archive,
              mock_match_and_check_overlaps, mock_delete,
              mock_renewal_file_for_certname):
          # pylint: disable = unused-argument
          config = self.config
          config.cert_path = "/some/reasonable/path"
          config.certname = ""
          mock_cert_path_to_lineage.return_value = "example.com"
          mock_match_and_check_overlaps.side_effect = errors.OverlappingMatchFound()
          self._call(config)
          mock_delete.assert_not_called()
  
      @mock.patch('certbot._internal.storage.renewal_file_for_certname')
      @mock.patch('certbot._internal.cert_manager.match_and_check_overlaps')
      @mock.patch('certbot._internal.storage.full_archive_path')
      @mock.patch('certbot._internal.cert_manager.delete')
      @mock.patch('certbot._internal.cert_manager.cert_path_to_lineage')
      @test_util.patch_get_utility()
      def test_cert_path_only(self, mock_get_utility,
              mock_cert_path_to_lineage, mock_delete, mock_archive,
              mock_overlapping_archive_dirs, mock_renewal_file_for_certname):
          # pylint: disable = unused-argument
          config = self.config
          config.cert_path = "/some/reasonable/path"
          config.certname = ""
          mock_cert_path_to_lineage.return_value = "example.com"
          mock_overlapping_archive_dirs.return_value = False
          self._call(config)
          self.assertEqual(mock_delete.call_count, 1)
  
      @mock.patch('certbot._internal.storage.renewal_file_for_certname')
      @mock.patch('certbot._internal.cert_manager.match_and_check_overlaps')
      @mock.patch('certbot._internal.storage.full_archive_path')
      @mock.patch('certbot._internal.cert_manager.cert_path_to_lineage')
      @mock.patch('certbot._internal.cert_manager.delete')
      @test_util.patch_get_utility()
      def test_noninteractive_deletion(self, mock_get_utility, mock_delete,
              mock_cert_path_to_lineage, mock_full_archive_dir,
              mock_match_and_check_overlaps, mock_renewal_file_for_certname):
          # pylint: disable = unused-argument
          config = self.config
          config.namespace.noninteractive_mode = True
          config.cert_path = "/some/reasonable/path"
          config.certname = ""
          mock_cert_path_to_lineage.return_value = "example.com"
          mock_full_archive_dir.return_value = ""
          mock_match_and_check_overlaps.return_value = ""
          self._call(config)
          self.assertEqual(mock_delete.call_count, 1)
  
      @mock.patch('certbot._internal.storage.renewal_file_for_certname')
      @mock.patch('certbot._internal.cert_manager.match_and_check_overlaps')
      @mock.patch('certbot._internal.storage.full_archive_path')
      @mock.patch('certbot._internal.cert_manager.cert_path_to_lineage')
      @mock.patch('certbot._internal.cert_manager.delete')
      @test_util.patch_get_utility()
      def test_opt_in_deletion(self, mock_get_utility, mock_delete,
              mock_cert_path_to_lineage, mock_full_archive_dir,
              mock_match_and_check_overlaps, mock_renewal_file_for_certname):
          # pylint: disable = unused-argument
          config = self.config
          config.namespace.delete_after_revoke = True
          config.cert_path = "/some/reasonable/path"
          config.certname = ""
          mock_cert_path_to_lineage.return_value = "example.com"
          mock_full_archive_dir.return_value = ""
          mock_match_and_check_overlaps.return_value = ""
          self._call(config)
          self.assertEqual(mock_delete.call_count, 1)
          self.assertFalse(mock_get_utility().yesno.called)
  
  
  class DetermineAccountTest(test_util.ConfigTestCase):
      """Tests for certbot._internal.main._determine_account."""
  
      def setUp(self):
          super(DetermineAccountTest, self).setUp()
          self.config.account = None
          self.config.email = None
          self.config.register_unsafely_without_email = False
          self.accs = [mock.MagicMock(id='x'), mock.MagicMock(id='y')]
          self.account_storage = account.AccountMemoryStorage()
          # For use in saving accounts: fake out the new_authz URL.
          self.mock_client = mock.MagicMock()
          self.mock_client.directory.new_authz = "hi"
  
      def _call(self):
          # pylint: disable=protected-access
          from certbot._internal.main import _determine_account
          with mock.patch('certbot._internal.main.account.AccountFileStorage') as mock_storage:
              mock_storage.return_value = self.account_storage
              return _determine_account(self.config)
  
      def test_args_account_set(self):
          self.account_storage.save(self.accs[1], self.mock_client)
          self.config.account = self.accs[1].id
          self.assertEqual((self.accs[1], None), self._call())
          self.assertEqual(self.accs[1].id, self.config.account)
          self.assertTrue(self.config.email is None)
  
      def test_single_account(self):
          self.account_storage.save(self.accs[0], self.mock_client)
          self.assertEqual((self.accs[0], None), self._call())
          self.assertEqual(self.accs[0].id, self.config.account)
          self.assertTrue(self.config.email is None)
  
      @mock.patch('certbot._internal.client.display_ops.choose_account')
      def test_multiple_accounts(self, mock_choose_accounts):
          for acc in self.accs:
              self.account_storage.save(acc, self.mock_client)
          mock_choose_accounts.return_value = self.accs[1]
          self.assertEqual((self.accs[1], None), self._call())
          self.assertEqual(
              set(mock_choose_accounts.call_args[0][0]), set(self.accs))
          self.assertEqual(self.accs[1].id, self.config.account)
          self.assertTrue(self.config.email is None)
  
      @mock.patch('certbot._internal.client.display_ops.get_email')
      def test_no_accounts_no_email(self, mock_get_email):
          mock_get_email.return_value = 'foo@bar.baz'
  
          with mock.patch('certbot._internal.main.client') as client:
              client.register.return_value = (
                  self.accs[0], mock.sentinel.acme)
              self.assertEqual((self.accs[0], mock.sentinel.acme), self._call())
          client.register.assert_called_once_with(
              self.config, self.account_storage, tos_cb=mock.ANY)
  
          self.assertEqual(self.accs[0].id, self.config.account)
          self.assertEqual('foo@bar.baz', self.config.email)
  
      def test_no_accounts_email(self):
          self.config.email = 'other email'
          with mock.patch('certbot._internal.main.client') as client:
              client.register.return_value = (self.accs[1], mock.sentinel.acme)
              self._call()
          self.assertEqual(self.accs[1].id, self.config.account)
          self.assertEqual('other email', self.config.email)
  
  
  class MainTest(test_util.ConfigTestCase):
      """Tests for different commands."""
  
      def setUp(self):
          super(MainTest, self).setUp()
  
          filesystem.mkdir(self.config.logs_dir)
          self.standard_args = ['--config-dir', self.config.config_dir,
                                '--work-dir', self.config.work_dir,
                                '--logs-dir', self.config.logs_dir, '--text']
  
          self.mock_sleep = mock.patch('time.sleep').start()
  
      def tearDown(self):
          # Reset globals in cli
          reload_module(cli)
  
          super(MainTest, self).tearDown()
  
      def _call(self, args, stdout=None, mockisfile=False):
          """Run the cli with output streams, actual client and optionally
          os.path.isfile() mocked out"""
  
          if mockisfile:
              orig_open = os.path.isfile
  
              def mock_isfile(fn, *args, **kwargs):  # pylint: disable=unused-argument
                  """Mock os.path.isfile()"""
                  if (fn.endswith("cert") or
                          fn.endswith("chain") or
                          fn.endswith("privkey")):
                      return True
                  return orig_open(fn)
  
              with mock.patch("certbot.compat.os.path.isfile") as mock_if:
                  mock_if.side_effect = mock_isfile
                  with mock.patch('certbot._internal.main.client') as client:
                      ret, stdout, stderr = self._call_no_clientmock(args, stdout)
                      return ret, stdout, stderr, client
          else:
              with mock.patch('certbot._internal.main.client') as client:
                  ret, stdout, stderr = self._call_no_clientmock(args, stdout)
                  return ret, stdout, stderr, client
  
      def _call_no_clientmock(self, args, stdout=None):
          "Run the client with output streams mocked out"
          args = self.standard_args + args
  
          toy_stdout = stdout if stdout else six.StringIO()
          with mock.patch('certbot._internal.main.sys.stdout', new=toy_stdout):
              with mock.patch('certbot._internal.main.sys.stderr') as stderr:
                  with mock.patch("certbot.util.atexit"):
                      ret = main.main(args[:])  # NOTE: parser can alter its args!
          return ret, toy_stdout, stderr
  
      def test_no_flags(self):
          with mock.patch('certbot._internal.main.run') as mock_run:
              self._call([])
              self.assertEqual(1, mock_run.call_count)
  
      def test_version_string_program_name(self):
          toy_out = six.StringIO()
          toy_err = six.StringIO()
          with mock.patch('certbot._internal.main.sys.stdout', new=toy_out):
              with mock.patch('certbot._internal.main.sys.stderr', new=toy_err):
                  try:
                      main.main(["--version"])
                  except SystemExit:
                      pass
                  finally:
                      output = toy_out.getvalue() or toy_err.getvalue()
                      self.assertTrue("certbot" in output, "Output is {0}".format(output))
  
      def _cli_missing_flag(self, args, message):
          "Ensure that a particular error raises a missing cli flag error containing message"
          exc = None
          try:
              with mock.patch('certbot._internal.main.sys.stderr'):
                  main.main(self.standard_args + args[:])  # NOTE: parser can alter its args!
          except errors.MissingCommandlineFlag as exc_:
              exc = exc_
              self.assertTrue(message in str(exc))
          self.assertTrue(exc is not None)
  
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      def test_noninteractive(self, _):
          args = ['-n', 'certonly']
          self._cli_missing_flag(args, "specify a plugin")
          args.extend(['--standalone', '-d', 'eg.is'])
          self._cli_missing_flag(args, "register before running")
  
      @mock.patch('certbot._internal.eff.handle_subscription')
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      @mock.patch('certbot._internal.main._report_new_cert')
      @mock.patch('certbot._internal.main.client.acme_client.Client')
      @mock.patch('certbot._internal.main._determine_account')
      @mock.patch('certbot._internal.main.client.Client.obtain_and_enroll_certificate')
      @mock.patch('certbot._internal.main._get_and_save_cert')
      def test_user_agent(self, gsc, _obt, det, _client, _, __, ___):
          # Normally the client is totally mocked out, but here we need more
          # arguments to automate it...
          args = ["--standalone", "certonly", "-m", "none@none.com",
                  "-d", "example.com", '--agree-tos'] + self.standard_args
          det.return_value = mock.MagicMock(), None
          gsc.return_value = mock.MagicMock()
  
          with mock.patch('certbot._internal.main.client.acme_client.ClientNetwork') as acme_net:
              self._call_no_clientmock(args)
              os_ver = util.get_os_info_ua()
              ua = acme_net.call_args[1]["user_agent"]
              self.assertTrue(os_ver in ua)
              import platform
              plat = platform.platform()
              if "linux" in plat.lower():
                  self.assertTrue(util.get_os_info_ua() in ua)
  
          with mock.patch('certbot._internal.main.client.acme_client.ClientNetwork') as acme_net:
              ua = "bandersnatch"
              args += ["--user-agent", ua]
              self._call_no_clientmock(args)
              acme_net.assert_called_once_with(mock.ANY, account=mock.ANY, verify_ssl=True,
                  user_agent=ua)
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      def test_installer_selection(self, mock_pick_installer, _rec):
          self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert',
                      '--key-path', 'privkey', '--chain-path', 'chain'], mockisfile=True)
          self.assertEqual(mock_pick_installer.call_count, 1)
  
      @mock.patch('certbot._internal.main._install_cert')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      def test_installer_certname(self, _inst, _rec, mock_install):
          mock_lineage = mock.MagicMock(cert_path=test_util.temp_join('cert'),
                                        chain_path=test_util.temp_join('chain'),
                                        fullchain_path=test_util.temp_join('chain'),
                                        key_path=test_util.temp_join('privkey'))
  
          with mock.patch("certbot._internal.cert_manager.lineage_for_certname") as mock_getlin:
              mock_getlin.return_value = mock_lineage
              self._call(['install', '--cert-name', 'whatever'], mockisfile=True)
              call_config = mock_install.call_args[0][0]
              self.assertEqual(call_config.cert_path, test_util.temp_join('cert'))
              self.assertEqual(call_config.fullchain_path, test_util.temp_join('chain'))
              self.assertEqual(call_config.key_path, test_util.temp_join('privkey'))
  
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      @mock.patch('certbot._internal.main._install_cert')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      def test_installer_param_override(self, _inst, _rec, mock_install, _):
          mock_lineage = mock.MagicMock(cert_path=test_util.temp_join('cert'),
                                        chain_path=test_util.temp_join('chain'),
                                        fullchain_path=test_util.temp_join('chain'),
                                        key_path=test_util.temp_join('privkey'))
          with mock.patch("certbot._internal.cert_manager.lineage_for_certname") as mock_getlin:
              mock_getlin.return_value = mock_lineage
              self._call(['install', '--cert-name', 'whatever',
                          '--key-path', test_util.temp_join('overriding_privkey')], mockisfile=True)
              call_config = mock_install.call_args[0][0]
              self.assertEqual(call_config.cert_path, test_util.temp_join('cert'))
              self.assertEqual(call_config.fullchain_path, test_util.temp_join('chain'))
              self.assertEqual(call_config.chain_path, test_util.temp_join('chain'))
              self.assertEqual(call_config.key_path, test_util.temp_join('overriding_privkey'))
  
              mock_install.reset()
  
              self._call(['install', '--cert-name', 'whatever',
                          '--cert-path', test_util.temp_join('overriding_cert')], mockisfile=True)
              call_config = mock_install.call_args[0][0]
              self.assertEqual(call_config.cert_path, test_util.temp_join('overriding_cert'))
              self.assertEqual(call_config.fullchain_path, test_util.temp_join('chain'))
              self.assertEqual(call_config.key_path, test_util.temp_join('privkey'))
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      def test_installer_param_error(self, _inst, _rec):
          self.assertRaises(errors.ConfigurationError,
                            self._call,
                            ['install', '--cert-name', 'notfound',
                             '--key-path', 'invalid'])
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      @mock.patch('certbot._internal.cert_manager.get_certnames')
      @mock.patch('certbot._internal.main._install_cert')
      def test_installer_select_cert(self, mock_inst, mock_getcert, _inst, _rec):
          mock_lineage = mock.MagicMock(cert_path=test_util.temp_join('cert'),
                                        chain_path=test_util.temp_join('chain'),
                                        fullchain_path=test_util.temp_join('chain'),
                                        key_path=test_util.temp_join('privkey'))
          with mock.patch("certbot._internal.cert_manager.lineage_for_certname") as mock_getlin:
              mock_getlin.return_value = mock_lineage
              self._call(['install'], mockisfile=True)
          self.assertTrue(mock_getcert.called)
          self.assertTrue(mock_inst.called)
  
      @mock.patch('certbot._internal.eff.handle_subscription')
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      @mock.patch('certbot._internal.main._report_new_cert')
      @mock.patch('certbot.util.exe_exists')
      def test_configurator_selection(self, mock_exe_exists, _, __, ___):
          mock_exe_exists.return_value = True
          real_plugins = disco.PluginsRegistry.find_all()
          args = ['--apache', '--authenticator', 'standalone']
  
          # This needed two calls to find_all(), which we're avoiding for now
          # because of possible side effects:
          # https://github.com/letsencrypt/letsencrypt/commit/51ed2b681f87b1eb29088dd48718a54f401e4855
          #with mock.patch('certbot._internal.cli.plugins_testable') as plugins:
          #    plugins.return_value = {"apache": True, "nginx": True}
          #    ret, _, _, _ = self._call(args)
          #    self.assertTrue("Too many flags setting" in ret)
  
          args = ["install", "--nginx", "--cert-path",
                  test_util.temp_join('blah'), "--key-path", test_util.temp_join('blah'),
                  "--nginx-server-root", "/nonexistent/thing", "-d",
                  "example.com", "--debug"]
          if "nginx" in real_plugins:
              # Sending nginx a non-existent conf dir will simulate misconfiguration
              # (we can only do that if certbot-nginx is actually present)
              ret, _, _, _ = self._call(args)
              self.assertTrue("The nginx plugin is not working" in ret)
              self.assertTrue("MisconfigurationError" in ret)
  
          self._cli_missing_flag(["--standalone"], "With the standalone plugin, you probably")
  
          with mock.patch("certbot._internal.main._init_le_client") as mock_init:
              with mock.patch("certbot._internal.main._get_and_save_cert") as mock_gsc:
                  mock_gsc.return_value = mock.MagicMock()
                  self._call(["certonly", "--manual", "-d", "foo.bar"])
                  unused_config, auth, unused_installer = mock_init.call_args[0]
                  self.assertTrue(isinstance(auth, manual.Authenticator))
  
          with mock.patch('certbot._internal.main.certonly') as mock_certonly:
              self._call(["auth", "--standalone"])
              self.assertEqual(1, mock_certonly.call_count)
  
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      def test_rollback(self, _):
          _, _, _, client = self._call(['rollback'])
          self.assertEqual(1, client.rollback.call_count)
  
          _, _, _, client = self._call(['rollback', '--checkpoints', '123'])
          client.rollback.assert_called_once_with(
              mock.ANY, 123, mock.ANY, mock.ANY)
  
      @mock.patch('certbot._internal.cert_manager.update_live_symlinks')
      def test_update_symlinks(self, mock_cert_manager):
          self._call_no_clientmock(['update_symlinks'])
          self.assertEqual(1, mock_cert_manager.call_count)
  
      @mock.patch('certbot._internal.cert_manager.certificates')
      def test_certificates(self, mock_cert_manager):
          self._call_no_clientmock(['certificates'])
          self.assertEqual(1, mock_cert_manager.call_count)
  
      @mock.patch('certbot._internal.cert_manager.delete')
      def test_delete(self, mock_cert_manager):
          self._call_no_clientmock(['delete'])
          self.assertEqual(1, mock_cert_manager.call_count)
  
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      def test_plugins(self, _):
          flags = ['--init', '--prepare', '--authenticators', '--installers']
          for args in itertools.chain(
                  *(itertools.combinations(flags, r)
                    for r in six.moves.range(len(flags)))):
              self._call(['plugins'] + list(args))
  
      @mock.patch('certbot._internal.main.plugins_disco')
      @mock.patch('certbot._internal.main.cli.HelpfulArgumentParser.determine_help_topics')
      def test_plugins_no_args(self, _det, mock_disco):
          ifaces = []  # type: List[interfaces.IPlugin]
          plugins = mock_disco.PluginsRegistry.find_all()
  
          stdout = six.StringIO()
          with test_util.patch_get_utility_with_stdout(stdout=stdout):
              _, stdout, _, _ = self._call(['plugins'], stdout)
  
          plugins.visible.assert_called_once_with()
          plugins.visible().ifaces.assert_called_once_with(ifaces)
          filtered = plugins.visible().ifaces()
          self.assertEqual(stdout.getvalue().strip(), str(filtered))
  
      @mock.patch('certbot._internal.main.plugins_disco')
      @mock.patch('certbot._internal.main.cli.HelpfulArgumentParser.determine_help_topics')
      def test_plugins_no_args_unprivileged(self, _det, mock_disco):
          ifaces = []  # type: List[interfaces.IPlugin]
          plugins = mock_disco.PluginsRegistry.find_all()
  
          def throw_error(directory, mode, strict):
              """Raises error.Error."""
              _, _, _ = directory, mode, strict
              raise errors.Error()
  
          stdout = six.StringIO()
          with mock.patch('certbot.util.set_up_core_dir') as mock_set_up_core_dir:
              with test_util.patch_get_utility_with_stdout(stdout=stdout):
                  mock_set_up_core_dir.side_effect = throw_error
                  _, stdout, _, _ = self._call(['plugins'], stdout)
  
          plugins.visible.assert_called_once_with()
          plugins.visible().ifaces.assert_called_once_with(ifaces)
          filtered = plugins.visible().ifaces()
          self.assertEqual(stdout.getvalue().strip(), str(filtered))
  
      @mock.patch('certbot._internal.main.plugins_disco')
      @mock.patch('certbot._internal.main.cli.HelpfulArgumentParser.determine_help_topics')
      def test_plugins_init(self, _det, mock_disco):
          ifaces = []  # type: List[interfaces.IPlugin]
          plugins = mock_disco.PluginsRegistry.find_all()
  
          stdout = six.StringIO()
          with test_util.patch_get_utility_with_stdout(stdout=stdout):
              _, stdout, _, _ = self._call(['plugins', '--init'], stdout)
  
          plugins.visible.assert_called_once_with()
          plugins.visible().ifaces.assert_called_once_with(ifaces)
          filtered = plugins.visible().ifaces()
          self.assertEqual(filtered.init.call_count, 1)
          filtered.verify.assert_called_once_with(ifaces)
          verified = filtered.verify()
          self.assertEqual(stdout.getvalue().strip(), str(verified))
  
      @mock.patch('certbot._internal.main.plugins_disco')
      @mock.patch('certbot._internal.main.cli.HelpfulArgumentParser.determine_help_topics')
      def test_plugins_prepare(self, _det, mock_disco):
          ifaces = []  # type: List[interfaces.IPlugin]
          plugins = mock_disco.PluginsRegistry.find_all()
  
          stdout = six.StringIO()
          with test_util.patch_get_utility_with_stdout(stdout=stdout):
              _, stdout, _, _ = self._call(['plugins', '--init', '--prepare'], stdout)
  
          plugins.visible.assert_called_once_with()
          plugins.visible().ifaces.assert_called_once_with(ifaces)
          filtered = plugins.visible().ifaces()
          self.assertEqual(filtered.init.call_count, 1)
          filtered.verify.assert_called_once_with(ifaces)
          verified = filtered.verify()
          verified.prepare.assert_called_once_with()
          verified.available.assert_called_once_with()
          available = verified.available()
          self.assertEqual(stdout.getvalue().strip(), str(available))
  
      def test_certonly_abspath(self):
          cert = 'cert'
          key = 'key'
          chain = 'chain'
          fullchain = 'fullchain'
  
          with mock.patch('certbot._internal.main.certonly') as mock_certonly:
              self._call(['certonly', '--cert-path', cert, '--key-path', 'key',
                          '--chain-path', 'chain',
                          '--fullchain-path', 'fullchain'])
  
          config, unused_plugins = mock_certonly.call_args[0]
          self.assertEqual(config.cert_path, os.path.abspath(cert))
          self.assertEqual(config.key_path, os.path.abspath(key))
          self.assertEqual(config.chain_path, os.path.abspath(chain))
          self.assertEqual(config.fullchain_path, os.path.abspath(fullchain))
  
      def test_certonly_bad_args(self):
          try:
              self._call(['-a', 'bad_auth', 'certonly'])
              assert False, "Exception should have been raised"
          except errors.PluginSelectionError as e:
              self.assertTrue('The requested bad_auth plugin does not appear' in str(e))
  
      def test_check_config_sanity_domain(self):
          # FQDN
          self.assertRaises(errors.ConfigurationError,
                            self._call,
                            ['-d', 'a' * 64])
          # FQDN 2
          self.assertRaises(errors.ConfigurationError,
                            self._call,
                            ['-d', (('a' * 50) + '.') * 10])
          # Bare IP address (this is actually a different error message now)
          self.assertRaises(errors.ConfigurationError,
                            self._call,
                            ['-d', '204.11.231.35'])
  
      def test_csr_with_besteffort(self):
          self.assertRaises(
              errors.Error, self._call,
              'certonly --csr {0} --allow-subset-of-names'.format(CSR).split())
  
      def test_run_with_csr(self):
          # This is an error because you can only use --csr with certonly
          try:
              self._call(['--csr', CSR])
          except errors.Error as e:
              assert "Please try the certonly" in repr(e)
              return
          assert False, "Expected supplying --csr to fail with default verb"
  
      def test_csr_with_no_domains(self):
          self.assertRaises(
              errors.Error, self._call,
              'certonly --csr {0}'.format(
                  test_util.vector_path('csr-nonames_512.pem')).split())
  
      def test_csr_with_inconsistent_domains(self):
          self.assertRaises(
              errors.Error, self._call,
              'certonly -d example.org --csr {0}'.format(CSR).split())
  
      def _certonly_new_request_common(self, mock_client, args=None):
          with mock.patch('certbot._internal.main._find_lineage_for_domains_and_certname') \
              as mock_renewal:
              mock_renewal.return_value = ("newcert", None)
              with mock.patch('certbot._internal.main._init_le_client') as mock_init:
                  mock_init.return_value = mock_client
                  if args is None:
                      args = []
                  args += '-d foo.bar -a standalone certonly'.split()
                  self._call(args)
  
      @test_util.patch_get_utility()
      def test_certonly_dry_run_new_request_success(self, mock_get_utility):
          mock_client = mock.MagicMock()
          mock_client.obtain_and_enroll_certificate.return_value = None
          self._certonly_new_request_common(mock_client, ['--dry-run'])
          self.assertEqual(
              mock_client.obtain_and_enroll_certificate.call_count, 1)
          self.assertTrue(
              'dry run' in mock_get_utility().add_message.call_args[0][0])
          # Asserts we don't suggest donating after a successful dry run
          self.assertEqual(mock_get_utility().add_message.call_count, 1)
  
      @mock.patch('certbot._internal.eff.handle_subscription')
      @mock.patch('certbot.crypto_util.notAfter')
      @test_util.patch_get_utility()
      def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter, mock_subscription):
          cert_path = os.path.normpath(os.path.join(self.config.config_dir, 'live/foo.bar'))
          key_path = os.path.normpath(os.path.join(self.config.config_dir, 'live/baz.qux'))
          date = '1970-01-01'
          mock_notAfter().date.return_value = date
  
          mock_lineage = mock.MagicMock(cert=cert_path, fullchain=cert_path,
                                        fullchain_path=cert_path, key_path=key_path)
          mock_client = mock.MagicMock()
          mock_client.obtain_and_enroll_certificate.return_value = mock_lineage
          self._certonly_new_request_common(mock_client)
          self.assertEqual(
              mock_client.obtain_and_enroll_certificate.call_count, 1)
          cert_msg = mock_get_utility().add_message.call_args_list[0][0][0]
          self.assertTrue(cert_path in cert_msg)
          self.assertTrue(date in cert_msg)
          self.assertTrue(key_path in cert_msg)
          self.assertTrue(
              'donate' in mock_get_utility().add_message.call_args[0][0])
          self.assertTrue(mock_subscription.called)
  
      @mock.patch('certbot._internal.eff.handle_subscription')
      def test_certonly_new_request_failure(self, mock_subscription):
          mock_client = mock.MagicMock()
          mock_client.obtain_and_enroll_certificate.return_value = False
          self.assertRaises(errors.Error,
                            self._certonly_new_request_common, mock_client)
          self.assertFalse(mock_subscription.called)
  
      def _test_renewal_common(self, due_for_renewal, extra_args, log_out=None,
                               args=None, should_renew=True, error_expected=False,
                               quiet_mode=False, expiry_date=datetime.datetime.now(),
                               reuse_key=False):
          cert_path = test_util.vector_path('cert_512.pem')
          chain_path = os.path.normpath(os.path.join(self.config.config_dir,
                                                     'live/foo.bar/fullchain.pem'))
          mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path,
                                        cert_path=cert_path, fullchain_path=chain_path)
          mock_lineage.should_autorenew.return_value = due_for_renewal
          mock_lineage.has_pending_deployment.return_value = False
          mock_lineage.names.return_value = ['isnot.org']
          mock_certr = mock.MagicMock()
          mock_key = mock.MagicMock(pem='pem_key')
          mock_client = mock.MagicMock()
          stdout = six.StringIO()
          mock_client.obtain_certificate.return_value = (mock_certr, 'chain',
                                                         mock_key, 'csr')
  
          def write_msg(message, *args, **kwargs):  # pylint: disable=unused-argument
              """Write message to stdout."""
              stdout.write(message)
  
          try:
              with mock.patch('certbot._internal.cert_manager.find_duplicative_certs') as mock_fdc:
                  mock_fdc.return_value = (mock_lineage, None)
                  with mock.patch('certbot._internal.main._init_le_client') as mock_init:
                      mock_init.return_value = mock_client
                      with test_util.patch_get_utility() as mock_get_utility:
                          if not quiet_mode:
                              mock_get_utility().notification.side_effect = write_msg
                          with mock.patch('certbot._internal.main.renewal.OpenSSL') as mock_ssl:
                              mock_latest = mock.MagicMock()
                              mock_latest.get_issuer.return_value = "Fake fake"
                              mock_ssl.crypto.load_certificate.return_value = mock_latest
                              with mock.patch('certbot._internal.main.renewal.crypto_util') \
                                  as mock_crypto_util:
                                  mock_crypto_util.notAfter.return_value = expiry_date
                                  with mock.patch('certbot._internal.eff.handle_subscription'):
                                      if not args:
                                          args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly']
                                      if extra_args:
                                          args += extra_args
                                      try:
                                          ret, stdout, _, _ = self._call(args, stdout)
                                          if ret:
                                              print("Returned", ret)
                                              raise AssertionError(ret)
                                          assert not error_expected, "renewal should have errored"
                                      except: # pylint: disable=bare-except
                                          if not error_expected:
                                              raise AssertionError(
                                                  "Unexpected renewal error:\n" +
                                                  traceback.format_exc())
  
              if should_renew:
                  if reuse_key:
                      # The location of the previous live privkey.pem is passed
                      # to obtain_certificate
                      mock_client.obtain_certificate.assert_called_once_with(['isnot.org'],
                          os.path.normpath(os.path.join(
                              self.config.config_dir, "live/sample-renewal/privkey.pem")))
                  else:
                      mock_client.obtain_certificate.assert_called_once_with(['isnot.org'], None)
              else:
                  self.assertEqual(mock_client.obtain_certificate.call_count, 0)
          except:
              self._dump_log()
              raise
          finally:
              if log_out:
                  with open(os.path.join(self.config.logs_dir, "letsencrypt.log")) as lf:
                      self.assertTrue(log_out in lf.read())
  
          return mock_lineage, mock_get_utility, stdout
  
      @mock.patch('certbot.crypto_util.notAfter')
      def test_certonly_renewal(self, _):
          lineage, get_utility, _ = self._test_renewal_common(True, [])
          self.assertEqual(lineage.save_successor.call_count, 1)
          lineage.update_all_links_to.assert_called_once_with(
              lineage.latest_common_version())
          cert_msg = get_utility().add_message.call_args_list[0][0][0]
          self.assertTrue('fullchain.pem' in cert_msg)
          self.assertTrue('donate' in get_utility().add_message.call_args[0][0])
  
      @mock.patch('certbot._internal.log.logging.handlers.RotatingFileHandler.doRollover')
      @mock.patch('certbot.crypto_util.notAfter')
      def test_certonly_renewal_triggers(self, _, __):
          # --dry-run should force renewal
          _, get_utility, _ = self._test_renewal_common(False, ['--dry-run', '--keep'],
                                                        log_out="simulating renewal")
          self.assertEqual(get_utility().add_message.call_count, 1)
          self.assertTrue('dry run' in get_utility().add_message.call_args[0][0])
  
          self._test_renewal_common(False, ['--renew-by-default', '-tvv', '--debug'],
                                    log_out="Auto-renewal forced")
          self.assertEqual(get_utility().add_message.call_count, 1)
  
          self._test_renewal_common(False, ['-tvv', '--debug', '--keep'],
                                    log_out="not yet due", should_renew=False)
  
      def _dump_log(self):
          print("Logs:")
          log_path = os.path.join(self.config.logs_dir, "letsencrypt.log")
          if os.path.exists(log_path):
              with open(log_path) as lf:
                  print(lf.read())
  
      def test_renew_verb(self):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run", "-tvv"]
          self._test_renewal_common(True, [], args=args, should_renew=True)
  
      def test_reuse_key(self):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run", "--reuse-key"]
          self._test_renewal_common(True, [], args=args, should_renew=True, reuse_key=True)
  
      @mock.patch('certbot._internal.storage.RenewableCert.save_successor')
      def test_reuse_key_no_dry_run(self, unused_save_successor):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--reuse-key"]
          self._test_renewal_common(True, [], args=args, should_renew=True, reuse_key=True)
  
      @mock.patch('sys.stdin')
      def test_noninteractive_renewal_delay(self, stdin):
          stdin.isatty.return_value = False
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run", "-tvv"]
          self._test_renewal_common(True, [], args=args, should_renew=True)
          self.assertEqual(self.mock_sleep.call_count, 1)
          # in main.py:
          #     sleep_time = random.randint(1, 60*8)
          sleep_call_arg = self.mock_sleep.call_args[0][0]
          self.assertTrue(1 <= sleep_call_arg <= 60*8)
  
      @mock.patch('sys.stdin')
      def test_interactive_no_renewal_delay(self, stdin):
          stdin.isatty.return_value = True
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run", "-tvv"]
          self._test_renewal_common(True, [], args=args, should_renew=True)
          self.assertEqual(self.mock_sleep.call_count, 0)
  
      @mock.patch('certbot._internal.renewal.should_renew')
      def test_renew_skips_recent_certs(self, should_renew):
          should_renew.return_value = False
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          expiry = datetime.datetime.now() + datetime.timedelta(days=90)
          _, _, stdout = self._test_renewal_common(False, extra_args=None, should_renew=False,
                                                   args=['renew'], expiry_date=expiry)
          self.assertTrue('No renewals were attempted.' in stdout.getvalue())
          self.assertTrue('The following certs are not due for renewal yet:' in stdout.getvalue())
  
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      def test_quiet_renew(self, _):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run"]
          _, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True)
          out = stdout.getvalue()
          self.assertTrue("renew" in out)
  
          args = ["renew", "--dry-run", "-q"]
          _, _, stdout = self._test_renewal_common(True, [], args=args,
                                                   should_renew=True, quiet_mode=True)
          out = stdout.getvalue()
          self.assertEqual("", out)
  
      def test_renew_hook_validation(self):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run", "--post-hook=no-such-command"]
          self._test_renewal_common(True, [], args=args, should_renew=False,
                                    error_expected=True)
  
      def test_renew_no_hook_validation(self):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          args = ["renew", "--dry-run", "--post-hook=no-such-command",
                  "--disable-hook-validation"]
          with mock.patch("certbot._internal.hooks.post_hook"):
              self._test_renewal_common(True, [], args=args, should_renew=True,
                                        error_expected=False)
  
      def test_renew_verb_empty_config(self):
          rd = os.path.join(self.config.config_dir, 'renewal')
          if not os.path.exists(rd):
              filesystem.makedirs(rd)
          with open(os.path.join(rd, 'empty.conf'), 'w'):
              pass  # leave the file empty
          args = ["renew", "--dry-run", "-tvv"]
          self._test_renewal_common(False, [], args=args, should_renew=False, error_expected=True)
  
      def test_renew_with_certname(self):
          test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
          self._test_renewal_common(True, [], should_renew=True,
              args=['renew', '--dry-run', '--cert-name', 'sample-renewal'])
  
      def test_renew_with_bad_certname(self):
          self._test_renewal_common(True, [], should_renew=False,
              args=['renew', '--dry-run', '--cert-name', 'sample-renewal'],
              error_expected=True)
  
      def _make_dummy_renewal_config(self):
          renewer_configs_dir = os.path.join(self.config.config_dir, 'renewal')
          filesystem.makedirs(renewer_configs_dir)
          with open(os.path.join(renewer_configs_dir, 'test.conf'), 'w') as f:
              f.write("My contents don't matter")
  
      def _test_renew_common(self, renewalparams=None, names=None,
                             assert_oc_called=None, **kwargs):
          self._make_dummy_renewal_config()
          with mock.patch('certbot._internal.storage.RenewableCert') as mock_rc:
              mock_lineage = mock.MagicMock()
              mock_lineage.fullchain = "somepath/fullchain.pem"
              if renewalparams is not None:
                  mock_lineage.configuration = {'renewalparams': renewalparams}
              if names is not None:
                  mock_lineage.names.return_value = names
              mock_rc.return_value = mock_lineage
              with mock.patch('certbot._internal.main.renew_cert') as mock_renew_cert:
                  kwargs.setdefault('args', ['renew'])
                  self._test_renewal_common(True, None, should_renew=False, **kwargs)
  
              if assert_oc_called is not None:
                  if assert_oc_called:
                      self.assertTrue(mock_renew_cert.called)
                  else:
                      self.assertFalse(mock_renew_cert.called)
  
      def test_renew_no_renewalparams(self):
          self._test_renew_common(assert_oc_called=False, error_expected=True)
  
      def test_renew_no_authenticator(self):
          self._test_renew_common(renewalparams={}, assert_oc_called=False,
              error_expected=True)
  
      def test_renew_with_bad_int(self):
          renewalparams = {'authenticator': 'webroot',
                           'rsa_key_size': 'over 9000'}
          self._test_renew_common(renewalparams=renewalparams, error_expected=True,
                                  assert_oc_called=False)
  
      def test_renew_with_nonetype_http01(self):
          renewalparams = {'authenticator': 'webroot',
                           'http01_port': 'None'}
          self._test_renew_common(renewalparams=renewalparams,
                                  assert_oc_called=True)
  
      def test_renew_with_bad_domain(self):
          renewalparams = {'authenticator': 'webroot'}
          names = ['uniçodé.com']
          self._test_renew_common(renewalparams=renewalparams, error_expected=True,
                                  names=names, assert_oc_called=False)
  
      @mock.patch('certbot._internal.plugins.selection.choose_configurator_plugins')
      def test_renew_with_configurator(self, mock_sel):
          mock_sel.return_value = (mock.MagicMock(), mock.MagicMock())
          renewalparams = {'authenticator': 'webroot'}
          self._test_renew_common(
              renewalparams=renewalparams, assert_oc_called=True,
              args='renew --configurator apache'.split())
  
      def test_renew_plugin_config_restoration(self):
          renewalparams = {'authenticator': 'webroot',
                           'webroot_path': 'None',
                           'webroot_imaginary_flag': '42'}
          self._test_renew_common(renewalparams=renewalparams,
                                  assert_oc_called=True)
  
      def test_renew_with_webroot_map(self):
          renewalparams = {'authenticator': 'webroot'}
          self._test_renew_common(
              renewalparams=renewalparams, assert_oc_called=True,
              args=['renew', '--webroot-map', json.dumps({'example.com': tempfile.gettempdir()})])
  
      def test_renew_reconstitute_error(self):
          # pylint: disable=protected-access
          with mock.patch('certbot._internal.main.renewal._reconstitute') as mock_reconstitute:
              mock_reconstitute.side_effect = Exception
              self._test_renew_common(assert_oc_called=False, error_expected=True)
  
      def test_renew_obtain_cert_error(self):
          self._make_dummy_renewal_config()
          with mock.patch('certbot._internal.storage.RenewableCert') as mock_rc:
              mock_lineage = mock.MagicMock()
              mock_lineage.fullchain = "somewhere/fullchain.pem"
              mock_rc.return_value = mock_lineage
              mock_lineage.configuration = {
                  'renewalparams': {'authenticator': 'webroot'}}
              with mock.patch('certbot._internal.main.renew_cert') as mock_renew_cert:
                  mock_renew_cert.side_effect = Exception
                  self._test_renewal_common(True, None, error_expected=True,
                                            args=['renew'], should_renew=False)
  
      def test_renew_with_bad_cli_args(self):
          self._test_renewal_common(True, None, args='renew -d example.com'.split(),
                                    should_renew=False, error_expected=True)
          self._test_renewal_common(True, None, args='renew --csr {0}'.format(CSR).split(),
                                    should_renew=False, error_expected=True)
  
      def test_no_renewal_with_hooks(self):
          _, _, stdout = self._test_renewal_common(
              due_for_renewal=False, extra_args=None, should_renew=False,
              args=['renew', '--post-hook',
                    '{0} -c "from __future__ import print_function; print(\'hello world\');"'
                    .format(sys.executable)])
          self.assertTrue('No hooks were run.' in stdout.getvalue())
  
      @test_util.patch_get_utility()
      @mock.patch('certbot._internal.main._find_lineage_for_domains_and_certname')
      @mock.patch('certbot._internal.main._init_le_client')
      def test_certonly_reinstall(self, mock_init, mock_renewal, mock_get_utility):
          mock_renewal.return_value = ('reinstall', mock.MagicMock())
          mock_init.return_value = mock_client = mock.MagicMock()
          self._call(['-d', 'foo.bar', '-a', 'standalone', 'certonly'])
          self.assertFalse(mock_client.obtain_certificate.called)
          self.assertFalse(mock_client.obtain_and_enroll_certificate.called)
          self.assertEqual(mock_get_utility().add_message.call_count, 0)
          #self.assertTrue('donate' not in mock_get_utility().add_message.call_args[0][0])
  
      def _test_certonly_csr_common(self, extra_args=None):
          certr = 'certr'
          chain = 'chain'
          mock_client = mock.MagicMock()
          mock_client.obtain_certificate_from_csr.return_value = (certr, chain)
          cert_path = os.path.normpath(os.path.join(
              self.config.config_dir,
              'live/example.com/cert_512.pem'))
          full_path = os.path.normpath(os.path.join(
              self.config.config_dir,
              'live/example.com/fullchain.pem'))
          mock_client.save_certificate.return_value = cert_path, None, full_path
          with mock.patch('certbot._internal.main._init_le_client') as mock_init:
              mock_init.return_value = mock_client
              with test_util.patch_get_utility() as mock_get_utility:
                  chain_path = os.path.normpath(os.path.join(
                      self.config.config_dir,
                      'live/example.com/chain.pem'))
                  args = ('-a standalone certonly --csr {0} --cert-path {1} '
                          '--chain-path {2} --fullchain-path {3}').format(
                              CSR, cert_path, chain_path, full_path).split()
                  if extra_args:
                      args += extra_args
                  with mock.patch('certbot._internal.main.crypto_util'):
                      self._call(args)
  
          if '--dry-run' in args:
              self.assertFalse(mock_client.save_certificate.called)
          else:
              mock_client.save_certificate.assert_called_once_with(
                  certr, chain, cert_path, chain_path, full_path)
  
          return mock_get_utility
  
      @mock.patch('certbot._internal.eff.handle_subscription')
      def test_certonly_csr(self, mock_subscription):
          mock_get_utility = self._test_certonly_csr_common()
          cert_msg = mock_get_utility().add_message.call_args_list[0][0][0]
          self.assertTrue('fullchain.pem' in cert_msg)
          self.assertFalse('Your key file has been saved at' in cert_msg)
          self.assertTrue(
              'donate' in mock_get_utility().add_message.call_args[0][0])
          self.assertTrue(mock_subscription.called)
  
      def test_certonly_csr_dry_run(self):
          mock_get_utility = self._test_certonly_csr_common(['--dry-run'])
          self.assertEqual(mock_get_utility().add_message.call_count, 1)
          self.assertTrue(
              'dry run' in mock_get_utility().add_message.call_args[0][0])
  
      @mock.patch('certbot._internal.main._delete_if_appropriate')
      @mock.patch('certbot._internal.main.client.acme_client')
      def test_revoke_with_key(self, mock_acme_client,
              mock_delete_if_appropriate):
          mock_delete_if_appropriate.return_value = False
          server = 'foo.bar'
          self._call_no_clientmock(['--cert-path', SS_CERT_PATH, '--key-path', RSA2048_KEY_PATH,
                                   '--server', server, 'revoke'])
          with open(RSA2048_KEY_PATH, 'rb') as f:
              mock_acme_client.BackwardsCompatibleClientV2.assert_called_once_with(
                  mock.ANY, jose.JWK.load(f.read()), server)
          with open(SS_CERT_PATH, 'rb') as f:
              cert = crypto_util.pyopenssl_load_certificate(f.read())[0]
              mock_revoke = mock_acme_client.BackwardsCompatibleClientV2().revoke
              mock_revoke.assert_called_once_with(
                      jose.ComparableX509(cert),
                      mock.ANY)
  
      def test_revoke_with_key_mismatch(self):
          server = 'foo.bar'
          self.assertRaises(errors.Error, self._call_no_clientmock,
              ['--cert-path', CERT, '--key-path', KEY,
                                   '--server', server, 'revoke'])
  
      @mock.patch('certbot._internal.main._delete_if_appropriate')
      @mock.patch('certbot._internal.main._determine_account')
      def test_revoke_without_key(self, mock_determine_account,
              mock_delete_if_appropriate):
          mock_delete_if_appropriate.return_value = False
          mock_determine_account.return_value = (mock.MagicMock(), None)
          _, _, _, client = self._call(['--cert-path', CERT, 'revoke'])
          with open(CERT) as f:
              cert = crypto_util.pyopenssl_load_certificate(f.read())[0]
              mock_revoke = client.acme_from_config_key().revoke
              mock_revoke.assert_called_once_with(
                      jose.ComparableX509(cert),
                      mock.ANY)
  
      @mock.patch('certbot._internal.log.post_arg_parse_setup')
      def test_register(self, _):
          with mock.patch('certbot._internal.main.client') as mocked_client:
              acc = mock.MagicMock()
              acc.id = "imaginary_account"
              mocked_client.register.return_value = (acc, "worked")
              self._call_no_clientmock(["register", "--email", "user@example.org"])
              # TODO: It would be more correct to explicitly check that
              #       _determine_account() gets called in the above case,
              #       but coverage statistics should also show that it did.
              with mock.patch('certbot._internal.main.account') as mocked_account:
                  mocked_storage = mock.MagicMock()
                  mocked_account.AccountFileStorage.return_value = mocked_storage
                  mocked_storage.find_all.return_value = ["an account"]
                  x = self._call_no_clientmock(["register", "--email", "user@example.org"])
                  self.assertTrue("There is an existing account" in x[0])
  
      def test_update_account_no_existing_accounts(self):
          # with mock.patch('certbot._internal.main.client') as mocked_client:
          with mock.patch('certbot._internal.main.account') as mocked_account:
              mocked_storage = mock.MagicMock()
              mocked_account.AccountFileStorage.return_value = mocked_storage
              mocked_storage.find_all.return_value = []
              x = self._call_no_clientmock(
                  ["update_account", "--email",
                   "user@example.org"])
              self.assertTrue("Could not find an existing account" in x[0])
  
      @mock.patch('certbot._internal.main.display_ops.get_email')
      @test_util.patch_get_utility()
      def test_update_account_with_email(self, mock_utility, mock_email):
          email = "user@example.com"
          mock_email.return_value = email
          with mock.patch('certbot._internal.eff.prepare_subscription') as mock_prepare:
              with mock.patch('certbot._internal.main._determine_account') as mocked_det:
                  with mock.patch('certbot._internal.main.account') as mocked_account:
                      with mock.patch('certbot._internal.main.client') as mocked_client:
                          mocked_storage = mock.MagicMock()
                          mocked_account.AccountFileStorage.return_value = mocked_storage
                          mocked_storage.find_all.return_value = ["an account"]
                          mocked_det.return_value = (mock.MagicMock(), "foo")
                          cb_client = mock.MagicMock()
                          mocked_client.Client.return_value = cb_client
                          x = self._call_no_clientmock(
                              ["update_account"])
                          # When registration change succeeds, the return value
                          # of register() is None
                          self.assertTrue(x[0] is None)
                          # and we got supposedly did update the registration from
                          # the server
                          self.assertTrue(
                              cb_client.acme.update_registration.called)
                          # and we saved the updated registration on disk
                          self.assertTrue(mocked_storage.update_regr.called)
                          self.assertTrue(
                              email in mock_utility().add_message.call_args[0][0])
                          self.assertTrue(mock_prepare.called)
  
      @mock.patch('certbot._internal.plugins.selection.choose_configurator_plugins')
      @mock.patch('certbot._internal.updater._run_updaters')
      def test_plugin_selection_error(self, mock_run, mock_choose):
          mock_choose.side_effect = errors.PluginSelectionError
          self.assertRaises(errors.PluginSelectionError, main.renew_cert,
                            None, None, None)
  
          self.config.dry_run = False
          updater.run_generic_updaters(self.config, None, None)
          # Make sure we're returning None, and hence not trying to run the
          # without installer
          self.assertFalse(mock_run.called)
  
  
  class UnregisterTest(unittest.TestCase):
      def setUp(self):
          self.patchers = {
              '_determine_account': mock.patch('certbot._internal.main._determine_account'),
              'account': mock.patch('certbot._internal.main.account'),
              'client': mock.patch('certbot._internal.main.client'),
              'get_utility': test_util.patch_get_utility()}
          self.mocks = dict((k, v.start()) for k, v in self.patchers.items())
  
      def tearDown(self):
          for patch in self.patchers.values():
              patch.stop()
  
      def test_abort_unregister(self):
          self.mocks['account'].AccountFileStorage.return_value = mock.Mock()
  
          util_mock = self.mocks['get_utility']()
          util_mock.yesno.return_value = False
  
          config = mock.Mock()
          unused_plugins = mock.Mock()
  
          res = main.unregister(config, unused_plugins)
          self.assertEqual(res, "Deactivation aborted.")
  
      def test_unregister(self):
          mocked_storage = mock.MagicMock()
          mocked_storage.find_all.return_value = ["an account"]
  
          self.mocks['account'].AccountFileStorage.return_value = mocked_storage
          self.mocks['_determine_account'].return_value = (mock.MagicMock(), "foo")
  
          cb_client = mock.MagicMock()
          self.mocks['client'].Client.return_value = cb_client
  
          config = mock.MagicMock()
          unused_plugins = mock.MagicMock()
  
          res = main.unregister(config, unused_plugins)
  
          self.assertTrue(res is None)
          self.assertTrue(cb_client.acme.deactivate_registration.called)
          m = "Account deactivated."
          self.assertTrue(m in self.mocks['get_utility']().add_message.call_args[0][0])
  
      def test_unregister_no_account(self):
          mocked_storage = mock.MagicMock()
          mocked_storage.find_all.return_value = []
          self.mocks['account'].AccountFileStorage.return_value = mocked_storage
  
          cb_client = mock.MagicMock()
          self.mocks['client'].Client.return_value = cb_client
  
          config = mock.MagicMock()
          unused_plugins = mock.MagicMock()
  
          res = main.unregister(config, unused_plugins)
          m = "Could not find existing account to deactivate."
          self.assertEqual(res, m)
          self.assertFalse(cb_client.acme.deactivate_registration.called)
  
  
  class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
      """Tests for certbot._internal.main.make_or_verify_needed_dirs."""
  
      @mock.patch("certbot._internal.main.util")
      def test_it(self, mock_util):
          main.make_or_verify_needed_dirs(self.config)
          for core_dir in (self.config.config_dir, self.config.work_dir,):
              mock_util.set_up_core_dir.assert_any_call(
                  core_dir, constants.CONFIG_DIRS_MODE,
                  self.config.strict_permissions
              )
  
          hook_dirs = (self.config.renewal_pre_hooks_dir,
                       self.config.renewal_deploy_hooks_dir,
                       self.config.renewal_post_hooks_dir,)
          for hook_dir in hook_dirs:
              # default mode of 755 is used
              mock_util.make_or_verify_dir.assert_any_call(
                  hook_dir, strict=self.config.strict_permissions)
  
  
  class EnhanceTest(test_util.ConfigTestCase):
      """Tests for certbot._internal.main.enhance."""
  
      def setUp(self):
          super(EnhanceTest, self).setUp()
          self.get_utility_patch = test_util.patch_get_utility()
          self.mock_get_utility = self.get_utility_patch.start()
          self.mockinstaller = mock.MagicMock(spec=enhancements.AutoHSTSEnhancement)
  
      def tearDown(self):
          self.get_utility_patch.stop()
  
      def _call(self, args):
          plugins = disco.PluginsRegistry.find_all()
          config = configuration.NamespaceConfig(
              cli.prepare_and_parse_args(plugins, args))
  
          with mock.patch('certbot._internal.cert_manager.get_certnames') as mock_certs:
              mock_certs.return_value = ['example.com']
              with mock.patch('certbot._internal.cert_manager.domains_for_certname') as mock_dom:
                  mock_dom.return_value = ['example.com']
                  with mock.patch('certbot._internal.main._init_le_client') as mock_init:
                      mock_client = mock.MagicMock()
                      mock_client.config = config
                      mock_init.return_value = mock_client
                      main.enhance(config, plugins)
                      return mock_client # returns the client
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main._find_domains_or_certname')
      def test_selection_question(self, mock_find, mock_choose, mock_lineage, _rec):
          mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
          mock_choose.return_value = ['example.com']
          mock_find.return_value = (None, None)
          with mock.patch('certbot._internal.main.plug_sel.pick_installer') as mock_pick:
              self._call(['enhance', '--redirect'])
              self.assertTrue(mock_pick.called)
              # Check that the message includes "enhancements"
              self.assertTrue("enhancements" in mock_pick.call_args[0][3])
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main._find_domains_or_certname')
      def test_selection_auth_warning(self, mock_find, mock_choose, mock_lineage, _rec):
          mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
          mock_choose.return_value = ["example.com"]
          mock_find.return_value = (None, None)
          with mock.patch('certbot._internal.main.plug_sel.pick_installer'):
              with mock.patch('certbot._internal.main.plug_sel.logger.warning') as mock_log:
                  mock_client = self._call(['enhance', '-a', 'webroot', '--redirect'])
                  self.assertTrue(mock_log.called)
                  self.assertTrue("make sense" in mock_log.call_args[0][0])
                  self.assertTrue(mock_client.enhance_config.called)
  
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      def test_enhance_config_call(self, _rec, mock_choose, mock_lineage):
          mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
          mock_choose.return_value = ["example.com"]
          with mock.patch('certbot._internal.main.plug_sel.pick_installer'):
              mock_client = self._call(['enhance', '--redirect', '--hsts'])
              req_enh = ["redirect", "hsts"]
              not_req_enh = ["uir"]
              self.assertTrue(mock_client.enhance_config.called)
              self.assertTrue(
                  all(getattr(mock_client.config, e) for e in req_enh))
              self.assertFalse(
                  any(getattr(mock_client.config, e) for e in not_req_enh))
              self.assertTrue(
                  "example.com" in mock_client.enhance_config.call_args[0][0])
  
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      def test_enhance_noninteractive(self, _rec, mock_choose, mock_lineage):
          mock_lineage.return_value = mock.MagicMock(
              chain_path="/tmp/nonexistent")
          mock_choose.return_value = ["example.com"]
          with mock.patch('certbot._internal.main.plug_sel.pick_installer'):
              mock_client = self._call(['enhance', '--redirect',
                                        '--hsts', '--non-interactive'])
              self.assertTrue(mock_client.enhance_config.called)
              self.assertFalse(mock_choose.called)
  
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      def test_user_abort_domains(self, _rec, mock_choose):
          mock_choose.return_value = []
          with mock.patch('certbot._internal.main.plug_sel.pick_installer'):
              self.assertRaises(errors.Error,
                                self._call,
                                ['enhance', '--redirect', '--hsts'])
  
      def test_no_enhancements_defined(self):
          self.assertRaises(errors.MisconfigurationError,
                            self._call, ['enhance', '-a', 'null'])
  
      @mock.patch('certbot._internal.main.plug_sel.choose_configurator_plugins')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      def test_plugin_selection_error(self, _rec, mock_choose, mock_pick):
          mock_choose.return_value = ["example.com"]
          mock_pick.return_value = (None, None)
          mock_pick.side_effect = errors.PluginSelectionError()
          mock_client = self._call(['enhance', '--hsts'])
          self.assertFalse(mock_client.enhance_config.called)
  
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @test_util.patch_get_utility()
      def test_enhancement_enable(self, _, _rec, mock_inst, mock_choose, mock_lineage):
          mock_inst.return_value = self.mockinstaller
          mock_choose.return_value = ["example.com", "another.tld"]
          mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
          self._call(['enhance', '--auto-hsts'])
          self.assertTrue(self.mockinstaller.enable_autohsts.called)
          self.assertEqual(self.mockinstaller.enable_autohsts.call_args[0][1],
                            ["example.com", "another.tld"])
  
      @mock.patch('certbot._internal.cert_manager.lineage_for_certname')
      @mock.patch('certbot._internal.main.display_ops.choose_values')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @test_util.patch_get_utility()
      def test_enhancement_enable_not_supported(self, _, _rec, mock_inst, mock_choose, mock_lineage):
          mock_inst.return_value = null.Installer(self.config, "null")
          mock_choose.return_value = ["example.com", "another.tld"]
          mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
          self.assertRaises(
              errors.NotSupportedError,
              self._call, ['enhance', '--auto-hsts'])
  
      def test_enhancement_enable_conflict(self):
          self.assertRaises(
              errors.Error,
              self._call, ['enhance', '--auto-hsts', '--hsts'])
  
  
  class InstallTest(test_util.ConfigTestCase):
      """Tests for certbot._internal.main.install."""
  
      def setUp(self):
          super(InstallTest, self).setUp()
          self.mockinstaller = mock.MagicMock(spec=enhancements.AutoHSTSEnhancement)
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      def test_install_enhancement_not_supported(self, mock_inst, _rec):
          mock_inst.return_value = null.Installer(self.config, "null")
          plugins = disco.PluginsRegistry.find_all()
          self.config.auto_hsts = True
          self.config.certname = "nonexistent"
          self.assertRaises(errors.NotSupportedError,
                            main.install,
                            self.config, plugins)
  
      @mock.patch('certbot._internal.main.plug_sel.record_chosen_plugins')
      @mock.patch('certbot._internal.main.plug_sel.pick_installer')
      def test_install_enhancement_no_certname(self, mock_inst, _rec):
          mock_inst.return_value = self.mockinstaller
          plugins = disco.PluginsRegistry.find_all()
          self.config.auto_hsts = True
          self.config.certname = None
          self.config.key_path = "/tmp/nonexistent"
          self.config.cert_path = "/tmp/nonexistent"
          self.assertRaises(errors.ConfigurationError,
                            main.install,
                            self.config, plugins)
  
  
  if __name__ == '__main__':
      unittest.main()  # pragma: no cover
  