mirror of
https://github.com/certbot/certbot.git
synced 2026-06-06 23:32:06 -04:00
508 lines
20 KiB
Python
508 lines
20 KiB
Python
# coding=utf-8
|
|
"""Test certbot.display.ops."""
|
|
import sys
|
|
import unittest
|
|
|
|
import josepy as jose
|
|
try:
|
|
import mock
|
|
except ImportError: # pragma: no cover
|
|
from unittest import mock
|
|
import zope.component
|
|
|
|
from acme import messages
|
|
from certbot import errors
|
|
from certbot._internal import account
|
|
from certbot.compat import filesystem
|
|
from certbot.compat import os
|
|
from certbot.display import ops
|
|
from certbot.display import util as display_util
|
|
import certbot.tests.util as test_util
|
|
|
|
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
|
|
|
|
|
|
class GetEmailTest(unittest.TestCase):
|
|
"""Tests for certbot.display.ops.get_email."""
|
|
|
|
@classmethod
|
|
def _call(cls, **kwargs):
|
|
from certbot.display.ops import get_email
|
|
return get_email(**kwargs)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_cancel_none(self, mock_get_utility):
|
|
mock_input = mock_get_utility().input
|
|
mock_input.return_value = (display_util.CANCEL, "foo@bar.baz")
|
|
self.assertRaises(errors.Error, self._call)
|
|
self.assertRaises(errors.Error, self._call, optional=False)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_ok_safe(self, mock_get_utility):
|
|
mock_input = mock_get_utility().input
|
|
mock_input.return_value = (display_util.OK, "foo@bar.baz")
|
|
with mock.patch("certbot.display.ops.util.safe_email") as mock_safe_email:
|
|
mock_safe_email.return_value = True
|
|
self.assertTrue(self._call() == "foo@bar.baz")
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_ok_not_safe(self, mock_get_utility):
|
|
mock_input = mock_get_utility().input
|
|
mock_input.return_value = (display_util.OK, "foo@bar.baz")
|
|
with mock.patch("certbot.display.ops.util.safe_email") as mock_safe_email:
|
|
mock_safe_email.side_effect = [False, True]
|
|
self.assertTrue(self._call() == "foo@bar.baz")
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_invalid_flag(self, mock_get_utility):
|
|
invalid_txt = "There seem to be problems"
|
|
mock_input = mock_get_utility().input
|
|
mock_input.return_value = (display_util.OK, "foo@bar.baz")
|
|
with mock.patch("certbot.display.ops.util.safe_email") as mock_safe_email:
|
|
mock_safe_email.return_value = True
|
|
self._call()
|
|
self.assertTrue(invalid_txt not in mock_input.call_args[0][0])
|
|
self._call(invalid=True)
|
|
self.assertTrue(invalid_txt in mock_input.call_args[0][0])
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_optional_flag(self, mock_get_utility):
|
|
mock_input = mock_get_utility().input
|
|
mock_input.return_value = (display_util.OK, "foo@bar.baz")
|
|
with mock.patch("certbot.display.ops.util.safe_email") as mock_safe_email:
|
|
mock_safe_email.side_effect = [False, True]
|
|
self._call(optional=False)
|
|
for call in mock_input.call_args_list:
|
|
self.assertTrue(
|
|
"--register-unsafely-without-email" not in call[0][0])
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_optional_invalid_unsafe(self, mock_get_utility):
|
|
invalid_txt = "There seem to be problems"
|
|
mock_input = mock_get_utility().input
|
|
mock_input.return_value = (display_util.OK, "foo@bar.baz")
|
|
with mock.patch("certbot.display.ops.util.safe_email") as mock_safe_email:
|
|
mock_safe_email.side_effect = [False, True]
|
|
self._call(invalid=True)
|
|
self.assertTrue(invalid_txt in mock_input.call_args[0][0])
|
|
|
|
|
|
class ChooseAccountTest(test_util.TempDirTestCase):
|
|
"""Tests for certbot.display.ops.choose_account."""
|
|
def setUp(self):
|
|
super(ChooseAccountTest, self).setUp()
|
|
|
|
zope.component.provideUtility(display_util.FileDisplay(sys.stdout,
|
|
False))
|
|
|
|
self.account_keys_dir = os.path.join(self.tempdir, "keys")
|
|
filesystem.makedirs(self.account_keys_dir, 0o700)
|
|
|
|
self.config = mock.MagicMock(
|
|
accounts_dir=self.tempdir,
|
|
account_keys_dir=self.account_keys_dir,
|
|
server="certbot-demo.org")
|
|
self.key = KEY
|
|
|
|
self.acc1 = account.Account(messages.RegistrationResource(
|
|
uri=None, body=messages.Registration.from_data(
|
|
email="email1@g.com")), self.key)
|
|
self.acc2 = account.Account(messages.RegistrationResource(
|
|
uri=None, body=messages.Registration.from_data(
|
|
email="email2@g.com", phone="phone")), self.key)
|
|
|
|
@classmethod
|
|
def _call(cls, accounts):
|
|
return ops.choose_account(accounts)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_one(self, mock_util):
|
|
mock_util().menu.return_value = (display_util.OK, 0)
|
|
self.assertEqual(self._call([self.acc1]), self.acc1)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_two(self, mock_util):
|
|
mock_util().menu.return_value = (display_util.OK, 1)
|
|
self.assertEqual(self._call([self.acc1, self.acc2]), self.acc2)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_cancel(self, mock_util):
|
|
mock_util().menu.return_value = (display_util.CANCEL, 1)
|
|
self.assertTrue(self._call([self.acc1, self.acc2]) is None)
|
|
|
|
|
|
class GenHttpsNamesTest(unittest.TestCase):
|
|
"""Test _gen_https_names."""
|
|
def setUp(self):
|
|
zope.component.provideUtility(display_util.FileDisplay(sys.stdout,
|
|
False))
|
|
|
|
@classmethod
|
|
def _call(cls, domains):
|
|
from certbot.display.ops import _gen_https_names
|
|
return _gen_https_names(domains)
|
|
|
|
def test_zero(self):
|
|
self.assertEqual(self._call([]), "")
|
|
|
|
def test_one(self):
|
|
doms = [
|
|
"example.com",
|
|
"asllkjsadfljasdf.c",
|
|
]
|
|
for dom in doms:
|
|
self.assertEqual(self._call([dom]), "https://%s" % dom)
|
|
|
|
def test_two(self):
|
|
domains_list = [
|
|
["foo.bar.org", "bar.org"],
|
|
["paypal.google.facebook.live.com", "*.zombo.example.com"],
|
|
]
|
|
for doms in domains_list:
|
|
self.assertEqual(
|
|
self._call(doms),
|
|
"https://{dom[0]} and https://{dom[1]}".format(dom=doms))
|
|
|
|
def test_three(self):
|
|
doms = ["a.org", "b.org", "c.org"]
|
|
# We use an oxford comma
|
|
self.assertEqual(
|
|
self._call(doms),
|
|
"https://{dom[0]}, https://{dom[1]}, and https://{dom[2]}".format(
|
|
dom=doms))
|
|
|
|
def test_four(self):
|
|
doms = ["a.org", "b.org", "c.org", "d.org"]
|
|
exp = ("https://{dom[0]}, https://{dom[1]}, https://{dom[2]}, "
|
|
"and https://{dom[3]}".format(dom=doms))
|
|
|
|
self.assertEqual(self._call(doms), exp)
|
|
|
|
|
|
class ChooseNamesTest(unittest.TestCase):
|
|
"""Test choose names."""
|
|
def setUp(self):
|
|
zope.component.provideUtility(display_util.FileDisplay(sys.stdout,
|
|
False))
|
|
self.mock_install = mock.MagicMock()
|
|
|
|
@classmethod
|
|
def _call(cls, installer, question=None):
|
|
from certbot.display.ops import choose_names
|
|
return choose_names(installer, question)
|
|
|
|
@mock.patch("certbot.display.ops._choose_names_manually")
|
|
def test_no_installer(self, mock_manual):
|
|
self._call(None)
|
|
self.assertEqual(mock_manual.call_count, 1)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_no_installer_cancel(self, mock_util):
|
|
mock_util().input.return_value = (display_util.CANCEL, [])
|
|
self.assertEqual(self._call(None), [])
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_no_names_choose(self, mock_util):
|
|
self.mock_install().get_all_names.return_value = set()
|
|
domain = "example.com"
|
|
mock_util().input.return_value = (display_util.OK, domain)
|
|
|
|
actual_doms = self._call(self.mock_install)
|
|
self.assertEqual(mock_util().input.call_count, 1)
|
|
self.assertEqual(actual_doms, [domain])
|
|
self.assertTrue(
|
|
"configuration files" in mock_util().input.call_args[0][0])
|
|
|
|
def test_sort_names_trivial(self):
|
|
from certbot.display.ops import _sort_names
|
|
|
|
#sort an empty list
|
|
self.assertEqual(_sort_names([]), [])
|
|
|
|
#sort simple domains
|
|
some_domains = ["ex.com", "zx.com", "ax.com"]
|
|
self.assertEqual(_sort_names(some_domains), ["ax.com", "ex.com", "zx.com"])
|
|
|
|
#Sort subdomains of a single domain
|
|
domain = ".ex.com"
|
|
unsorted_short = ["e", "a", "z", "y"]
|
|
unsorted_long = [us + domain for us in unsorted_short]
|
|
|
|
sorted_short = sorted(unsorted_short)
|
|
sorted_long = [us + domain for us in sorted_short]
|
|
|
|
self.assertEqual(_sort_names(unsorted_long), sorted_long)
|
|
|
|
def test_sort_names_many(self):
|
|
from certbot.display.ops import _sort_names
|
|
|
|
unsorted_domains = [".cx.com", ".bx.com", ".ax.com", ".dx.com"]
|
|
unsorted_short = ["www", "bnother.long.subdomain", "a", "a.long.subdomain", "z", "b"]
|
|
#Of course sorted doesn't work here ;-)
|
|
sorted_short = ["a", "b", "a.long.subdomain", "bnother.long.subdomain", "www", "z"]
|
|
|
|
to_sort = []
|
|
for short in unsorted_short:
|
|
for domain in unsorted_domains:
|
|
to_sort.append(short+domain)
|
|
sortd = []
|
|
for domain in sorted(unsorted_domains):
|
|
for short in sorted_short:
|
|
sortd.append(short+domain)
|
|
self.assertEqual(_sort_names(to_sort), sortd)
|
|
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_filter_names_valid_return(self, mock_util):
|
|
self.mock_install.get_all_names.return_value = {"example.com"}
|
|
mock_util().checklist.return_value = (display_util.OK, ["example.com"])
|
|
|
|
names = self._call(self.mock_install)
|
|
self.assertEqual(names, ["example.com"])
|
|
self.assertEqual(mock_util().checklist.call_count, 1)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_filter_namees_override_question(self, mock_util):
|
|
self.mock_install.get_all_names.return_value = {"example.com"}
|
|
mock_util().checklist.return_value = (display_util.OK, ["example.com"])
|
|
names = self._call(self.mock_install, "Custom")
|
|
self.assertEqual(names, ["example.com"])
|
|
self.assertEqual(mock_util().checklist.call_count, 1)
|
|
self.assertEqual(mock_util().checklist.call_args[0][0], "Custom")
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_filter_names_nothing_selected(self, mock_util):
|
|
self.mock_install.get_all_names.return_value = {"example.com"}
|
|
mock_util().checklist.return_value = (display_util.OK, [])
|
|
|
|
self.assertEqual(self._call(self.mock_install), [])
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_filter_names_cancel(self, mock_util):
|
|
self.mock_install.get_all_names.return_value = {"example.com"}
|
|
mock_util().checklist.return_value = (
|
|
display_util.CANCEL, ["example.com"])
|
|
|
|
self.assertEqual(self._call(self.mock_install), [])
|
|
|
|
def test_get_valid_domains(self):
|
|
from certbot.display.ops import get_valid_domains
|
|
all_valid = ["example.com", "second.example.com",
|
|
"also.example.com", "under_score.example.com",
|
|
"justtld", "*.wildcard.com"]
|
|
all_invalid = ["öóòps.net", "uniçodé.com"]
|
|
two_valid = ["example.com", "úniçøde.com", "also.example.com"]
|
|
self.assertEqual(get_valid_domains(all_valid), all_valid)
|
|
self.assertEqual(get_valid_domains(all_invalid), [])
|
|
self.assertEqual(len(get_valid_domains(two_valid)), 2)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_choose_manually(self, mock_util):
|
|
from certbot.display.ops import _choose_names_manually
|
|
utility_mock = mock_util()
|
|
# No retry
|
|
utility_mock.yesno.return_value = False
|
|
# IDN and no retry
|
|
utility_mock.input.return_value = (display_util.OK,
|
|
"uniçodé.com")
|
|
self.assertEqual(_choose_names_manually(), [])
|
|
# IDN exception with previous mocks
|
|
with mock.patch(
|
|
"certbot.display.ops.display_util.separate_list_input"
|
|
) as mock_sli:
|
|
unicode_error = UnicodeEncodeError('mock', u'', 0, 1, 'mock')
|
|
mock_sli.side_effect = unicode_error
|
|
self.assertEqual(_choose_names_manually(), [])
|
|
# Valid domains
|
|
utility_mock.input.return_value = (display_util.OK,
|
|
("example.com,"
|
|
"under_score.example.com,"
|
|
"justtld,"
|
|
"valid.example.com"))
|
|
self.assertEqual(_choose_names_manually(),
|
|
["example.com", "under_score.example.com",
|
|
"justtld", "valid.example.com"])
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_choose_manually_retry(self, mock_util):
|
|
from certbot.display.ops import _choose_names_manually
|
|
utility_mock = mock_util()
|
|
# Three iterations
|
|
utility_mock.input.return_value = (display_util.OK,
|
|
"uniçodé.com")
|
|
utility_mock.yesno.side_effect = [True, True, False]
|
|
_choose_names_manually()
|
|
self.assertEqual(utility_mock.yesno.call_count, 3)
|
|
|
|
|
|
class SuccessInstallationTest(unittest.TestCase):
|
|
"""Test the success installation message."""
|
|
@classmethod
|
|
def _call(cls, names):
|
|
from certbot.display.ops import success_installation
|
|
success_installation(names)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_success_installation(self, mock_util):
|
|
mock_util().notification.return_value = None
|
|
names = ["example.com", "abc.com"]
|
|
|
|
self._call(names)
|
|
|
|
self.assertEqual(mock_util().notification.call_count, 1)
|
|
arg = mock_util().notification.call_args_list[0][0][0]
|
|
|
|
for name in names:
|
|
self.assertTrue(name in arg)
|
|
|
|
|
|
class SuccessRenewalTest(unittest.TestCase):
|
|
"""Test the success renewal message."""
|
|
@classmethod
|
|
def _call(cls, names):
|
|
from certbot.display.ops import success_renewal
|
|
success_renewal(names)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_success_renewal(self, mock_util):
|
|
mock_util().notification.return_value = None
|
|
names = ["example.com", "abc.com"]
|
|
|
|
self._call(names)
|
|
|
|
self.assertEqual(mock_util().notification.call_count, 1)
|
|
arg = mock_util().notification.call_args_list[0][0][0]
|
|
|
|
for name in names:
|
|
self.assertTrue(name in arg)
|
|
|
|
class SuccessRevocationTest(unittest.TestCase):
|
|
"""Test the success revocation message."""
|
|
@classmethod
|
|
def _call(cls, path):
|
|
from certbot.display.ops import success_revocation
|
|
success_revocation(path)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_success_revocation(self, mock_util):
|
|
mock_util().notification.return_value = None
|
|
path = "/path/to/cert.pem"
|
|
self._call(path)
|
|
mock_util().notification.assert_called_once_with(
|
|
"Congratulations! You have successfully revoked the certificate "
|
|
"that was located at {0}{1}{1}".format(
|
|
path,
|
|
os.linesep), pause=False)
|
|
self.assertTrue(path in mock_util().notification.call_args[0][0])
|
|
|
|
|
|
class ValidatorTests(unittest.TestCase):
|
|
"""Tests for `validated_input` and `validated_directory`."""
|
|
|
|
__ERROR = "Must be non-empty"
|
|
|
|
valid_input = "asdf"
|
|
valid_directory = "/var/www/html"
|
|
|
|
@staticmethod
|
|
def __validator(m):
|
|
if m == "":
|
|
raise errors.PluginError(ValidatorTests.__ERROR)
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_input_blank_with_validator(self, mock_util):
|
|
mock_util().input.side_effect = [(display_util.OK, ""),
|
|
(display_util.OK, ""),
|
|
(display_util.OK, ""),
|
|
(display_util.OK, self.valid_input)]
|
|
|
|
returned = ops.validated_input(self.__validator, "message", force_interactive=True)
|
|
self.assertEqual(ValidatorTests.__ERROR, mock_util().notification.call_args[0][0])
|
|
self.assertEqual(returned, (display_util.OK, self.valid_input))
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_input_validation_with_default(self, mock_util):
|
|
mock_util().input.side_effect = [(display_util.OK, self.valid_input)]
|
|
|
|
returned = ops.validated_input(self.__validator, "msg", default="other")
|
|
self.assertEqual(returned, (display_util.OK, self.valid_input))
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_input_validation_with_bad_default(self, mock_util):
|
|
mock_util().input.side_effect = [(display_util.OK, self.valid_input)]
|
|
|
|
self.assertRaises(AssertionError,
|
|
ops.validated_input,
|
|
self.__validator, "msg", default="")
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_input_cancel_with_validator(self, mock_util):
|
|
mock_util().input.side_effect = [(display_util.CANCEL, "")]
|
|
|
|
code, unused_raw = ops.validated_input(self.__validator, "message", force_interactive=True)
|
|
self.assertEqual(code, display_util.CANCEL)
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_directory_select_validation(self, mock_util):
|
|
mock_util().directory_select.side_effect = [(display_util.OK, ""),
|
|
(display_util.OK, self.valid_directory)]
|
|
|
|
returned = ops.validated_directory(self.__validator, "msg", force_interactive=True)
|
|
self.assertEqual(ValidatorTests.__ERROR, mock_util().notification.call_args[0][0])
|
|
self.assertEqual(returned, (display_util.OK, self.valid_directory))
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_directory_select_validation_with_default(self, mock_util):
|
|
mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)]
|
|
|
|
returned = ops.validated_directory(self.__validator, "msg", default="other")
|
|
self.assertEqual(returned, (display_util.OK, self.valid_directory))
|
|
|
|
@test_util.patch_get_utility()
|
|
def test_directory_select_validation_with_bad_default(self, mock_util):
|
|
mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)]
|
|
|
|
self.assertRaises(AssertionError,
|
|
ops.validated_directory,
|
|
self.__validator, "msg", default="")
|
|
|
|
|
|
class ChooseValuesTest(unittest.TestCase):
|
|
"""Test choose_values."""
|
|
@classmethod
|
|
def _call(cls, values, question):
|
|
from certbot.display.ops import choose_values
|
|
return choose_values(values, question)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_choose_names_success(self, mock_util):
|
|
items = ["first", "second", "third"]
|
|
mock_util().checklist.return_value = (display_util.OK, [items[2]])
|
|
result = self._call(items, None)
|
|
self.assertEqual(result, [items[2]])
|
|
self.assertTrue(mock_util().checklist.called)
|
|
self.assertEqual(mock_util().checklist.call_args[0][0], None)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_choose_names_success_question(self, mock_util):
|
|
items = ["first", "second", "third"]
|
|
question = "Which one?"
|
|
mock_util().checklist.return_value = (display_util.OK, [items[1]])
|
|
result = self._call(items, question)
|
|
self.assertEqual(result, [items[1]])
|
|
self.assertTrue(mock_util().checklist.called)
|
|
self.assertEqual(mock_util().checklist.call_args[0][0], question)
|
|
|
|
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
|
def test_choose_names_user_cancel(self, mock_util):
|
|
items = ["first", "second", "third"]
|
|
question = "Want to cancel?"
|
|
mock_util().checklist.return_value = (display_util.CANCEL, [])
|
|
result = self._call(items, question)
|
|
self.assertEqual(result, [])
|
|
self.assertTrue(mock_util().checklist.called)
|
|
self.assertEqual(mock_util().checklist.call_args[0][0], question)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() # pragma: no cover
|