Compare commits

...

4 Commits

Author SHA1 Message Date
Brad Warren
a0f2713e34 uncomment code 2023-03-15 13:01:05 -07:00
Brad Warren
ef5958355f run monkeytype on macos 2023-03-15 13:01:05 -07:00
Brad Warren
13a61542fc fix stuff and make changes to help monkeytype 2023-03-15 13:01:05 -07:00
Brad Warren
5de37679ff apply monkeytype from windows 2023-03-15 13:01:04 -07:00
41 changed files with 1499 additions and 1442 deletions

View File

@@ -15,6 +15,7 @@ from certbot.compat import filesystem
from certbot.compat import misc
from certbot.compat import os
import certbot.tests.util as test_util
from unittest.mock import MagicMock
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
@@ -22,7 +23,7 @@ KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
class AccountTest(unittest.TestCase):
"""Tests for certbot._internal.account.Account."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal.account import Account
self.regr = mock.MagicMock()
self.meta = Account.Meta(
@@ -38,25 +39,25 @@ class AccountTest(unittest.TestCase):
mock_dt.datetime.now.return_value = self.meta.creation_dt
self.acc_no_meta = Account(self.regr, KEY)
def test_init(self):
def test_init(self) -> None:
assert self.regr == self.acc.regr
assert KEY == self.acc.key
assert self.meta == self.acc_no_meta.meta
def test_id(self):
def test_id(self) -> None:
assert self.acc.id == "7adac10320f585ddf118429c0c4af2cd"
def test_slug(self):
def test_slug(self) -> None:
assert self.acc.slug == "test.certbot.org@2015-07-04T14:04:10Z (7ada)"
def test_repr(self):
def test_repr(self) -> None:
assert repr(self.acc).startswith(
"<Account(i_am_a_regr, 7adac10320f585ddf118429c0c4af2cd, Meta(")
class MetaTest(unittest.TestCase):
"""Tests for certbot._internal.account.Meta."""
def test_deserialize_partial(self):
def test_deserialize_partial(self) -> None:
from certbot._internal.account import Account
meta = Account.Meta.json_loads(
'{'
@@ -67,7 +68,7 @@ class MetaTest(unittest.TestCase):
assert meta.creation_host is not None
assert meta.register_to_eff is None
def test_deserialize_full(self):
def test_deserialize_full(self) -> None:
from certbot._internal.account import Account
meta = Account.Meta.json_loads(
'{'
@@ -83,11 +84,11 @@ class MetaTest(unittest.TestCase):
class AccountMemoryStorageTest(unittest.TestCase):
"""Tests for certbot._internal.account.AccountMemoryStorage."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal.account import AccountMemoryStorage
self.storage = AccountMemoryStorage()
def test_it(self):
def test_it(self) -> None:
account = mock.Mock(id="x")
assert [] == self.storage.find_all()
with pytest.raises(errors.AccountNotFound):
@@ -102,7 +103,7 @@ class AccountMemoryStorageTest(unittest.TestCase):
class AccountFileStorageTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.account.AccountFileStorage."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
from certbot._internal.account import AccountFileStorage
@@ -120,11 +121,11 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
meta=meta)
self.mock_client = mock.MagicMock()
def test_init_creates_dir(self):
def test_init_creates_dir(self) -> None:
assert os.path.isdir(
misc.underscores_for_unsupported_characters_in_path(self.config.accounts_dir))
def test_save_and_restore(self):
def test_save_and_restore(self) -> None:
self.storage.save(self.acc, self.mock_client)
account_path = os.path.join(self.config.accounts_dir, self.acc.id)
assert os.path.exists(account_path)
@@ -137,7 +138,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
loaded = self.storage.load(self.acc.id)
assert self.acc == loaded
def test_update_regr(self):
def test_update_regr(self) -> None:
self.storage.update_regr(self.acc)
account_path = os.path.join(self.config.accounts_dir, self.acc.id)
assert os.path.exists(account_path)
@@ -146,7 +147,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
assert not os.path.exists(os.path.join(account_path, "meta.json"))
assert not os.path.exists(os.path.join(account_path, "private_key.json"))
def test_update_meta(self):
def test_update_meta(self) -> None:
self.storage.update_meta(self.acc)
account_path = os.path.join(self.config.accounts_dir, self.acc.id)
assert os.path.exists(account_path)
@@ -155,18 +156,18 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
assert not os.path.exists(os.path.join(account_path, "regr.json"))
assert not os.path.exists(os.path.join(account_path, "private_key.json"))
def test_find_all(self):
def test_find_all(self) -> None:
self.storage.save(self.acc, self.mock_client)
assert [self.acc] == self.storage.find_all()
def test_find_all_none_empty_list(self):
def test_find_all_none_empty_list(self) -> None:
assert [] == self.storage.find_all()
def test_find_all_accounts_dir_absent(self):
def test_find_all_accounts_dir_absent(self) -> None:
os.rmdir(self.config.accounts_dir)
assert [] == self.storage.find_all()
def test_find_all_load_skips(self):
def test_find_all_load_skips(self) -> None:
# pylint: disable=protected-access
self.storage._load_for_server_path = mock.MagicMock(
side_effect=["x", errors.AccountStorageError, "z"])
@@ -174,22 +175,22 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
mock_listdir.return_value = ["x", "y", "z"]
assert ["x", "z"] == self.storage.find_all()
def test_load_non_existent_raises_error(self):
def test_load_non_existent_raises_error(self) -> None:
with pytest.raises(errors.AccountNotFound):
self.storage.load("missing")
def _set_server(self, server):
def _set_server(self, server: str) -> None:
self.config.server = server
from certbot._internal.account import AccountFileStorage
self.storage = AccountFileStorage(self.config)
def test_find_all_neither_exists(self):
def test_find_all_neither_exists(self) -> None:
self._set_server('https://acme-staging-v02.api.letsencrypt.org/directory')
assert [] == self.storage.find_all()
assert [] == self.storage.find_all()
assert not os.path.islink(self.config.accounts_dir)
def test_find_all_find_before_save(self):
def test_find_all_find_before_save(self) -> None:
self._set_server('https://acme-staging-v02.api.letsencrypt.org/directory')
assert [] == self.storage.find_all()
self.storage.save(self.acc, self.mock_client)
@@ -200,7 +201,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
prev_server_path = 'https://acme-staging.api.letsencrypt.org/directory'
assert not os.path.isdir(self.config.accounts_dir_for_server_path(prev_server_path))
def test_find_all_save_before_find(self):
def test_find_all_save_before_find(self) -> None:
self._set_server('https://acme-staging-v02.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
assert [self.acc] == self.storage.find_all()
@@ -210,7 +211,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
prev_server_path = 'https://acme-staging.api.letsencrypt.org/directory'
assert not os.path.isdir(self.config.accounts_dir_for_server_path(prev_server_path))
def test_find_all_server_downgrade(self):
def test_find_all_server_downgrade(self) -> None:
# don't use v2 accounts with a v1 url
self._set_server('https://acme-staging-v02.api.letsencrypt.org/directory')
assert [] == self.storage.find_all()
@@ -219,20 +220,20 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
assert [] == self.storage.find_all()
def test_upgrade_version_staging(self):
def test_upgrade_version_staging(self) -> None:
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
self._set_server('https://acme-staging-v02.api.letsencrypt.org/directory')
assert [self.acc] == self.storage.find_all()
def test_upgrade_version_production(self):
def test_upgrade_version_production(self) -> None:
self._set_server('https://acme-v01.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
self._set_server('https://acme-v02.api.letsencrypt.org/directory')
assert [self.acc] == self.storage.find_all()
@mock.patch('certbot.compat.os.rmdir')
def test_corrupted_account(self, mock_rmdir):
def test_corrupted_account(self, mock_rmdir: MagicMock) -> None:
# pylint: disable=protected-access
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
@@ -242,7 +243,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
self._set_server('https://acme-staging-v02.api.letsencrypt.org/directory')
assert [] == self.storage.find_all()
def test_upgrade_load(self):
def test_upgrade_load(self) -> None:
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
prev_account = self.storage.load(self.acc.id)
@@ -250,7 +251,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
account = self.storage.load(self.acc.id)
assert prev_account == account
def test_upgrade_load_single_account(self):
def test_upgrade_load_single_account(self) -> None:
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
prev_account = self.storage.load(self.acc.id)
@@ -258,7 +259,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
account = self.storage.load(self.acc.id)
assert prev_account == account
def test_load_ioerror(self):
def test_load_ioerror(self) -> None:
self.storage.save(self.acc, self.mock_client)
mock_open = mock.mock_open()
mock_open.side_effect = IOError
@@ -266,24 +267,24 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
with pytest.raises(errors.AccountStorageError):
self.storage.load(self.acc.id)
def test_save_ioerrors(self):
def test_save_ioerrors(self) -> None:
mock_open = mock.mock_open()
mock_open.side_effect = IOError # TODO: [None, None, IOError]
with mock.patch("builtins.open", mock_open):
with pytest.raises(errors.AccountStorageError):
self.storage.save(self.acc, self.mock_client)
def test_delete(self):
def test_delete(self) -> None:
self.storage.save(self.acc, self.mock_client)
self.storage.delete(self.acc.id)
with pytest.raises(errors.AccountNotFound):
self.storage.load(self.acc.id)
def test_delete_no_account(self):
def test_delete_no_account(self) -> None:
with pytest.raises(errors.AccountNotFound):
self.storage.delete(self.acc.id)
def _assert_symlinked_account_removed(self):
def _assert_symlinked_account_removed(self) -> None:
# create v1 account
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
@@ -293,7 +294,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
with pytest.raises(errors.AccountNotFound):
self.storage.load(self.acc.id)
def _test_delete_folders(self, server_url):
def _test_delete_folders(self, server_url: str) -> None:
# create symlinked servers
self._set_server('https://acme-staging.api.letsencrypt.org/directory')
self.storage.save(self.acc, self.mock_client)
@@ -312,24 +313,24 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
with pytest.raises(errors.AccountNotFound):
self.storage.load(self.acc.id)
def test_delete_folders_up(self):
def test_delete_folders_up(self) -> None:
self._test_delete_folders('https://acme-staging.api.letsencrypt.org/directory')
self._assert_symlinked_account_removed()
def test_delete_folders_down(self):
def test_delete_folders_down(self) -> None:
self._test_delete_folders('https://acme-staging-v02.api.letsencrypt.org/directory')
self._assert_symlinked_account_removed()
def _set_server_and_stop_symlink(self, server_path):
def _set_server_and_stop_symlink(self, server_path: str) -> None:
self._set_server(server_path)
with open(os.path.join(self.config.accounts_dir, 'foo'), 'w') as f:
f.write('bar')
def test_delete_shared_account_up(self):
def test_delete_shared_account_up(self) -> None:
self._set_server_and_stop_symlink('https://acme-staging-v02.api.letsencrypt.org/directory')
self._test_delete_folders('https://acme-staging.api.letsencrypt.org/directory')
def test_delete_shared_account_down(self):
def test_delete_shared_account_down(self) -> None:
self._set_server_and_stop_symlink('https://acme-staging-v02.api.letsencrypt.org/directory')
self._test_delete_folders('https://acme-staging-v02.api.letsencrypt.org/directory')

View File

@@ -18,12 +18,17 @@ from certbot._internal.display import obj as display_obj
from certbot.plugins import common as plugin_common
from certbot.tests import acme_util
from certbot.tests import util as test_util
from acme.challenges import DNS01, HTTP01
from acme.messages import AuthorizationResource, ChallengeBody, Status
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
from typing import Callable, List, Tuple, Type, Union
from unittest.mock import MagicMock
class ChallengeFactoryTest(unittest.TestCase):
# pylint: disable=protected-access
def setUp(self):
def setUp(self) -> None:
from certbot._internal.auth_handler import AuthHandler
# Account is mocked...
@@ -33,18 +38,18 @@ class ChallengeFactoryTest(unittest.TestCase):
messages.STATUS_PENDING, "test", acme_util.CHALLENGES,
[messages.STATUS_PENDING] * 6)
def test_all(self):
def test_all(self) -> None:
achalls = self.handler._challenge_factory(
self.authzr, range(0, len(acme_util.CHALLENGES)))
assert [achall.chall for achall in achalls] == acme_util.CHALLENGES
def test_one_http(self):
def test_one_http(self) -> None:
achalls = self.handler._challenge_factory(self.authzr, [0])
assert [achall.chall for achall in achalls] == [acme_util.HTTP01]
def test_unrecognized(self):
def test_unrecognized(self) -> None:
authzr = acme_util.gen_authzr(
messages.STATUS_PENDING, "test",
[mock.Mock(chall="chall", typ="unrecognized")],
@@ -61,7 +66,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
"""
def setUp(self):
def setUp(self) -> None:
from certbot._internal.auth_handler import AuthHandler
self.mock_display = mock.Mock()
@@ -83,10 +88,10 @@ class HandleAuthorizationsTest(unittest.TestCase):
logging.disable(logging.CRITICAL)
def tearDown(self):
def tearDown(self) -> None:
logging.disable(logging.NOTSET)
def _test_name1_http_01_1_common(self):
def _test_name1_http_01_1_common(self) -> None:
authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)
mock_order = mock.MagicMock(authorizations=[authzr])
@@ -110,10 +115,10 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert len(authzr) == 1
def test_name1_http_01_1_acme_2(self):
def test_name1_http_01_1_acme_2(self) -> None:
self._test_name1_http_01_1_common()
def test_name1_http_01_1_dns_1_acme_2(self):
def test_name1_http_01_1_dns_1_acme_2(self) -> None:
self.mock_net.poll.side_effect = _gen_mock_on_poll()
self.mock_auth.get_chall_pref.return_value.append(challenges.DNS01)
@@ -133,7 +138,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
# Length of authorizations list
assert len(authzr) == 1
def test_name3_http_01_3_common_acme_2(self):
def test_name3_http_01_3_common_acme_2(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES),
gen_dom_authzr(domain="1", challs=acme_util.CHALLENGES),
gen_dom_authzr(domain="2", challs=acme_util.CHALLENGES)]
@@ -151,7 +156,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert len(authzr) == 3
def test_debug_challenges(self):
def test_debug_challenges(self) -> None:
config = mock.Mock(debug_challenges=True, verbose_count=0)
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
@@ -173,7 +178,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert b64encode(account_key_thumbprint).decode() not in \
self.mock_display.notification.call_args[0][0]
def test_debug_challenges_verbose(self):
def test_debug_challenges_verbose(self) -> None:
config = mock.Mock(debug_challenges=True, verbose_count=1)
authzrs = [gen_dom_authzr(domain="0", challs=[acme_util.HTTP01]),
gen_dom_authzr(domain="1", challs=[acme_util.DNS01])]
@@ -203,7 +208,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert authzrs[1].body.challenges[0].validation(self.mock_account.key) in \
self.mock_display.notification.call_args[0][0]
def test_perform_failure(self):
def test_perform_failure(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
@@ -212,7 +217,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
with pytest.raises(errors.AuthorizationError):
self.handler.handle_authorizations(mock_order, self.mock_config)
def test_max_retries_exceeded(self):
def test_max_retries_exceeded(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
@@ -225,7 +230,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
self.handler.handle_authorizations(mock_order, self.mock_config, False, 1)
@mock.patch('certbot._internal.auth_handler.time.sleep')
def test_deadline_exceeded(self, mock_sleep):
def test_deadline_exceeded(self, mock_sleep: MagicMock) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
@@ -257,12 +262,12 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert abs(mock_sleep.call_args_list[1][0][0] - (interval - 1)) <= 1
assert abs(mock_sleep.call_args_list[2][0][0] - (interval/2 - 1)) <= 1
def test_no_domains(self):
def test_no_domains(self) -> None:
mock_order = mock.MagicMock(authorizations=[])
with pytest.raises(errors.AuthorizationError):
self.handler.handle_authorizations(mock_order, self.mock_config)
def test_preferred_challenge_choice_common_acme_2(self):
def test_preferred_challenge_choice_common_acme_2(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
@@ -277,20 +282,20 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert self.mock_auth.cleanup.call_count == 1
assert self.mock_auth.cleanup.call_args[0][0][0].typ == "http-01"
def test_preferred_challenges_not_supported_acme_2(self):
def test_preferred_challenges_not_supported_acme_2(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
self.handler.pref_challs.append(challenges.DNS01.typ)
with pytest.raises(errors.AuthorizationError):
self.handler.handle_authorizations(mock_order, self.mock_config)
def test_dns_only_challenge_not_supported(self):
def test_dns_only_challenge_not_supported(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=[acme_util.DNS01])]
mock_order = mock.MagicMock(authorizations=authzrs)
with pytest.raises(errors.AuthorizationError):
self.handler.handle_authorizations(mock_order, self.mock_config)
def test_perform_error(self):
def test_perform_error(self) -> None:
self.mock_auth.perform.side_effect = errors.AuthorizationError
authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)
@@ -301,7 +306,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert self.mock_auth.cleanup.call_count == 1
assert self.mock_auth.cleanup.call_args[0][0][0].typ == "http-01"
def test_answer_error(self):
def test_answer_error(self) -> None:
self.mock_net.answer_challenge.side_effect = errors.AuthorizationError
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
@@ -312,7 +317,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert self.mock_auth.cleanup.call_count == 1
assert self.mock_auth.cleanup.call_args[0][0][0].typ == "http-01"
def test_incomplete_authzr_error(self):
def test_incomplete_authzr_error(self) -> None:
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
self.mock_net.poll.side_effect = _gen_mock_on_poll(status=messages.STATUS_INVALID)
@@ -323,7 +328,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert self.mock_auth.cleanup.call_count == 1
assert self.mock_auth.cleanup.call_args[0][0][0].typ == "http-01"
def test_best_effort(self):
def test_best_effort(self) -> None:
def _conditional_mock_on_poll(authzr):
"""This mock will invalidate one authzr, and invalidate the other one"""
valid_mock = _gen_mock_on_poll(messages.STATUS_VALID)
@@ -355,7 +360,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
# Despite best_effort=True, process will fail because no authzr is valid.
self.handler.handle_authorizations(mock_order, self.mock_config, True)
def test_validated_challenge_not_rerun(self):
def test_validated_challenge_not_rerun(self) -> None:
# With a pending challenge that is not supported by the plugin, we
# expect an exception to be raised.
authzr = acme_util.gen_authzr(
@@ -376,7 +381,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
mock_order = mock.MagicMock(authorizations=[authzr])
self.handler.handle_authorizations(mock_order, self.mock_config)
def test_valid_authzrs_deactivated(self):
def test_valid_authzrs_deactivated(self) -> None:
"""When we deactivate valid authzrs in an orderr, we expect them to become deactivated
and to receive a list of deactivated authzrs in return."""
def _mock_deactivate(authzr):
@@ -410,7 +415,7 @@ class HandleAuthorizationsTest(unittest.TestCase):
assert failed[0].body.status == messages.STATUS_VALID
def _gen_mock_on_poll(status=messages.STATUS_VALID, retry=0, wait_value=1):
def _gen_mock_on_poll(status: Status=messages.STATUS_VALID, retry: int=0, wait_value: int=1) -> Callable:
state = {'count': retry}
def _mock(authzr):
@@ -428,11 +433,11 @@ def _gen_mock_on_poll(status=messages.STATUS_VALID, retry=0, wait_value=1):
class ChallbToAchallTest(unittest.TestCase):
"""Tests for certbot._internal.auth_handler.challb_to_achall."""
def _call(self, challb):
def _call(self, challb: ChallengeBody) -> KeyAuthorizationAnnotatedChallenge:
from certbot._internal.auth_handler import challb_to_achall
return challb_to_achall(challb, "account_key", "domain")
def test_it(self):
def test_it(self) -> None:
assert self._call(acme_util.HTTP01_P) == \
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.HTTP01_P, account_key="account_key",
@@ -443,18 +448,18 @@ class GenChallengePathTest(unittest.TestCase):
"""Tests for certbot._internal.auth_handler.gen_challenge_path.
"""
def setUp(self):
def setUp(self) -> None:
logging.disable(logging.FATAL)
def tearDown(self):
def tearDown(self) -> None:
logging.disable(logging.NOTSET)
@classmethod
def _call(cls, challbs, preferences):
def _call(cls, challbs: Union[Tuple[ChallengeBody], Tuple[ChallengeBody, ChallengeBody]], preferences: List[Union[Type[HTTP01], Type[DNS01]]]) -> Tuple[int]:
from certbot._internal.auth_handler import gen_challenge_path
return gen_challenge_path(challbs, preferences)
def test_common_case(self):
def test_common_case(self) -> None:
"""Given DNS01 and HTTP01 with appropriate combos."""
challbs = (acme_util.DNS01_P, acme_util.HTTP01_P)
prefs = [challenges.DNS01, challenges.HTTP01]
@@ -462,7 +467,7 @@ class GenChallengePathTest(unittest.TestCase):
assert self._call(challbs, prefs) == (0,)
assert self._call(challbs[::-1], prefs) == (1,)
def test_not_supported(self):
def test_not_supported(self) -> None:
challbs = (acme_util.DNS01_P,)
prefs = [challenges.HTTP01]
@@ -476,7 +481,7 @@ class ReportFailedAuthzrsTest(unittest.TestCase):
# pylint: disable=protected-access
def setUp(self):
def setUp(self) -> None:
from certbot._internal.auth_handler import AuthHandler
self.mock_auth = mock.MagicMock(spec=plugin_common.Plugin, name="buzz")
@@ -511,7 +516,7 @@ class ReportFailedAuthzrsTest(unittest.TestCase):
self.authzr2.body.challenges = [http_01_diff]
@mock.patch('certbot._internal.auth_handler.display_util.notify')
def test_same_error_and_domain(self, mock_notify):
def test_same_error_and_domain(self, mock_notify: MagicMock) -> None:
self.handler._report_failed_authzrs([self.authzr1])
mock_notify.assert_called_with(
'\n'
@@ -528,7 +533,7 @@ class ReportFailedAuthzrsTest(unittest.TestCase):
)
@mock.patch('certbot._internal.auth_handler.display_util.notify')
def test_different_errors_and_domains(self, mock_notify):
def test_different_errors_and_domains(self, mock_notify: MagicMock) -> None:
self.mock_auth.name = "quux"
self.mock_auth.auth_hint.return_value = "quuuuuux"
self.handler._report_failed_authzrs([self.authzr1, self.authzr2])
@@ -551,7 +556,7 @@ class ReportFailedAuthzrsTest(unittest.TestCase):
)
@mock.patch('certbot._internal.auth_handler.display_util.notify')
def test_non_subclassed_authenticator(self, mock_notify):
def test_non_subclassed_authenticator(self, mock_notify: MagicMock) -> None:
"""If authenticator not derived from common.Plugin, we shouldn't call .auth_hint"""
from certbot._internal.auth_handler import AuthHandler
@@ -563,13 +568,13 @@ class ReportFailedAuthzrsTest(unittest.TestCase):
assert mock_notify.call_count == 1
def gen_auth_resp(chall_list):
def gen_auth_resp(chall_list: List[KeyAuthorizationAnnotatedChallenge]) -> List[str]:
"""Generate a dummy authorization response."""
return ["%s%s" % (chall.__class__.__name__, chall.domain)
for chall in chall_list]
def gen_dom_authzr(domain, challs):
def gen_dom_authzr(domain: str, challs: List[Union[HTTP01, DNS01]]) -> AuthorizationResource:
"""Generates new authzr for domains."""
return acme_util.gen_authzr(
messages.STATUS_PENDING, domain, challs,

View File

@@ -13,18 +13,22 @@ import pytest
from certbot import configuration
from certbot import errors
from certbot._internal.storage import ALL_FOUR
from certbot._internal.storage import RenewableCert, ALL_FOUR
from certbot.compat import filesystem
from certbot.compat import os
from certbot.display import util as display_util
from certbot.tests import util as test_util
import storage_test
from certbot.configuration import NamespaceConfig
from certbot.tests.util import FreezableMock
from typing import Callable, List, Optional
from unittest.mock import MagicMock
class BaseCertManagerTest(test_util.ConfigTestCase):
"""Base class for setting up Cert Manager tests.
"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.quiet = False
@@ -43,7 +47,7 @@ class BaseCertManagerTest(test_util.ConfigTestCase):
with open(os.path.join(self.config.renewal_configs_dir, "IGNORE.THIS"), "w") as junk:
junk.write("This file should be ignored!")
def _set_up_config(self, domain, custom_archive):
def _set_up_config(self, domain: str, custom_archive: Optional[str]) -> configobj.ConfigObj:
# TODO: maybe provide NamespaceConfig.make_dirs?
# TODO: main() should create those dirs, c.f. #902
filesystem.makedirs(os.path.join(self.config.live_dir, domain))
@@ -68,7 +72,7 @@ class BaseCertManagerTest(test_util.ConfigTestCase):
class UpdateLiveSymlinksTest(BaseCertManagerTest):
"""Tests for certbot._internal.cert_manager.update_live_symlinks
"""
def test_update_live_symlinks(self):
def test_update_live_symlinks(self) -> None:
"""Test update_live_symlinks"""
# create files with incorrect symlinks
from certbot._internal import cert_manager
@@ -107,7 +111,7 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
"""Tests for certbot._internal.cert_manager.delete
"""
def _call(self):
def _call(self) -> None:
from certbot._internal import cert_manager
cert_manager.delete(self.config)
@@ -115,8 +119,8 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
@mock.patch('certbot.display.util.notify')
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.storage.delete_files')
def test_delete_from_config_yes(self, mock_delete_files, mock_lineage_for_certname,
mock_notify, mock_util):
def test_delete_from_config_yes(self, mock_delete_files: MagicMock, mock_lineage_for_certname: MagicMock,
mock_notify: MagicMock, mock_util: FreezableMock) -> None:
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
mock_util().yesno.return_value = True
@@ -130,8 +134,8 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
@test_util.patch_display_util()
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.storage.delete_files')
def test_delete_from_config_no(self, mock_delete_files, mock_lineage_for_certname,
mock_util):
def test_delete_from_config_no(self, mock_delete_files: MagicMock, mock_lineage_for_certname: MagicMock,
mock_util: FreezableMock) -> None:
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
mock_util().yesno.return_value = False
@@ -142,8 +146,8 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
@test_util.patch_display_util()
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.storage.delete_files')
def test_delete_interactive_single_yes(self, mock_delete_files, mock_lineage_for_certname,
mock_util):
def test_delete_interactive_single_yes(self, mock_delete_files: MagicMock, mock_lineage_for_certname: MagicMock,
mock_util: FreezableMock) -> None:
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
mock_util().checklist.return_value = (display_util.OK, ["example.org"])
@@ -154,8 +158,8 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
@test_util.patch_display_util()
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.storage.delete_files')
def test_delete_interactive_single_no(self, mock_delete_files, mock_lineage_for_certname,
mock_util):
def test_delete_interactive_single_no(self, mock_delete_files: MagicMock, mock_lineage_for_certname: MagicMock,
mock_util: FreezableMock) -> None:
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
mock_util().checklist.return_value = (display_util.OK, ["example.org"])
@@ -166,8 +170,8 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
@test_util.patch_display_util()
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.storage.delete_files')
def test_delete_interactive_multiple_yes(self, mock_delete_files, mock_lineage_for_certname,
mock_util):
def test_delete_interactive_multiple_yes(self, mock_delete_files: MagicMock, mock_lineage_for_certname: MagicMock,
mock_util: FreezableMock) -> None:
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
mock_util().checklist.return_value = (display_util.OK, ["example.org", "other.org"])
@@ -180,8 +184,8 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
@test_util.patch_display_util()
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
@mock.patch('certbot._internal.storage.delete_files')
def test_delete_interactive_multiple_no(self, mock_delete_files, mock_lineage_for_certname,
mock_util):
def test_delete_interactive_multiple_no(self, mock_delete_files: MagicMock, mock_lineage_for_certname: MagicMock,
mock_util: FreezableMock) -> None:
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
mock_util().checklist.return_value = (display_util.OK, ["example.org", "other.org"])
@@ -193,20 +197,20 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
class CertificatesTest(BaseCertManagerTest):
"""Tests for certbot._internal.cert_manager.certificates
"""
def _certificates(self, *args, **kwargs):
def _certificates(self, *args, **kwargs) -> None:
from certbot._internal.cert_manager import certificates
return certificates(*args, **kwargs)
@mock.patch('certbot._internal.cert_manager.logger')
@test_util.patch_display_util()
def test_certificates_parse_fail(self, mock_utility, mock_logger):
def test_certificates_parse_fail(self, mock_utility: FreezableMock, mock_logger: MagicMock) -> None:
self._certificates(self.config)
assert mock_logger.warning.called #pylint: disable=no-member
assert mock_utility.called
@mock.patch('certbot._internal.cert_manager.logger')
@test_util.patch_display_util()
def test_certificates_quiet(self, mock_utility, mock_logger):
def test_certificates_quiet(self, mock_utility: FreezableMock, mock_logger: MagicMock) -> None:
self.config.quiet = True
self._certificates(self.config)
assert mock_utility.notification.called is False
@@ -217,8 +221,8 @@ class CertificatesTest(BaseCertManagerTest):
@test_util.patch_display_util()
@mock.patch("certbot._internal.storage.RenewableCert")
@mock.patch('certbot._internal.cert_manager._report_human_readable')
def test_certificates_parse_success(self, mock_report, mock_renewable_cert,
mock_utility, mock_logger, mock_verifier):
def test_certificates_parse_success(self, mock_report: MagicMock, mock_renewable_cert: MagicMock,
mock_utility: FreezableMock, mock_logger: MagicMock, mock_verifier: MagicMock) -> None:
mock_verifier.return_value = None
mock_report.return_value = ""
self._certificates(self.config)
@@ -229,7 +233,7 @@ class CertificatesTest(BaseCertManagerTest):
@mock.patch('certbot._internal.cert_manager.logger')
@test_util.patch_display_util()
def test_certificates_no_files(self, mock_utility, mock_logger):
def test_certificates_no_files(self, mock_utility: FreezableMock, mock_logger: MagicMock) -> None:
empty_tempdir = tempfile.mkdtemp()
empty_config = configuration.NamespaceConfig(mock.MagicMock(
config_dir=os.path.join(empty_tempdir, "config"),
@@ -246,7 +250,7 @@ class CertificatesTest(BaseCertManagerTest):
@mock.patch('certbot.crypto_util.get_serial_from_cert')
@mock.patch('certbot._internal.cert_manager.ocsp.RevocationChecker.ocsp_revoked')
def test_report_human_readable(self, mock_revoked, mock_serial):
def test_report_human_readable(self, mock_revoked: MagicMock, mock_serial: MagicMock) -> None:
mock_revoked.return_value = None
mock_serial.return_value = 1234567890
import datetime
@@ -326,8 +330,8 @@ class SearchLineagesTest(BaseCertManagerTest):
@mock.patch('certbot.util.make_or_verify_dir')
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.RenewableCert')
def test_cert_storage_error(self, mock_renewable_cert, mock_renewal_conf_files,
mock_make_or_verify_dir):
def test_cert_storage_error(self, mock_renewable_cert: MagicMock, mock_renewal_conf_files: MagicMock,
mock_make_or_verify_dir: MagicMock) -> None:
mock_renewal_conf_files.return_value = ["badfile"]
mock_renewable_cert.side_effect = errors.CertStorageError
from certbot._internal import cert_manager
@@ -343,8 +347,8 @@ class LineageForCertnameTest(BaseCertManagerTest):
@mock.patch('certbot.util.make_or_verify_dir')
@mock.patch('certbot._internal.storage.renewal_file_for_certname')
@mock.patch('certbot._internal.storage.RenewableCert')
def test_found_match(self, mock_renewable_cert, mock_renewal_conf_file,
mock_make_or_verify_dir):
def test_found_match(self, mock_renewable_cert: MagicMock, mock_renewal_conf_file: MagicMock,
mock_make_or_verify_dir: MagicMock) -> None:
mock_renewal_conf_file.return_value = "somefile.conf"
mock_match = mock.Mock(lineagename="example.com")
mock_renewable_cert.return_value = mock_match
@@ -354,7 +358,7 @@ class LineageForCertnameTest(BaseCertManagerTest):
@mock.patch('certbot.util.make_or_verify_dir')
@mock.patch('certbot._internal.storage.renewal_file_for_certname')
def test_no_match(self, mock_renewal_conf_file, mock_make_or_verify_dir):
def test_no_match(self, mock_renewal_conf_file: MagicMock, mock_make_or_verify_dir: MagicMock) -> None:
mock_renewal_conf_file.return_value = "other.com.conf"
from certbot._internal import cert_manager
assert cert_manager.lineage_for_certname(self.config, "example.com") is None
@@ -362,7 +366,7 @@ class LineageForCertnameTest(BaseCertManagerTest):
@mock.patch('certbot.util.make_or_verify_dir')
@mock.patch('certbot._internal.storage.renewal_file_for_certname')
def test_no_renewal_file(self, mock_renewal_conf_file, mock_make_or_verify_dir):
def test_no_renewal_file(self, mock_renewal_conf_file: MagicMock, mock_make_or_verify_dir: MagicMock) -> None:
mock_renewal_conf_file.side_effect = errors.CertStorageError()
from certbot._internal import cert_manager
assert cert_manager.lineage_for_certname(self.config, "example.com") is None
@@ -375,8 +379,8 @@ class DomainsForCertnameTest(BaseCertManagerTest):
@mock.patch('certbot.util.make_or_verify_dir')
@mock.patch('certbot._internal.storage.renewal_file_for_certname')
@mock.patch('certbot._internal.storage.RenewableCert')
def test_found_match(self, mock_renewable_cert, mock_renewal_conf_file,
mock_make_or_verify_dir):
def test_found_match(self, mock_renewable_cert: MagicMock, mock_renewal_conf_file: MagicMock,
mock_make_or_verify_dir: MagicMock) -> None:
mock_renewal_conf_file.return_value = "somefile.conf"
mock_match = mock.Mock(lineagename="example.com")
domains = ["example.com", "example.org"]
@@ -389,7 +393,7 @@ class DomainsForCertnameTest(BaseCertManagerTest):
@mock.patch('certbot.util.make_or_verify_dir')
@mock.patch('certbot._internal.storage.renewal_file_for_certname')
def test_no_match(self, mock_renewal_conf_file, mock_make_or_verify_dir):
def test_no_match(self, mock_renewal_conf_file: MagicMock, mock_make_or_verify_dir: MagicMock) -> None:
mock_renewal_conf_file.return_value = "somefile.conf"
from certbot._internal import cert_manager
assert cert_manager.domains_for_certname(self.config, "other.com") is None
@@ -399,18 +403,18 @@ class DomainsForCertnameTest(BaseCertManagerTest):
class RenameLineageTest(BaseCertManagerTest):
"""Tests for certbot._internal.cert_manager.rename_lineage"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.certname = "example.org"
self.config.new_certname = "after"
def _call(self, *args, **kwargs):
def _call(self, *args, **kwargs) -> None:
from certbot._internal import cert_manager
return cert_manager.rename_lineage(*args, **kwargs)
@mock.patch('certbot._internal.storage.renewal_conf_files')
@test_util.patch_display_util()
def test_no_certname(self, mock_get_utility, mock_renewal_conf_files):
def test_no_certname(self, mock_get_utility: FreezableMock, mock_renewal_conf_files: MagicMock) -> None:
self.config.certname = None
self.config.new_certname = "two"
@@ -430,7 +434,7 @@ class RenameLineageTest(BaseCertManagerTest):
self._call(self.config)
@test_util.patch_display_util()
def test_no_new_certname(self, mock_get_utility):
def test_no_new_certname(self, mock_get_utility: FreezableMock) -> None:
self.config.certname = "one"
self.config.new_certname = None
@@ -445,7 +449,7 @@ class RenameLineageTest(BaseCertManagerTest):
@test_util.patch_display_util()
@mock.patch('certbot._internal.cert_manager.lineage_for_certname')
def test_no_existing_certname(self, mock_lineage_for_certname, unused_get_utility):
def test_no_existing_certname(self, mock_lineage_for_certname: MagicMock, unused_get_utility: FreezableMock) -> None:
self.config.certname = "one"
self.config.new_certname = "two"
mock_lineage_for_certname.return_value = None
@@ -454,7 +458,7 @@ class RenameLineageTest(BaseCertManagerTest):
@test_util.patch_display_util()
@mock.patch("certbot._internal.storage.RenewableCert._check_symlinks")
def test_rename_cert(self, mock_check, unused_get_utility):
def test_rename_cert(self, mock_check: MagicMock, unused_get_utility: FreezableMock) -> None:
mock_check.return_value = True
self._call(self.config)
from certbot._internal import cert_manager
@@ -464,7 +468,7 @@ class RenameLineageTest(BaseCertManagerTest):
@test_util.patch_display_util()
@mock.patch("certbot._internal.storage.RenewableCert._check_symlinks")
def test_rename_cert_interactive_certname(self, mock_check, mock_get_utility):
def test_rename_cert_interactive_certname(self, mock_check: MagicMock, mock_get_utility: FreezableMock) -> None:
mock_check.return_value = True
self.config.certname = None
util_mock = mock_get_utility()
@@ -477,7 +481,7 @@ class RenameLineageTest(BaseCertManagerTest):
@test_util.patch_display_util()
@mock.patch("certbot._internal.storage.RenewableCert._check_symlinks")
def test_rename_cert_bad_new_certname(self, mock_check, unused_get_utility):
def test_rename_cert_bad_new_certname(self, mock_check: MagicMock, unused_get_utility: FreezableMock) -> None:
mock_check.return_value = True
# for example, don't rename to existing certname
@@ -493,13 +497,13 @@ class RenameLineageTest(BaseCertManagerTest):
class DuplicativeCertsTest(storage_test.BaseRenewableCertTest):
"""Test to avoid duplicate lineages."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config_file.write()
self._write_out_ex_kinds()
@mock.patch('certbot.util.make_or_verify_dir')
def test_find_duplicative_names(self, unused_makedir):
def test_find_duplicative_names(self, unused_makedir: MagicMock) -> None:
from certbot._internal.cert_manager import find_duplicative_certs
test_cert = test_util.load_vector('cert-san_512.pem')
with open(self.test_rc.cert, 'wb') as f:
@@ -531,7 +535,7 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest):
class CertPathToLineageTest(storage_test.BaseRenewableCertTest):
"""Tests for certbot._internal.cert_manager.cert_path_to_lineage"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config_file.write()
self._write_out_ex_kinds()
@@ -539,18 +543,18 @@ class CertPathToLineageTest(storage_test.BaseRenewableCertTest):
'fullchain.pem')
self.config.cert_path = self.fullchain
def _call(self, cli_config):
def _call(self, cli_config: NamespaceConfig) -> str:
from certbot._internal.cert_manager import cert_path_to_lineage
return cert_path_to_lineage(cli_config)
def _archive_files(self, cli_config, filetype):
def _archive_files(self, cli_config: RenewableCert, filetype: str) -> List[str]:
from certbot._internal.cert_manager import _archive_files
return _archive_files(cli_config, filetype)
def test_basic_match(self):
def test_basic_match(self) -> None:
assert 'example.org' == self._call(self.config)
def test_no_match_exists(self):
def test_no_match_exists(self) -> None:
bad_test_config = self.config
bad_test_config.cert_path = os.path.join(self.config.config_dir, 'live',
'SailorMoon', 'fullchain.pem')
@@ -558,13 +562,13 @@ class CertPathToLineageTest(storage_test.BaseRenewableCertTest):
self._call(bad_test_config)
@mock.patch('certbot._internal.cert_manager._acceptable_matches')
def test_options_fullchain(self, mock_acceptable_matches):
def test_options_fullchain(self, mock_acceptable_matches: MagicMock) -> None:
mock_acceptable_matches.return_value = [lambda x: x.fullchain_path]
self.config.fullchain_path = self.fullchain
assert 'example.org' == self._call(self.config)
@mock.patch('certbot._internal.cert_manager._acceptable_matches')
def test_options_cert_path(self, mock_acceptable_matches):
def test_options_cert_path(self, mock_acceptable_matches: MagicMock) -> None:
mock_acceptable_matches.return_value = [lambda x: x.cert_path]
test_cert_path = os.path.join(self.config.config_dir, 'live', 'example.org',
'cert.pem')
@@ -572,7 +576,7 @@ class CertPathToLineageTest(storage_test.BaseRenewableCertTest):
assert 'example.org' == self._call(self.config)
@mock.patch('certbot._internal.cert_manager._acceptable_matches')
def test_options_archive_cert(self, mock_acceptable_matches):
def test_options_archive_cert(self, mock_acceptable_matches: MagicMock) -> None:
# Also this and the next test check that the regex of _archive_files is working.
self.config.cert_path = os.path.join(self.config.config_dir, 'archive', 'example.org',
'cert11.pem')
@@ -580,14 +584,14 @@ class CertPathToLineageTest(storage_test.BaseRenewableCertTest):
assert 'example.org' == self._call(self.config)
@mock.patch('certbot._internal.cert_manager._acceptable_matches')
def test_options_archive_fullchain(self, mock_acceptable_matches):
def test_options_archive_fullchain(self, mock_acceptable_matches: MagicMock) -> None:
self.config.cert_path = os.path.join(self.config.config_dir, 'archive',
'example.org', 'fullchain11.pem')
mock_acceptable_matches.return_value = [lambda x:
self._archive_files(x, 'fullchain')]
assert 'example.org' == self._call(self.config)
def test_only_path(self):
def test_only_path(self) -> None:
self.config.cert_path = self.fullchain
assert 'example.org' == self._call(self.config)
@@ -596,7 +600,7 @@ class MatchAndCheckOverlaps(storage_test.BaseRenewableCertTest):
"""Tests for certbot._internal.cert_manager.match_and_check_overlaps w/o overlapping
archive dirs."""
# A test with real overlapping archive dirs can be found in tests/boulder_integration.sh
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config_file.write()
self._write_out_ex_kinds()
@@ -604,23 +608,23 @@ class MatchAndCheckOverlaps(storage_test.BaseRenewableCertTest):
'fullchain.pem')
self.config.cert_path = self.fullchain
def _call(self, cli_config, acceptable_matches, match_func, rv_func):
def _call(self, cli_config: NamespaceConfig, acceptable_matches: Optional[List[Callable]], match_func: Optional[Callable], rv_func: Optional[Callable]) -> List[str]:
from certbot._internal.cert_manager import match_and_check_overlaps
return match_and_check_overlaps(cli_config, acceptable_matches, match_func, rv_func)
def test_basic_match(self):
def test_basic_match(self) -> None:
from certbot._internal.cert_manager import _acceptable_matches
assert ['example.org'] == self._call(self.config, _acceptable_matches(),
lambda x: self.config.cert_path, lambda x: x.lineagename)
@mock.patch('certbot._internal.cert_manager._search_lineages')
def test_no_matches(self, mock_search_lineages):
def test_no_matches(self, mock_search_lineages: MagicMock) -> None:
mock_search_lineages.return_value = []
with pytest.raises(errors.Error):
self._call(self.config, None, None, None)
@mock.patch('certbot._internal.cert_manager._search_lineages')
def test_too_many_matches(self, mock_search_lineages):
def test_too_many_matches(self, mock_search_lineages: MagicMock) -> None:
mock_search_lineages.return_value = ['spider', 'dance']
with pytest.raises(errors.OverlappingMatchFound):
self._call(self.config, None, None, None)
@@ -629,7 +633,7 @@ class MatchAndCheckOverlaps(storage_test.BaseRenewableCertTest):
class GetCertnameTest(unittest.TestCase):
"""Tests for certbot._internal.cert_manager."""
def setUp(self):
def setUp(self) -> None:
get_utility_patch = test_util.patch_display_util()
self.mock_get_utility = get_utility_patch.start()
self.addCleanup(get_utility_patch.stop)
@@ -638,7 +642,7 @@ class GetCertnameTest(unittest.TestCase):
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.lineagename_for_filename')
def test_get_certnames(self, mock_name, mock_files):
def test_get_certnames(self, mock_name: MagicMock, mock_files: MagicMock) -> None:
mock_files.return_value = ['example.com.conf']
mock_name.return_value = 'example.com'
from certbot._internal import cert_manager
@@ -650,7 +654,7 @@ class GetCertnameTest(unittest.TestCase):
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.lineagename_for_filename')
def test_get_certnames_custom_prompt(self, mock_name, mock_files):
def test_get_certnames_custom_prompt(self, mock_name: MagicMock, mock_files: MagicMock) -> None:
mock_files.return_value = ['example.com.conf']
mock_name.return_value = 'example.com'
from certbot._internal import cert_manager
@@ -664,7 +668,7 @@ class GetCertnameTest(unittest.TestCase):
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.lineagename_for_filename')
def test_get_certnames_user_abort(self, mock_name, mock_files):
def test_get_certnames_user_abort(self, mock_name: MagicMock, mock_files: MagicMock) -> None:
mock_files.return_value = ['example.com.conf']
mock_name.return_value = 'example.com'
from certbot._internal import cert_manager
@@ -674,7 +678,7 @@ class GetCertnameTest(unittest.TestCase):
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.lineagename_for_filename')
def test_get_certnames_allow_multiple(self, mock_name, mock_files):
def test_get_certnames_allow_multiple(self, mock_name: MagicMock, mock_files: MagicMock) -> None:
mock_files.return_value = ['example.com.conf']
mock_name.return_value = 'example.com'
from certbot._internal import cert_manager
@@ -687,7 +691,7 @@ class GetCertnameTest(unittest.TestCase):
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.lineagename_for_filename')
def test_get_certnames_allow_multiple_custom_prompt(self, mock_name, mock_files):
def test_get_certnames_allow_multiple_custom_prompt(self, mock_name: MagicMock, mock_files: MagicMock) -> None:
mock_files.return_value = ['example.com.conf']
mock_name.return_value = 'example.com'
from certbot._internal import cert_manager
@@ -702,7 +706,7 @@ class GetCertnameTest(unittest.TestCase):
@mock.patch('certbot._internal.storage.renewal_conf_files')
@mock.patch('certbot._internal.storage.lineagename_for_filename')
def test_get_certnames_allow_multiple_user_abort(self, mock_name, mock_files):
def test_get_certnames_allow_multiple_user_abort(self, mock_name: MagicMock, mock_files: MagicMock) -> None:
mock_files.return_value = ['example.com.conf']
mock_name.return_value = 'example.com'
from certbot._internal import cert_manager

View File

@@ -19,13 +19,15 @@ from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
from certbot.tests.util import TempDirTestCase
from typing import List, Union
from unittest.mock import MagicMock
PLUGINS = disco.PluginsRegistry.find_all()
class TestReadFile(TempDirTestCase):
"""Test cli.read_file"""
def test_read_file(self):
def test_read_file(self) -> None:
curr_dir = os.getcwd()
try:
# On Windows current directory may be on a different drive than self.tempdir.
@@ -58,7 +60,7 @@ class TestReadFile(TempDirTestCase):
class FlagDefaultTest(unittest.TestCase):
"""Tests cli.flag_default"""
def test_default_directories(self):
def test_default_directories(self) -> None:
if os.name != 'nt':
assert cli.flag_default('config_dir') == '/etc/letsencrypt'
assert cli.flag_default('work_dir') == '/var/lib/letsencrypt'
@@ -73,21 +75,21 @@ class ParseTest(unittest.TestCase):
'''Test the cli args entrypoint'''
def setUp(self):
def setUp(self) -> None:
reload_module(cli)
@staticmethod
def _unmocked_parse(*args, **kwargs):
def _unmocked_parse(*args, **kwargs) -> argparse.Namespace:
"""Get result of cli.prepare_and_parse_args."""
return cli.prepare_and_parse_args(PLUGINS, *args, **kwargs)
@staticmethod
def parse(*args, **kwargs):
def parse(*args, **kwargs) -> argparse.Namespace:
"""Mocks certbot._internal.display.obj.get_display and calls _unmocked_parse."""
with test_util.patch_display_util():
return ParseTest._unmocked_parse(*args, **kwargs)
def _help_output(self, args):
def _help_output(self, args: List[str]) -> str:
"Run a command, and return the output string for scrutiny"
output = io.StringIO()
@@ -105,7 +107,7 @@ class ParseTest(unittest.TestCase):
return output.getvalue()
@mock.patch("certbot._internal.cli.helpful.flag_default")
def test_cli_ini_domains(self, mock_flag_default):
def test_cli_ini_domains(self, mock_flag_default: MagicMock) -> None:
with tempfile.NamedTemporaryFile() as tmp_config:
tmp_config.close() # close now because of compatibility issues on Windows
# use a shim to get ConfigArgParse to pick up tmp_config
@@ -125,12 +127,12 @@ class ParseTest(unittest.TestCase):
namespace = self.parse(["renew"])
assert namespace.domains == []
def test_no_args(self):
def test_no_args(self) -> None:
namespace = self.parse([])
for d in ('config_dir', 'logs_dir', 'work_dir'):
assert getattr(namespace, d) == cli.flag_default(d)
def test_install_abspath(self):
def test_install_abspath(self) -> None:
cert = 'cert'
key = 'key'
chain = 'chain'
@@ -146,7 +148,7 @@ class ParseTest(unittest.TestCase):
assert namespace.chain_path == os.path.abspath(chain)
assert namespace.fullchain_path == os.path.abspath(fullchain)
def test_help(self):
def test_help(self) -> None:
self._help_output(['--help']) # assert SystemExit is raised here
out = self._help_output(['--help', 'all'])
assert "--configurator" in out
@@ -204,7 +206,7 @@ class ParseTest(unittest.TestCase):
assert "%s" not in out
assert "{0}" not in out
def test_help_no_dashes(self):
def test_help_no_dashes(self) -> None:
self._help_output(['help']) # assert SystemExit is raised here
out = self._help_output(['help', 'all'])
@@ -223,7 +225,7 @@ class ParseTest(unittest.TestCase):
assert "--cert-path" in out
assert "--key-path" in out
def test_parse_domains(self):
def test_parse_domains(self) -> None:
short_args = ['-d', 'example.com']
namespace = self.parse(short_args)
assert namespace.domains == ['example.com']
@@ -249,7 +251,7 @@ class ParseTest(unittest.TestCase):
namespace = self.parse(long_args)
assert namespace.domains == ['example.com', 'another.net']
def test_preferred_challenges(self):
def test_preferred_challenges(self) -> None:
short_args = ['--preferred-challenges', 'http, dns']
namespace = self.parse(short_args)
@@ -263,17 +265,17 @@ class ParseTest(unittest.TestCase):
with pytest.raises(SystemExit):
self.parse(short_args)
def test_server_flag(self):
def test_server_flag(self) -> None:
namespace = self.parse('--server example.com'.split())
assert namespace.server == 'example.com'
def test_must_staple_flag(self):
def test_must_staple_flag(self) -> None:
short_args = ['--must-staple']
namespace = self.parse(short_args)
assert namespace.must_staple is True
assert namespace.staple is True
def _check_server_conflict_message(self, parser_args, conflicting_args):
def _check_server_conflict_message(self, parser_args: List[str], conflicting_args: Union[List[str], str]) -> None:
try:
self.parse(parser_args)
self.fail( # pragma: no cover
@@ -284,7 +286,7 @@ class ParseTest(unittest.TestCase):
for arg in conflicting_args:
assert arg in str(error)
def test_staging_flag(self):
def test_staging_flag(self) -> None:
short_args = ['--staging']
namespace = self.parse(short_args)
assert namespace.staging is True
@@ -293,7 +295,7 @@ class ParseTest(unittest.TestCase):
short_args += '--server example.com'.split()
self._check_server_conflict_message(short_args, '--staging')
def _assert_dry_run_flag_worked(self, namespace, existing_account):
def _assert_dry_run_flag_worked(self, namespace: argparse.Namespace, existing_account: bool) -> None:
assert namespace.dry_run is True
assert namespace.break_my_certs is True
assert namespace.staging is True
@@ -306,7 +308,7 @@ class ParseTest(unittest.TestCase):
assert namespace.tos is False
assert namespace.register_unsafely_without_email is False
def test_dry_run_flag(self):
def test_dry_run_flag(self) -> None:
config_dir = tempfile.mkdtemp()
short_args = '--dry-run --config-dir {0}'.format(config_dir).split()
with pytest.raises(errors.Error):
@@ -346,7 +348,7 @@ class ParseTest(unittest.TestCase):
self._check_server_conflict_message(short_args + ['--server', 'example.com', '--staging'],
conflicts)
def test_option_was_set(self):
def test_option_was_set(self) -> None:
key_size_option = 'rsa_key_size'
key_size_value = cli.flag_default(key_size_option)
self.parse('--rsa-key-size {0}'.format(key_size_value).split())
@@ -360,13 +362,13 @@ class ParseTest(unittest.TestCase):
assert not cli.option_was_set(
'authenticator', cli.flag_default('authenticator'))
def test_ecdsa_key_option(self):
def test_ecdsa_key_option(self) -> None:
elliptic_curve_option = 'elliptic_curve'
elliptic_curve_option_value = cli.flag_default(elliptic_curve_option)
self.parse('--elliptic-curve {0}'.format(elliptic_curve_option_value).split())
assert cli.option_was_set(elliptic_curve_option, elliptic_curve_option_value) is True
def test_invalid_key_type(self):
def test_invalid_key_type(self) -> None:
key_type_option = 'key_type'
key_type_value = cli.flag_default(key_type_option)
self.parse('--key-type {0}'.format(key_type_value).split())
@@ -375,7 +377,7 @@ class ParseTest(unittest.TestCase):
with pytest.raises(SystemExit):
self.parse("--key-type foo")
def test_encode_revocation_reason(self):
def test_encode_revocation_reason(self) -> None:
for reason, code in constants.REVOCATION_REASONS.items():
namespace = self.parse(['--reason', reason])
assert namespace.reason == code
@@ -383,18 +385,18 @@ class ParseTest(unittest.TestCase):
namespace = self.parse(['--reason', reason.upper()])
assert namespace.reason == code
def test_force_interactive(self):
def test_force_interactive(self) -> None:
with pytest.raises(errors.Error):
self.parse("renew --force-interactive".split())
with pytest.raises(errors.Error):
self.parse("-n --force-interactive".split())
def test_deploy_hook_conflict(self):
def test_deploy_hook_conflict(self) -> None:
with mock.patch("certbot._internal.cli.sys.stderr"):
with pytest.raises(SystemExit):
self.parse("--renew-hook foo --deploy-hook bar".split())
def test_deploy_hook_matches_renew_hook(self):
def test_deploy_hook_matches_renew_hook(self) -> None:
value = "foo"
namespace = self.parse(["--renew-hook", value,
"--deploy-hook", value,
@@ -402,19 +404,19 @@ class ParseTest(unittest.TestCase):
assert namespace.deploy_hook == value
assert namespace.renew_hook == value
def test_deploy_hook_sets_renew_hook(self):
def test_deploy_hook_sets_renew_hook(self) -> None:
value = "foo"
namespace = self.parse(
["--deploy-hook", value, "--disable-hook-validation"])
assert namespace.deploy_hook == value
assert namespace.renew_hook == value
def test_renew_hook_conflict(self):
def test_renew_hook_conflict(self) -> None:
with mock.patch("certbot._internal.cli.sys.stderr"):
with pytest.raises(SystemExit):
self.parse("--deploy-hook foo --renew-hook bar".split())
def test_renew_hook_matches_deploy_hook(self):
def test_renew_hook_matches_deploy_hook(self) -> None:
value = "foo"
namespace = self.parse(["--deploy-hook", value,
"--renew-hook", value,
@@ -422,26 +424,26 @@ class ParseTest(unittest.TestCase):
assert namespace.deploy_hook == value
assert namespace.renew_hook == value
def test_renew_hook_does_not_set_renew_hook(self):
def test_renew_hook_does_not_set_renew_hook(self) -> None:
value = "foo"
namespace = self.parse(
["--renew-hook", value, "--disable-hook-validation"])
assert namespace.deploy_hook is None
assert namespace.renew_hook == value
def test_max_log_backups_error(self):
def test_max_log_backups_error(self) -> None:
with mock.patch('certbot._internal.cli.sys.stderr'):
with pytest.raises(SystemExit):
self.parse("--max-log-backups foo".split())
with pytest.raises(SystemExit):
self.parse("--max-log-backups -42".split())
def test_max_log_backups_success(self):
def test_max_log_backups_success(self) -> None:
value = "42"
namespace = self.parse(["--max-log-backups", value])
assert namespace.max_log_backups == int(value)
def test_unchanging_defaults(self):
def test_unchanging_defaults(self) -> None:
namespace = self.parse([])
assert namespace.domains == []
assert namespace.pref_challs == []
@@ -453,29 +455,29 @@ class ParseTest(unittest.TestCase):
assert namespace.domains == []
assert namespace.pref_challs == []
def test_no_directory_hooks_set(self):
def test_no_directory_hooks_set(self) -> None:
assert not self.parse(["--no-directory-hooks"]).directory_hooks
def test_no_directory_hooks_unset(self):
def test_no_directory_hooks_unset(self) -> None:
assert self.parse([]).directory_hooks is True
def test_delete_after_revoke(self):
def test_delete_after_revoke(self) -> None:
namespace = self.parse(["--delete-after-revoke"])
assert namespace.delete_after_revoke is True
def test_delete_after_revoke_default(self):
def test_delete_after_revoke_default(self) -> None:
namespace = self.parse([])
assert namespace.delete_after_revoke is None
def test_no_delete_after_revoke(self):
def test_no_delete_after_revoke(self) -> None:
namespace = self.parse(["--no-delete-after-revoke"])
assert namespace.delete_after_revoke is False
def test_allow_subset_with_wildcard(self):
def test_allow_subset_with_wildcard(self) -> None:
with pytest.raises(errors.Error):
self.parse("--allow-subset-of-names -d *.example.org".split())
def test_route53_no_revert(self):
def test_route53_no_revert(self) -> None:
for help_flag in ['-h', '--help']:
for topic in ['all', 'plugins', 'dns-route53']:
assert 'certbot-route53:auth' not in self._help_output([help_flag, topic])
@@ -485,19 +487,19 @@ class DefaultTest(unittest.TestCase):
"""Tests for certbot._internal.cli._Default."""
def setUp(self):
def setUp(self) -> None:
# pylint: disable=protected-access
self.default1 = cli._Default()
self.default2 = cli._Default()
def test_boolean(self):
def test_boolean(self) -> None:
assert bool(self.default1) is False
assert bool(self.default2) is False
def test_equality(self):
def test_equality(self) -> None:
assert self.default1 == self.default2
def test_hash(self):
def test_hash(self) -> None:
assert hash(self.default1) == hash(self.default2)
@@ -505,20 +507,20 @@ class SetByCliTest(unittest.TestCase):
"""Tests for certbot.set_by_cli and related functions."""
def setUp(self):
def setUp(self) -> None:
reload_module(cli)
def test_deploy_hook(self):
def test_deploy_hook(self) -> None:
assert _call_set_by_cli(
'renew_hook', '--deploy-hook foo'.split(), 'renew')
def test_webroot_map(self):
def test_webroot_map(self) -> None:
args = '-w /var/www/html -d example.com'.split()
verb = 'renew'
assert _call_set_by_cli('webroot_map', args, verb) is True
def _call_set_by_cli(var, args, verb):
def _call_set_by_cli(var: str, args: List[str], verb: str) -> bool:
with mock.patch('certbot._internal.cli.helpful_parser') as mock_parser:
with test_util.patch_display_util():
mock_parser.args = args

View File

@@ -7,7 +7,7 @@ import sys
import tempfile
import unittest
from unittest import mock
from unittest.mock import MagicMock
from unittest.mock import _SentinelObject, MagicMock
from josepy import interfaces
import pytest
@@ -19,6 +19,10 @@ from certbot._internal import constants
from certbot._internal.display import obj as display_obj
from certbot.compat import os
import certbot.tests.util as test_util
from certbot._internal.account import Account
from certbot.tests.util import FreezableMock
from certbot.util import CSR, Key
from typing import List, Optional, Tuple, Union
KEY = test_util.load_vector("rsa512_key.pem")
CSR_SAN = test_util.load_vector("csr-san_512.pem")
@@ -29,19 +33,19 @@ CSR_SAN = test_util.load_vector("csr-san_512.pem")
class DetermineUserAgentTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.client.determine_user_agent."""
def _call(self):
def _call(self) -> str:
from certbot._internal.client import determine_user_agent
return determine_user_agent(self.config)
@mock.patch.dict(os.environ, {"CERTBOT_DOCS": "1"})
def test_docs_value(self):
def test_docs_value(self) -> None:
self._test(expect_doc_values=True)
@mock.patch.dict(os.environ, {})
def test_real_values(self):
def test_real_values(self) -> None:
self._test(expect_doc_values=False)
def _test(self, expect_doc_values):
def _test(self, expect_doc_values: bool) -> None:
ua = self._call()
if expect_doc_values:
@@ -60,7 +64,7 @@ class DetermineUserAgentTest(test_util.ConfigTestCase):
class RegisterTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.client.register."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.rsa_key_size = 1024
self.config.register_unsafely_without_email = False
@@ -69,7 +73,7 @@ class RegisterTest(test_util.ConfigTestCase):
self.tos_cb = mock.MagicMock()
display_obj.set_display(MagicMock())
def _call(self):
def _call(self) -> Tuple[Account, MagicMock]:
from certbot._internal.client import register
return register(self.config, self.account_storage, self.tos_cb)
@@ -84,20 +88,20 @@ class RegisterTest(test_util.ConfigTestCase):
return "/acme/new-account"
@staticmethod
def _true_mock():
def _true_mock() -> bool:
return True
@staticmethod
def _false_mock():
def _false_mock() -> bool:
return False
@staticmethod
@contextlib.contextmanager
def _patched_acme_client():
def _patched_acme_client() -> None:
with mock.patch('certbot._internal.client.acme_client') as mock_acme_client:
yield mock_acme_client.ClientV2
def test_no_tos(self):
def test_no_tos(self) -> None:
with self._patched_acme_client() as mock_client:
mock_client.new_account().terms_of_service = "http://tos"
mock_client().external_account_required.side_effect = self._false_mock
@@ -112,7 +116,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert mock_prepare.called is True
@mock.patch('certbot._internal.eff.prepare_subscription')
def test_empty_meta(self, unused_mock_prepare):
def test_empty_meta(self, unused_mock_prepare: MagicMock) -> None:
# Test that we can handle an ACME server which does not implement the 'meta'
# directory object (for terms-of-service handling).
with self._patched_acme_client() as mock_client:
@@ -125,7 +129,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert self.tos_cb.called is False
@test_util.patch_display_util()
def test_it(self, unused_mock_get_utility):
def test_it(self, unused_mock_get_utility: FreezableMock) -> None:
with self._patched_acme_client() as mock_client:
mock_client().external_account_required.side_effect = self._false_mock
with mock.patch("certbot._internal.eff.handle_subscription"):
@@ -133,7 +137,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert self.tos_cb.called is True
@mock.patch("certbot._internal.client.display_ops.get_email")
def test_email_retry(self, mock_get_email):
def test_email_retry(self, mock_get_email: MagicMock) -> None:
from acme import messages
self.config.noninteractive_mode = False
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
@@ -146,7 +150,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert mock_get_email.call_count == 1
assert mock_prepare.called is True
def test_email_invalid_noninteractive(self):
def test_email_invalid_noninteractive(self) -> None:
from acme import messages
self.config.noninteractive_mode = True
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
@@ -158,13 +162,13 @@ class RegisterTest(test_util.ConfigTestCase):
with pytest.raises(errors.Error):
self._call()
def test_needs_email(self):
def test_needs_email(self) -> None:
self.config.email = None
with pytest.raises(errors.Error):
self._call()
@mock.patch("certbot._internal.client.logger")
def test_without_email(self, mock_logger):
def test_without_email(self, mock_logger: MagicMock) -> None:
with mock.patch("certbot._internal.eff.prepare_subscription") as mock_prepare:
with self._patched_acme_client() as mock_client:
mock_client().external_account_required.side_effect = self._false_mock
@@ -176,7 +180,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert mock_prepare.called is True
@mock.patch("certbot._internal.client.display_ops.get_email")
def test_dry_run_no_staging_account(self, mock_get_email):
def test_dry_run_no_staging_account(self, mock_get_email: MagicMock) -> None:
"""Tests dry-run for no staging account, expect account created with no email"""
with self._patched_acme_client() as mock_client:
mock_client().external_account_required.side_effect = self._false_mock
@@ -189,7 +193,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert not mock_client().new_account.call_args[0][0].contact
@test_util.patch_display_util()
def test_with_eab_arguments(self, unused_mock_get_utility):
def test_with_eab_arguments(self, unused_mock_get_utility: FreezableMock) -> None:
with self._patched_acme_client() as mock_client:
mock_client().client.directory.__getitem__ = mock.Mock(
side_effect=self._new_acct_dir_mock
@@ -205,7 +209,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert mock_eab_from_data.called is True
@test_util.patch_display_util()
def test_without_eab_arguments(self, unused_mock_get_utility):
def test_without_eab_arguments(self, unused_mock_get_utility: FreezableMock) -> None:
with self._patched_acme_client() as mock_client:
mock_client().external_account_required.side_effect = self._false_mock
with mock.patch("certbot._internal.eff.handle_subscription"):
@@ -217,7 +221,7 @@ class RegisterTest(test_util.ConfigTestCase):
assert mock_eab_from_data.called is False
def test_external_account_required_without_eab_arguments(self):
def test_external_account_required_without_eab_arguments(self) -> None:
with self._patched_acme_client() as mock_client:
mock_client().client.net.key.public_key = mock.Mock(side_effect=self._public_key_mock)
mock_client().external_account_required.side_effect = self._true_mock
@@ -229,7 +233,7 @@ class RegisterTest(test_util.ConfigTestCase):
with pytest.raises(errors.Error):
self._call()
def test_unsupported_error(self):
def test_unsupported_error(self) -> None:
from acme import messages
msg = "Test"
mx_err = messages.Error.with_code("malformed", detail=msg, title="title")
@@ -248,7 +252,7 @@ class RegisterTest(test_util.ConfigTestCase):
class ClientTestCommon(test_util.ConfigTestCase):
"""Common base class for certbot._internal.client.Client tests."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.no_verify_ssl = False
self.config.allow_subset_of_names = False
@@ -268,7 +272,7 @@ class ClientTestCommon(test_util.ConfigTestCase):
class ClientTest(ClientTestCommon):
"""Tests for certbot._internal.client.Client."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.allow_subset_of_names = False
@@ -279,10 +283,10 @@ class ClientTest(ClientTestCommon):
authorizations=[None],
csr_pem=mock.sentinel.csr_pem)
def test_init_acme_verify_ssl(self):
def test_init_acme_verify_ssl(self) -> None:
assert self.client_network.call_args[1]['verify_ssl'] is True
def _mock_obtain_certificate(self):
def _mock_obtain_certificate(self) -> None:
self.client.auth_handler = mock.MagicMock()
self.client.auth_handler.handle_authorizations.return_value = [None]
self.client.auth_handler.deactivate_valid_authorizations.return_value = ([], [])
@@ -290,7 +294,7 @@ class ClientTest(ClientTestCommon):
self.acme.new_order.return_value = self.eg_order
self.eg_order.update.return_value = self.eg_order
def _check_obtain_certificate(self, auth_count=1):
def _check_obtain_certificate(self, auth_count: int=1) -> None:
if auth_count == 1:
self.client.auth_handler.handle_authorizations.assert_called_once_with(
self.eg_order,
@@ -305,7 +309,7 @@ class ClientTest(ClientTestCommon):
@mock.patch("certbot._internal.client.crypto_util")
@mock.patch("certbot._internal.client.logger")
def test_obtain_certificate_from_csr(self, mock_logger, mock_crypto_util):
def test_obtain_certificate_from_csr(self, mock_logger: MagicMock, mock_crypto_util: MagicMock) -> None:
self._mock_obtain_certificate()
test_csr = util.CSR(form="pem", file=None, data=CSR_SAN)
auth_handler = self.client.auth_handler
@@ -362,7 +366,7 @@ class ClientTest(ClientTestCommon):
mock_logger.error.assert_called_once_with(mock.ANY)
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate(self, mock_crypto_util):
def test_obtain_certificate(self, mock_crypto_util: MagicMock) -> None:
csr = util.CSR(form="pem", file=None, data=CSR_SAN)
mock_crypto_util.generate_csr.return_value = csr
mock_crypto_util.generate_key.return_value = mock.sentinel.key
@@ -383,7 +387,7 @@ class ClientTest(ClientTestCommon):
self.eg_order.fullchain_pem)
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_partial_success(self, mock_crypto_util):
def test_obtain_certificate_partial_success(self, mock_crypto_util: MagicMock) -> None:
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
mock_crypto_util.generate_csr.return_value = csr
@@ -399,7 +403,7 @@ class ClientTest(ClientTestCommon):
assert mock_crypto_util.cert_and_chain_from_fullchain.call_count == 1
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_finalize_order_partial_success(self, mock_crypto_util):
def test_obtain_certificate_finalize_order_partial_success(self, mock_crypto_util: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
@@ -435,7 +439,7 @@ class ClientTest(ClientTestCommon):
assert mock_crypto_util.cert_and_chain_from_fullchain.call_count == 1
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_finalize_order_no_retryable_domains(self, mock_crypto_util):
def test_obtain_certificate_finalize_order_no_retryable_domains(self, mock_crypto_util: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
@@ -465,7 +469,7 @@ class ClientTest(ClientTestCommon):
assert mock_crypto_util.cert_and_chain_from_fullchain.call_count == 0
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_finalize_order_rejected_identifier_no_subproblems(self, mock_crypto_util):
def test_obtain_certificate_finalize_order_rejected_identifier_no_subproblems(self, mock_crypto_util: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
@@ -491,7 +495,7 @@ class ClientTest(ClientTestCommon):
assert mock_crypto_util.cert_and_chain_from_fullchain.call_count == 0
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_get_order_partial_success(self, mock_crypto_util):
def test_obtain_certificate_get_order_partial_success(self, mock_crypto_util: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
@@ -527,7 +531,7 @@ class ClientTest(ClientTestCommon):
assert mock_crypto_util.cert_and_chain_from_fullchain.call_count == 1
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_get_order_no_retryable_domains(self, mock_crypto_util):
def test_obtain_certificate_get_order_no_retryable_domains(self, mock_crypto_util: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
@@ -557,7 +561,7 @@ class ClientTest(ClientTestCommon):
assert mock_crypto_util.cert_and_chain_from_fullchain.call_count == 0
@mock.patch("certbot._internal.client.crypto_util")
def test_obtain_certificate_get_order_rejected_identifier_no_subproblems(self, mock_crypto_util):
def test_obtain_certificate_get_order_rejected_identifier_no_subproblems(self, mock_crypto_util: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
@@ -584,7 +588,7 @@ class ClientTest(ClientTestCommon):
@mock.patch("certbot._internal.client.crypto_util")
@mock.patch("certbot._internal.client.acme_crypto_util")
def test_obtain_certificate_dry_run(self, mock_acme_crypto, mock_crypto):
def test_obtain_certificate_dry_run(self, mock_acme_crypto: MagicMock, mock_crypto: MagicMock) -> None:
csr = util.CSR(form="pem", file=None, data=CSR_SAN)
mock_acme_crypto.make_csr.return_value = CSR_SAN
mock_crypto.make_key.return_value = mock.sentinel.key_pem
@@ -608,8 +612,8 @@ class ClientTest(ClientTestCommon):
@mock.patch("certbot._internal.client.logger")
@mock.patch("certbot._internal.client.crypto_util")
@mock.patch("certbot._internal.client.acme_crypto_util")
def test_obtain_certificate_dry_run_authz_deactivations_failed(self, mock_acme_crypto,
mock_crypto, mock_log):
def test_obtain_certificate_dry_run_authz_deactivations_failed(self, mock_acme_crypto: MagicMock,
mock_crypto: MagicMock, mock_log: MagicMock) -> None:
from acme import messages
csr = util.CSR(form="pem", file=None, data=CSR_SAN)
mock_acme_crypto.make_csr.return_value = CSR_SAN
@@ -646,14 +650,14 @@ class ClientTest(ClientTestCommon):
" every domain. The dry run will continue, but results"
" may not be accurate.")
def _set_mock_from_fullchain(self, mock_from_fullchain):
def _set_mock_from_fullchain(self, mock_from_fullchain: MagicMock) -> None:
mock_cert = mock.Mock()
mock_cert.encode.return_value = mock.sentinel.cert
mock_chain = mock.Mock()
mock_chain.encode.return_value = mock.sentinel.chain
mock_from_fullchain.return_value = (mock_cert, mock_chain)
def _authzr_from_domains(self, domains):
def _authzr_from_domains(self, domains: List[str]) -> List[MagicMock]:
authzr = []
# domain ordering should not be affected by authorization order
@@ -665,7 +669,7 @@ class ClientTest(ClientTestCommon):
value=domain))))
return authzr
def _test_obtain_certificate_common(self, key, csr, authzr_ret=None, auth_count=1):
def _test_obtain_certificate_common(self, key: Union[Key, _SentinelObject, CSR], csr: CSR, authzr_ret: Optional[List[MagicMock]]=None, auth_count: int=1) -> None:
self._mock_obtain_certificate()
# return_value is essentially set to (None, None) in
@@ -686,7 +690,7 @@ class ClientTest(ClientTestCommon):
@mock.patch('certbot._internal.client.Client.obtain_certificate')
@mock.patch('certbot._internal.storage.RenewableCert.new_lineage')
def test_obtain_and_enroll_certificate(self,
mock_storage, mock_obtain_certificate):
mock_storage: MagicMock, mock_obtain_certificate: MagicMock) -> None:
domains = ["*.example.com", "example.com"]
mock_obtain_certificate.return_value = (mock.MagicMock(),
mock.MagicMock(), mock.MagicMock(), None)
@@ -705,7 +709,7 @@ class ClientTest(ClientTestCommon):
assert names == ["example_cert", "example.com", "example.com"]
@mock.patch("certbot._internal.cli.helpful_parser")
def test_save_certificate(self, mock_parser):
def test_save_certificate(self, mock_parser: MagicMock) -> None:
certs = ["cert_512.pem", "cert-san_512.pem"]
tmp_path = tempfile.mkdtemp()
@@ -742,7 +746,7 @@ class ClientTest(ClientTestCommon):
shutil.rmtree(tmp_path)
@test_util.patch_display_util()
def test_deploy_certificate_success(self, mock_util):
def test_deploy_certificate_success(self, mock_util: FreezableMock) -> None:
with pytest.raises(errors.Error):
self.client.deploy_certificate(["foo.bar"], "key", "cert", "chain", "fullchain")
@@ -761,7 +765,7 @@ class ClientTest(ClientTestCommon):
@mock.patch('certbot._internal.client.display_util.notify')
@test_util.patch_display_util()
def test_deploy_certificate_failure(self, mock_util, mock_notify):
def test_deploy_certificate_failure(self, mock_util: FreezableMock, mock_notify: MagicMock) -> None:
installer = mock.MagicMock()
self.client.installer = installer
self.config.installer = "foobar"
@@ -775,7 +779,7 @@ class ClientTest(ClientTestCommon):
@test_util.patch_display_util()
def test_deploy_certificate_save_failure(self, mock_util):
def test_deploy_certificate_save_failure(self, mock_util: FreezableMock) -> None:
installer = mock.MagicMock()
self.client.installer = installer
@@ -786,7 +790,7 @@ class ClientTest(ClientTestCommon):
@mock.patch('certbot._internal.client.display_util.notify')
@test_util.patch_display_util()
def test_deploy_certificate_restart_failure(self, mock_get_utility, mock_notify):
def test_deploy_certificate_restart_failure(self, mock_get_utility: FreezableMock, mock_notify: MagicMock) -> None:
installer = mock.MagicMock()
installer.restart.side_effect = [errors.PluginError, None]
self.client.installer = installer
@@ -801,7 +805,7 @@ class ClientTest(ClientTestCommon):
@mock.patch('certbot._internal.client.logger')
@test_util.patch_display_util()
def test_deploy_certificate_restart_failure2(self, mock_get_utility, mock_logger):
def test_deploy_certificate_restart_failure2(self, mock_get_utility: FreezableMock, mock_logger: MagicMock) -> None:
installer = mock.MagicMock()
installer.restart.side_effect = errors.PluginError
installer.rollback_checkpoints.side_effect = errors.ReverterError
@@ -819,7 +823,7 @@ class ClientTest(ClientTestCommon):
class EnhanceConfigTest(ClientTestCommon):
"""Tests for certbot._internal.client.Client.enhance_config."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.hsts = False
@@ -828,11 +832,11 @@ class EnhanceConfigTest(ClientTestCommon):
self.config.uir = False
self.domain = "example.org"
def test_no_installer(self):
def test_no_installer(self) -> None:
with pytest.raises(errors.Error):
self.client.enhance_config([self.domain], None)
def test_unsupported(self):
def test_unsupported(self) -> None:
self.client.installer = mock.MagicMock()
self.client.installer.supported_enhancements.return_value = []
@@ -844,7 +848,7 @@ class EnhanceConfigTest(ClientTestCommon):
self.client.installer.enhance.assert_not_called()
@mock.patch("certbot._internal.client.logger")
def test_already_exists_header(self, mock_log):
def test_already_exists_header(self, mock_log: MagicMock) -> None:
self.config.hsts = True
self._test_with_already_existing()
assert mock_log.info.called is True
@@ -852,7 +856,7 @@ class EnhanceConfigTest(ClientTestCommon):
'Strict-Transport-Security'
@mock.patch("certbot._internal.client.logger")
def test_already_exists_redirect(self, mock_log):
def test_already_exists_redirect(self, mock_log: MagicMock) -> None:
self.config.redirect = True
self._test_with_already_existing()
assert mock_log.info.called is True
@@ -860,71 +864,71 @@ class EnhanceConfigTest(ClientTestCommon):
'redirect'
@mock.patch("certbot._internal.client.logger")
def test_config_set_no_warning_redirect(self, mock_log):
def test_config_set_no_warning_redirect(self, mock_log: MagicMock) -> None:
self.config.redirect = False
self._test_with_already_existing()
assert mock_log.warning.called is False
@mock.patch("certbot._internal.client.logger")
def test_no_warn_redirect(self, mock_log):
def test_no_warn_redirect(self, mock_log: MagicMock) -> None:
self.config.redirect = None
self._test_with_all_supported()
assert mock_log.warning.called is False
def test_no_ask_hsts(self):
def test_no_ask_hsts(self) -> None:
self.config.hsts = True
self._test_with_all_supported()
self.client.installer.enhance.assert_called_with(
self.domain, "ensure-http-header", "Strict-Transport-Security")
def test_no_ask_redirect(self):
def test_no_ask_redirect(self) -> None:
self.config.redirect = True
self._test_with_all_supported()
self.client.installer.enhance.assert_called_with(
self.domain, "redirect", None)
def test_no_ask_staple(self):
def test_no_ask_staple(self) -> None:
self.config.staple = True
self._test_with_all_supported()
self.client.installer.enhance.assert_called_with(
self.domain, "staple-ocsp", None)
def test_no_ask_uir(self):
def test_no_ask_uir(self) -> None:
self.config.uir = True
self._test_with_all_supported()
self.client.installer.enhance.assert_called_with(
self.domain, "ensure-http-header", "Upgrade-Insecure-Requests")
def test_enhance_failure(self):
def test_enhance_failure(self) -> None:
self.client.installer = mock.MagicMock()
self.client.installer.enhance.side_effect = errors.PluginError
self._test_error(enhance_error=True)
self.client.installer.recovery_routine.assert_called_once_with()
def test_save_failure(self):
def test_save_failure(self) -> None:
self.client.installer = mock.MagicMock()
self.client.installer.save.side_effect = errors.PluginError
self._test_error()
self.client.installer.recovery_routine.assert_called_once_with()
self.client.installer.save.assert_called_once_with(mock.ANY)
def test_restart_failure(self):
def test_restart_failure(self) -> None:
self.client.installer = mock.MagicMock()
self.client.installer.restart.side_effect = [errors.PluginError, None]
self._test_error_with_rollback()
def test_restart_failure2(self):
def test_restart_failure2(self) -> None:
installer = mock.MagicMock()
installer.restart.side_effect = errors.PluginError
installer.rollback_checkpoints.side_effect = errors.ReverterError
self.client.installer = installer
self._test_error_with_rollback()
def _test_error_with_rollback(self):
def _test_error_with_rollback(self) -> None:
self._test_error()
assert self.client.installer.restart.called is True
def _test_error(self, enhance_error=False, restart_error=False):
def _test_error(self, enhance_error: bool=False, restart_error: bool=False) -> None:
self.config.redirect = True
with mock.patch('certbot._internal.client.logger') as mock_logger, \
test_util.patch_display_util() as mock_gu:
@@ -938,7 +942,7 @@ class EnhanceConfigTest(ClientTestCommon):
mock_logger.critical.assert_called_with(
'Rolling back to previous server configuration...')
def _test_with_all_supported(self):
def _test_with_all_supported(self) -> None:
if self.client.installer is None:
self.client.installer = mock.MagicMock()
self.client.installer.supported_enhancements.return_value = [
@@ -947,7 +951,7 @@ class EnhanceConfigTest(ClientTestCommon):
assert self.client.installer.save.call_count == 1
assert self.client.installer.restart.call_count == 1
def _test_with_already_existing(self):
def _test_with_already_existing(self) -> None:
self.client.installer = mock.MagicMock()
self.client.installer.supported_enhancements.return_value = [
"ensure-http-header", "redirect", "staple-ocsp"]
@@ -958,22 +962,22 @@ class EnhanceConfigTest(ClientTestCommon):
class RollbackTest(unittest.TestCase):
"""Tests for certbot._internal.client.rollback."""
def setUp(self):
def setUp(self) -> None:
self.m_install = mock.MagicMock()
@classmethod
def _call(cls, checkpoints, side_effect):
def _call(cls, checkpoints: int, side_effect: Optional[MagicMock]) -> None:
from certbot._internal.client import rollback
with mock.patch("certbot._internal.client.plugin_selection.pick_installer") as mpi:
mpi.side_effect = side_effect
rollback(None, checkpoints, {}, mock.MagicMock())
def test_no_problems(self):
def test_no_problems(self) -> None:
self._call(1, self.m_install)
assert self.m_install().rollback_checkpoints.call_count == 1
assert self.m_install().restart.call_count == 1
def test_no_installer(self):
def test_no_installer(self) -> None:
self._call(1, None) # Just make sure no exceptions are raised

View File

@@ -13,6 +13,7 @@ from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
from certbot.tests.util import TempDirTestCase
from unittest.mock import MagicMock
try:
import ntsecuritycon
@@ -32,11 +33,11 @@ ADMINS_SID = 'S-1-5-32-544'
@unittest.skipIf(POSIX_MODE, reason='Tests specific to Windows security')
class WindowsChmodTests(TempDirTestCase):
"""Unit tests for Windows chmod function in filesystem module"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.probe_path = _create_probe(self.tempdir)
def test_symlink_resolution(self):
def test_symlink_resolution(self) -> None:
link_path = os.path.join(self.tempdir, 'link')
os.symlink(self.probe_path, link_path)
@@ -51,7 +52,7 @@ class WindowsChmodTests(TempDirTestCase):
assert not filesystem._compare_dacls(ref_dacl_probe, cur_dacl_probe) # pylint: disable=protected-access
assert filesystem._compare_dacls(ref_dacl_link, cur_dacl_link) # pylint: disable=protected-access
def test_world_permission(self):
def test_world_permission(self) -> None:
everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID)
filesystem.chmod(self.probe_path, 0o700)
@@ -66,7 +67,7 @@ class WindowsChmodTests(TempDirTestCase):
assert [dacl.GetAce(index) for index in range(0, dacl.GetAceCount())
if dacl.GetAce(index)[2] == everybody]
def test_group_permissions_noop(self):
def test_group_permissions_noop(self) -> None:
filesystem.chmod(self.probe_path, 0o700)
ref_dacl_probe = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl()
@@ -75,7 +76,7 @@ class WindowsChmodTests(TempDirTestCase):
assert filesystem._compare_dacls(ref_dacl_probe, cur_dacl_probe) # pylint: disable=protected-access
def test_admin_permissions(self):
def test_admin_permissions(self) -> None:
system = win32security.ConvertStringSidToSid(SYSTEM_SID)
admins = win32security.ConvertStringSidToSid(ADMINS_SID)
@@ -93,21 +94,21 @@ class WindowsChmodTests(TempDirTestCase):
assert system_aces[0][1] == ntsecuritycon.FILE_ALL_ACCESS
assert admin_aces[0][1] == ntsecuritycon.FILE_ALL_ACCESS
def test_read_flag(self):
def test_read_flag(self) -> None:
self._test_flag(4, ntsecuritycon.FILE_GENERIC_READ)
def test_execute_flag(self):
def test_execute_flag(self) -> None:
self._test_flag(1, ntsecuritycon.FILE_GENERIC_EXECUTE)
def test_write_flag(self):
def test_write_flag(self) -> None:
self._test_flag(2, (ntsecuritycon.FILE_ALL_ACCESS
^ ntsecuritycon.FILE_GENERIC_READ
^ ntsecuritycon.FILE_GENERIC_EXECUTE))
def test_full_flag(self):
def test_full_flag(self) -> None:
self._test_flag(7, ntsecuritycon.FILE_ALL_ACCESS)
def _test_flag(self, everyone_mode, windows_flag):
def _test_flag(self, everyone_mode: int, windows_flag: int) -> None:
# Note that flag is tested against `everyone`, not `user`, because practically these unit
# tests are executed with admin privilege, so current user is effectively the admins group,
# and so will always have all rights.
@@ -124,7 +125,7 @@ class WindowsChmodTests(TempDirTestCase):
assert acls_everybody[1] == windows_flag
def test_user_admin_dacl_consistency(self):
def test_user_admin_dacl_consistency(self) -> None:
# Set ownership of target to authenticated user
authenticated_user, _, _ = win32security.LookupAccountName("", win32api.GetUserName())
security_owner = _get_security_owner(self.probe_path)
@@ -150,7 +151,7 @@ class WindowsChmodTests(TempDirTestCase):
class UmaskTest(TempDirTestCase):
def test_umask_on_dir(self):
def test_umask_on_dir(self) -> None:
previous_umask = filesystem.umask(0o022)
try:
@@ -170,7 +171,7 @@ class UmaskTest(TempDirTestCase):
finally:
filesystem.umask(previous_umask)
def test_umask_on_file(self):
def test_umask_on_file(self) -> None:
previous_umask = filesystem.umask(0o022)
try:
@@ -191,7 +192,7 @@ class UmaskTest(TempDirTestCase):
filesystem.umask(previous_umask)
@staticmethod
def _create_file(path, mode=0o777):
def _create_file(path: str, mode: int=0o777) -> None:
file_desc = None
try:
file_desc = filesystem.open(path, flags=os.O_CREAT, mode=mode)
@@ -201,11 +202,11 @@ class UmaskTest(TempDirTestCase):
class ComputePrivateKeyModeTest(TempDirTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.probe_path = _create_probe(self.tempdir)
def test_compute_private_key_mode(self):
def test_compute_private_key_mode(self) -> None:
filesystem.chmod(self.probe_path, 0o777)
new_mode = filesystem.compute_private_key_mode(self.probe_path, 0o600)
@@ -220,7 +221,7 @@ class ComputePrivateKeyModeTest(TempDirTestCase):
@unittest.skipIf(POSIX_MODE, reason='Tests specific to Windows security')
class WindowsOpenTest(TempDirTestCase):
def test_new_file_correct_permissions(self):
def test_new_file_correct_permissions(self) -> None:
path = os.path.join(self.tempdir, 'file')
desc = filesystem.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o700)
@@ -232,7 +233,7 @@ class WindowsOpenTest(TempDirTestCase):
assert not [dacl.GetAce(index) for index in range(0, dacl.GetAceCount())
if dacl.GetAce(index)[2] == everybody]
def test_existing_file_correct_permissions(self):
def test_existing_file_correct_permissions(self) -> None:
path = os.path.join(self.tempdir, 'file')
open(path, 'w').close()
@@ -245,7 +246,7 @@ class WindowsOpenTest(TempDirTestCase):
assert not [dacl.GetAce(index) for index in range(0, dacl.GetAceCount())
if dacl.GetAce(index)[2] == everybody]
def test_create_file_on_open(self):
def test_create_file_on_open(self) -> None:
# os.O_CREAT | os.O_EXCL + file not exists = OK
self._test_one_creation(1, file_exist=False, flags=(os.O_CREAT | os.O_EXCL))
@@ -275,7 +276,7 @@ class WindowsOpenTest(TempDirTestCase):
with pytest.raises(OSError):
self._test_one_creation(6, file_exist=False, flags=os.O_RDONLY)
def _test_one_creation(self, num, file_exist, flags):
def _test_one_creation(self, num: int, file_exist: bool, flags: int) -> None:
one_file = os.path.join(self.tempdir, str(num))
if file_exist and not os.path.exists(one_file):
with open(one_file, 'w'):
@@ -291,19 +292,19 @@ class WindowsOpenTest(TempDirTestCase):
class TempUmaskTests(test_util.TempDirTestCase):
"""Tests for using the TempUmask class in `with` statements"""
def _check_umask(self):
def _check_umask(self) -> int:
old_umask = filesystem.umask(0)
filesystem.umask(old_umask)
return old_umask
def test_works_normally(self):
def test_works_normally(self) -> None:
filesystem.umask(0o0022)
assert self._check_umask() == 0o0022
with filesystem.temp_umask(0o0077):
assert self._check_umask() == 0o0077
assert self._check_umask() == 0o0022
def test_resets_umask_after_exception(self):
def test_resets_umask_after_exception(self) -> None:
filesystem.umask(0o0022)
assert self._check_umask() == 0o0022
try:
@@ -317,7 +318,7 @@ class TempUmaskTests(test_util.TempDirTestCase):
@unittest.skipIf(POSIX_MODE, reason='Test specific to Windows security')
class WindowsMkdirTests(test_util.TempDirTestCase):
"""Unit tests for Windows mkdir + makedirs functions in filesystem module"""
def test_mkdir_correct_permissions(self):
def test_mkdir_correct_permissions(self) -> None:
path = os.path.join(self.tempdir, 'dir')
filesystem.mkdir(path, 0o700)
@@ -328,7 +329,7 @@ class WindowsMkdirTests(test_util.TempDirTestCase):
assert not [dacl.GetAce(index) for index in range(0, dacl.GetAceCount())
if dacl.GetAce(index)[2] == everybody]
def test_makedirs_correct_permissions(self):
def test_makedirs_correct_permissions(self) -> None:
path = os.path.join(self.tempdir, 'dir')
subpath = os.path.join(path, 'subpath')
@@ -340,7 +341,7 @@ class WindowsMkdirTests(test_util.TempDirTestCase):
assert not [dacl.GetAce(index) for index in range(0, dacl.GetAceCount())
if dacl.GetAce(index)[2] == everybody]
def test_makedirs_switch_os_mkdir(self):
def test_makedirs_switch_os_mkdir(self) -> None:
path = os.path.join(self.tempdir, 'dir')
import os as std_os # pylint: disable=os-module-forbidden
original_mkdir = std_os.mkdir
@@ -357,7 +358,7 @@ class WindowsMkdirTests(test_util.TempDirTestCase):
class MakedirsTests(test_util.TempDirTestCase):
"""Unit tests for makedirs function in filesystem module"""
def test_makedirs_correct_permissions(self):
def test_makedirs_correct_permissions(self) -> None:
path = os.path.join(self.tempdir, 'dir')
subpath = os.path.join(path, 'subpath')
@@ -374,12 +375,12 @@ class MakedirsTests(test_util.TempDirTestCase):
class CopyOwnershipAndModeTest(test_util.TempDirTestCase):
"""Tests about copy_ownership_and_apply_mode, copy_ownership_and_mode and has_same_ownership"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.probe_path = _create_probe(self.tempdir)
@unittest.skipIf(POSIX_MODE, reason='Test specific to Windows security')
def test_copy_ownership_and_apply_mode_windows(self):
def test_copy_ownership_and_apply_mode_windows(self) -> None:
system = win32security.ConvertStringSidToSid(SYSTEM_SID)
security = win32security.SECURITY_ATTRIBUTES().SECURITY_DESCRIPTOR
security.SetSecurityDescriptorOwner(system, False)
@@ -405,7 +406,7 @@ class CopyOwnershipAndModeTest(test_util.TempDirTestCase):
if dacl.GetAce(index)[2] == everybody]
@unittest.skipUnless(POSIX_MODE, reason='Test specific to Linux security')
def test_copy_ownership_and_apply_mode_linux(self):
def test_copy_ownership_and_apply_mode_linux(self) -> None:
with mock.patch('os.chown') as mock_chown:
with mock.patch('os.chmod') as mock_chmod:
with mock.patch('os.stat') as mock_stat:
@@ -417,7 +418,7 @@ class CopyOwnershipAndModeTest(test_util.TempDirTestCase):
mock_chown.assert_called_once_with(self.probe_path, 50, 51)
mock_chmod.assert_called_once_with(self.probe_path, 0o700)
def test_has_same_ownership(self):
def test_has_same_ownership(self) -> None:
path1 = os.path.join(self.tempdir, 'test1')
path2 = os.path.join(self.tempdir, 'test2')
@@ -427,7 +428,7 @@ class CopyOwnershipAndModeTest(test_util.TempDirTestCase):
assert filesystem.has_same_ownership(path1, path2) is True
@unittest.skipIf(POSIX_MODE, reason='Test specific to Windows security')
def test_copy_ownership_and_mode_windows(self):
def test_copy_ownership_and_mode_windows(self) -> None:
src = self.probe_path
dst = _create_probe(self.tempdir, name='dst')
@@ -447,18 +448,18 @@ class CopyOwnershipAndModeTest(test_util.TempDirTestCase):
class CheckPermissionsTest(test_util.TempDirTestCase):
"""Tests relative to functions that check modes."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.probe_path = _create_probe(self.tempdir)
def test_check_mode(self):
def test_check_mode(self) -> None:
assert filesystem.check_mode(self.probe_path, 0o744) is True
filesystem.chmod(self.probe_path, 0o700)
assert not filesystem.check_mode(self.probe_path, 0o744)
@unittest.skipIf(POSIX_MODE, reason='Test specific to Windows security')
def test_check_owner_windows(self):
def test_check_owner_windows(self) -> None:
assert filesystem.check_owner(self.probe_path) is True
system = win32security.ConvertStringSidToSid(SYSTEM_SID)
@@ -470,7 +471,7 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
assert not filesystem.check_owner(self.probe_path)
@unittest.skipUnless(POSIX_MODE, reason='Test specific to Linux security')
def test_check_owner_linux(self):
def test_check_owner_linux(self) -> None:
assert filesystem.check_owner(self.probe_path) is True
import os as std_os # pylint: disable=os-module-forbidden
@@ -483,7 +484,7 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
mock_uid.return_value = uid + 1
assert not filesystem.check_owner(self.probe_path)
def test_check_permissions(self):
def test_check_permissions(self) -> None:
assert filesystem.check_permissions(self.probe_path, 0o744) is True
with mock.patch('certbot.compat.filesystem.check_mode') as mock_mode:
@@ -494,7 +495,7 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
mock_owner.return_value = False
assert not filesystem.check_permissions(self.probe_path, 0o744)
def test_check_min_permissions(self):
def test_check_min_permissions(self) -> None:
filesystem.chmod(self.probe_path, 0o744)
assert filesystem.has_min_permissions(self.probe_path, 0o744) is True
@@ -504,7 +505,7 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
filesystem.chmod(self.probe_path, 0o741)
assert not filesystem.has_min_permissions(self.probe_path, 0o744)
def test_is_world_reachable(self):
def test_is_world_reachable(self) -> None:
filesystem.chmod(self.probe_path, 0o744)
assert filesystem.has_world_permissions(self.probe_path) is True
@@ -514,7 +515,7 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
class OsReplaceTest(test_util.TempDirTestCase):
"""Test to ensure consistent behavior of rename method"""
def test_os_replace_to_existing_file(self):
def test_os_replace_to_existing_file(self) -> None:
"""Ensure that replace will effectively rename src into dst for all platforms."""
src = os.path.join(self.tempdir, 'src')
dst = os.path.join(self.tempdir, 'dst')
@@ -530,11 +531,11 @@ class OsReplaceTest(test_util.TempDirTestCase):
class RealpathTest(test_util.TempDirTestCase):
"""Tests for realpath method"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.probe_path = _create_probe(self.tempdir)
def test_symlink_resolution(self):
def test_symlink_resolution(self) -> None:
# Remove any symlinks already in probe_path
self.probe_path = filesystem.realpath(self.probe_path)
# Absolute resolution
@@ -557,7 +558,7 @@ class RealpathTest(test_util.TempDirTestCase):
finally:
os.chdir(curdir)
def test_symlink_loop_mitigation(self):
def test_symlink_loop_mitigation(self) -> None:
link1_path = os.path.join(self.tempdir, 'link1')
link2_path = os.path.join(self.tempdir, 'link2')
link3_path = os.path.join(self.tempdir, 'link3')
@@ -571,7 +572,7 @@ class RealpathTest(test_util.TempDirTestCase):
class IsExecutableTest(test_util.TempDirTestCase):
"""Tests for is_executable method"""
def test_not_executable(self):
def test_not_executable(self) -> None:
file_path = os.path.join(self.tempdir, "foo")
# On Windows a file created within Certbot will always have all permissions to the
@@ -598,7 +599,7 @@ class IsExecutableTest(test_util.TempDirTestCase):
@mock.patch("certbot.compat.filesystem.os.path.isfile")
@mock.patch("certbot.compat.filesystem.os.access")
def test_full_path(self, mock_access, mock_isfile):
def test_full_path(self, mock_access: MagicMock, mock_isfile: MagicMock) -> None:
with _fix_windows_runtime():
mock_access.return_value = True
mock_isfile.return_value = True
@@ -606,7 +607,7 @@ class IsExecutableTest(test_util.TempDirTestCase):
@mock.patch("certbot.compat.filesystem.os.path.isfile")
@mock.patch("certbot.compat.filesystem.os.access")
def test_rel_path(self, mock_access, mock_isfile):
def test_rel_path(self, mock_access: MagicMock, mock_isfile: MagicMock) -> None:
with _fix_windows_runtime():
mock_access.return_value = True
mock_isfile.return_value = True
@@ -614,7 +615,7 @@ class IsExecutableTest(test_util.TempDirTestCase):
@mock.patch("certbot.compat.filesystem.os.path.isfile")
@mock.patch("certbot.compat.filesystem.os.access")
def test_not_found(self, mock_access, mock_isfile):
def test_not_found(self, mock_access: MagicMock, mock_isfile: MagicMock) -> None:
with _fix_windows_runtime():
mock_access.return_value = True
mock_isfile.return_value = False
@@ -624,13 +625,13 @@ class IsExecutableTest(test_util.TempDirTestCase):
class ReadlinkTest(unittest.TestCase):
@unittest.skipUnless(POSIX_MODE, reason='Tests specific to Linux')
@mock.patch("certbot.compat.filesystem.os.readlink")
def test_path_posix(self, mock_readlink):
def test_path_posix(self, mock_readlink: MagicMock) -> None:
mock_readlink.return_value = "/normal/path"
assert filesystem.readlink("dummy") == "/normal/path"
@unittest.skipIf(POSIX_MODE, reason='Tests specific to Windows')
@mock.patch("certbot.compat.filesystem.os.readlink")
def test_normal_path_windows(self, mock_readlink):
def test_normal_path_windows(self, mock_readlink: MagicMock) -> None:
# Python <3.8
mock_readlink.return_value = "C:\\short\\path"
assert filesystem.readlink("dummy") == "C:\\short\\path"
@@ -641,14 +642,14 @@ class ReadlinkTest(unittest.TestCase):
@unittest.skipIf(POSIX_MODE, reason='Tests specific to Windows')
@mock.patch("certbot.compat.filesystem.os.readlink")
def test_extended_path_windows(self, mock_readlink):
def test_extended_path_windows(self, mock_readlink: MagicMock) -> None:
# Following path is largely over the 260 characters permitted in the normal form.
mock_readlink.return_value = "\\\\?\\C:\\long" + 1000 * "\\path"
with pytest.raises(ValueError):
filesystem.readlink("dummy")
@contextlib.contextmanager
def _fix_windows_runtime():
def _fix_windows_runtime() -> None:
if os.name != 'nt':
yield
else:
@@ -673,7 +674,7 @@ def _set_owner(target, security_owner, user):
target, win32security.OWNER_SECURITY_INFORMATION, security_owner)
def _create_probe(tempdir, name='probe'):
def _create_probe(tempdir: str, name: str='probe') -> str:
filesystem.chmod(tempdir, 0o744)
probe_path = os.path.join(tempdir, name)
util.safe_open(probe_path, 'w', chmod=0o744).close()

View File

@@ -6,7 +6,7 @@ import pytest
from certbot.compat import os
def test_forbidden_methods():
def test_forbidden_methods() -> None:
# Checks for os module
for method in ['chmod', 'chown', 'open', 'mkdir', 'makedirs', 'rename',
'replace', 'access', 'stat', 'fstat']:

View File

@@ -11,29 +11,30 @@ from certbot._internal import constants
from certbot.compat import misc
from certbot.compat import os
from certbot.tests import util as test_util
from unittest.mock import MagicMock
class NamespaceConfigTest(test_util.ConfigTestCase):
"""Tests for certbot.configuration.NamespaceConfig."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.foo = 'bar' # pylint: disable=blacklisted-name
self.config.server = 'https://acme-server.org:443/new'
self.config.https_port = 1234
self.config.http01_port = 4321
def test_init_same_ports(self):
def test_init_same_ports(self) -> None:
self.config.namespace.https_port = 4321
from certbot.configuration import NamespaceConfig
with pytest.raises(errors.Error):
NamespaceConfig(self.config.namespace)
def test_proxy_getattr(self):
def test_proxy_getattr(self) -> None:
assert self.config.foo == 'bar'
assert self.config.work_dir == os.path.join(self.tempdir, 'work')
def test_server_path(self):
def test_server_path(self) -> None:
assert ['acme-server.org:443', 'new'] == \
self.config.server_path.split(os.path.sep)
@@ -43,7 +44,7 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
self.config.server_path.split(os.path.sep)
@mock.patch('certbot.configuration.constants')
def test_dynamic_dirs(self, mock_constants):
def test_dynamic_dirs(self, mock_constants: MagicMock) -> None:
mock_constants.ACCOUNTS_DIR = 'acc'
mock_constants.BACKUP_DIR = 'backups'
mock_constants.CSR_DIR = 'csr'
@@ -69,7 +70,7 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
assert os.path.normpath(self.config.temp_checkpoint_dir) == \
os.path.normpath(os.path.join(self.config.work_dir, 't'))
def test_absolute_paths(self):
def test_absolute_paths(self) -> None:
from certbot.configuration import NamespaceConfig
config_base = "foo"
@@ -106,7 +107,7 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
assert os.path.isabs(config.temp_checkpoint_dir)
@mock.patch('certbot.configuration.constants')
def test_renewal_dynamic_dirs(self, mock_constants):
def test_renewal_dynamic_dirs(self, mock_constants: MagicMock) -> None:
mock_constants.ARCHIVE_DIR = 'a'
mock_constants.LIVE_DIR = 'l'
mock_constants.RENEWAL_CONFIGS_DIR = 'renewal_configs'
@@ -116,7 +117,7 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
assert self.config.renewal_configs_dir == os.path.join(
self.config.config_dir, 'renewal_configs')
def test_renewal_absolute_paths(self):
def test_renewal_absolute_paths(self) -> None:
from certbot.configuration import NamespaceConfig
config_base = "foo"
@@ -136,13 +137,13 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
assert os.path.isabs(config.live_dir)
assert os.path.isabs(config.renewal_configs_dir)
def test_get_and_set_attr(self):
def test_get_and_set_attr(self) -> None:
self.config.foo = 42
assert self.config.namespace.foo == 42
self.config.namespace.bar = 1337
assert self.config.bar == 1337
def test_hook_directories(self):
def test_hook_directories(self) -> None:
assert self.config.renewal_hooks_dir == \
os.path.join(self.config.config_dir,
constants.RENEWAL_HOOKS_DIR)

View File

@@ -4,5 +4,5 @@ from certbot._internal import cli
@pytest.fixture(autouse=True)
def reset_cli_global():
def reset_cli_global() -> None:
cli.set_by_cli.detector = None

View File

@@ -13,6 +13,9 @@ from certbot import util
from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
from certbot.util import CSR, Key
from typing import Any, List, Tuple, Union
from unittest.mock import MagicMock
RSA256_KEY = test_util.load_vector('rsa256_key.pem')
RSA256_KEY_PATH = test_util.vector_path('rsa256_key.pem')
@@ -33,7 +36,7 @@ CERT_ALT_ISSUER = test_util.load_vector('cert_intermediate_2.pem')
class GenerateKeyTest(test_util.TempDirTestCase):
"""Tests for certbot.crypto_util.generate_key."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.workdir = os.path.join(self.tempdir, 'workdir')
@@ -41,18 +44,18 @@ class GenerateKeyTest(test_util.TempDirTestCase):
logging.disable(logging.CRITICAL)
def tearDown(self):
def tearDown(self) -> None:
super().tearDown()
logging.disable(logging.NOTSET)
@classmethod
def _call(cls, key_size, key_dir):
def _call(cls, key_size: int, key_dir: str) -> Key:
from certbot.crypto_util import generate_key
return generate_key(key_size, key_dir, 'key-certbot.pem', strict_permissions=True)
@mock.patch('certbot.crypto_util.make_key')
def test_success(self, mock_make):
def test_success(self, mock_make: MagicMock) -> None:
mock_make.return_value = b'key_pem'
key = self._call(1024, self.workdir)
assert key.pem == b'key_pem'
@@ -60,7 +63,7 @@ class GenerateKeyTest(test_util.TempDirTestCase):
assert os.path.exists(os.path.join(self.workdir, key.file))
@mock.patch('certbot.crypto_util.make_key')
def test_key_failure(self, mock_make):
def test_key_failure(self, mock_make: MagicMock) -> None:
mock_make.side_effect = ValueError
with pytest.raises(ValueError):
self._call(431, self.workdir)
@@ -70,7 +73,7 @@ class GenerateCSRTest(test_util.TempDirTestCase):
"""Tests for certbot.crypto_util.generate_csr."""
@mock.patch('acme.crypto_util.make_csr')
@mock.patch('certbot.crypto_util.util.make_or_verify_dir')
def test_it(self, unused_mock_verify, mock_csr):
def test_it(self, unused_mock_verify: MagicMock, mock_csr: MagicMock) -> None:
from certbot.crypto_util import generate_csr
mock_csr.return_value = b'csr_pem'
@@ -86,23 +89,23 @@ class ValidCSRTest(unittest.TestCase):
"""Tests for certbot.crypto_util.valid_csr."""
@classmethod
def _call(cls, csr):
def _call(cls, csr: Union[bytes, str]) -> Union[int, bool]:
from certbot.crypto_util import valid_csr
return valid_csr(csr)
def test_valid_pem_true(self):
def test_valid_pem_true(self) -> None:
assert self._call(test_util.load_vector('csr_512.pem'))
def test_valid_pem_san_true(self):
def test_valid_pem_san_true(self) -> None:
assert self._call(test_util.load_vector('csr-san_512.pem'))
def test_valid_der_false(self):
def test_valid_der_false(self) -> None:
assert not self._call(test_util.load_vector('csr_512.der'))
def test_empty_false(self):
def test_empty_false(self) -> None:
assert not self._call('')
def test_random_false(self):
def test_random_false(self) -> None:
assert not self._call('foo bar')
@@ -110,15 +113,15 @@ class CSRMatchesPubkeyTest(unittest.TestCase):
"""Tests for certbot.crypto_util.csr_matches_pubkey."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> Union[int, bool]:
from certbot.crypto_util import csr_matches_pubkey
return csr_matches_pubkey(*args, **kwargs)
def test_valid_true(self):
def test_valid_true(self) -> None:
assert self._call(
test_util.load_vector('csr_512.pem'), RSA512_KEY)
def test_invalid_false(self):
def test_invalid_false(self) -> None:
assert not self._call(
test_util.load_vector('csr_512.pem'), RSA256_KEY)
@@ -127,11 +130,11 @@ class ImportCSRFileTest(unittest.TestCase):
"""Tests for certbot.certbot_util.import_csr_file."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> Tuple[int, CSR, List[str]]:
from certbot.crypto_util import import_csr_file
return import_csr_file(*args, **kwargs)
def test_der_csr(self):
def test_der_csr(self) -> None:
csrfile = test_util.vector_path('csr_512.der')
data = test_util.load_vector('csr_512.der')
data_pem = test_util.load_vector('csr_512.pem')
@@ -143,7 +146,7 @@ class ImportCSRFileTest(unittest.TestCase):
["Example.com"]) == \
self._call(csrfile, data)
def test_pem_csr(self):
def test_pem_csr(self) -> None:
csrfile = test_util.vector_path('csr_512.pem')
data = test_util.load_vector('csr_512.pem')
@@ -154,7 +157,7 @@ class ImportCSRFileTest(unittest.TestCase):
["Example.com"],) == \
self._call(csrfile, data)
def test_bad_csr(self):
def test_bad_csr(self) -> None:
with pytest.raises(errors.Error):
self._call(test_util.vector_path('cert_512.pem'),
test_util.load_vector('cert_512.pem'))
@@ -163,14 +166,14 @@ class ImportCSRFileTest(unittest.TestCase):
class MakeKeyTest(unittest.TestCase):
"""Tests for certbot.crypto_util.make_key."""
def test_rsa(self): # pylint: disable=no-self-use
def test_rsa(self) -> None: # pylint: disable=no-self-use
# RSA Key Type Test
from certbot.crypto_util import make_key
# Do not test larger keys as it takes too long.
OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, make_key(1024))
def test_ec(self): # pylint: disable=no-self-use
def test_ec(self) -> None: # pylint: disable=no-self-use
# ECDSA Key Type Tests
from certbot.crypto_util import make_key
@@ -181,19 +184,19 @@ class MakeKeyTest(unittest.TestCase):
)
assert pkey.bits() == bits
def test_bad_key_sizes(self):
def test_bad_key_sizes(self) -> None:
from certbot.crypto_util import make_key
# Try a bad key size for RSA and ECDSA
with pytest.raises(errors.Error, match='Unsupported RSA key length: 512'):
make_key(bits=512, key_type='rsa')
def test_bad_elliptic_curve_name(self):
def test_bad_elliptic_curve_name(self) -> None:
from certbot.crypto_util import make_key
with pytest.raises(errors.Error, match='Unsupported elliptic curve: nothere'):
make_key(elliptic_curve="nothere", key_type='ecdsa')
def test_bad_key_type(self):
def test_bad_key_type(self) -> None:
from certbot.crypto_util import make_key
# Try a bad --key-type
@@ -206,7 +209,7 @@ class MakeKeyTest(unittest.TestCase):
class VerifyCertSetup(unittest.TestCase):
"""Refactoring for verification tests."""
def setUp(self):
def setUp(self) -> None:
self.renewable_cert = mock.MagicMock()
self.renewable_cert.cert_path = SS_CERT_PATH
self.renewable_cert.chain_path = SS_CERT_PATH
@@ -222,15 +225,15 @@ class VerifyCertSetup(unittest.TestCase):
class VerifyRenewableCertTest(VerifyCertSetup):
"""Tests for certbot.crypto_util.verify_renewable_cert."""
def _call(self, renewable_cert):
def _call(self, renewable_cert: MagicMock) -> None:
from certbot.crypto_util import verify_renewable_cert
return verify_renewable_cert(renewable_cert)
def test_verify_renewable_cert(self):
def test_verify_renewable_cert(self) -> None:
assert self._call(self.renewable_cert) is None
@mock.patch('certbot.crypto_util.verify_renewable_cert_sig', side_effect=errors.Error(""))
def test_verify_renewable_cert_failure(self, unused_verify_renewable_cert_sign):
def test_verify_renewable_cert_failure(self, unused_verify_renewable_cert_sign: MagicMock) -> None:
with pytest.raises(errors.Error):
self._call(self.bad_renewable_cert)
@@ -238,21 +241,21 @@ class VerifyRenewableCertTest(VerifyCertSetup):
class VerifyRenewableCertSigTest(VerifyCertSetup):
"""Tests for certbot.crypto_util.verify_renewable_cert."""
def _call(self, renewable_cert):
def _call(self, renewable_cert: MagicMock) -> None:
from certbot.crypto_util import verify_renewable_cert_sig
return verify_renewable_cert_sig(renewable_cert)
def test_cert_sig_match(self):
def test_cert_sig_match(self) -> None:
assert self._call(self.renewable_cert) is None
def test_cert_sig_match_ec(self):
def test_cert_sig_match_ec(self) -> None:
renewable_cert = mock.MagicMock()
renewable_cert.cert_path = P256_CERT_PATH
renewable_cert.chain_path = P256_CERT_PATH
renewable_cert.key_path = P256_KEY
assert self._call(renewable_cert) is None
def test_cert_sig_mismatch(self):
def test_cert_sig_mismatch(self) -> None:
self.bad_renewable_cert.cert_path = test_util.vector_path('cert_512_bad.pem')
with pytest.raises(errors.Error):
self._call(self.bad_renewable_cert)
@@ -261,18 +264,18 @@ class VerifyRenewableCertSigTest(VerifyCertSetup):
class VerifyFullchainTest(VerifyCertSetup):
"""Tests for certbot.crypto_util.verify_fullchain."""
def _call(self, renewable_cert):
def _call(self, renewable_cert: MagicMock) -> None:
from certbot.crypto_util import verify_fullchain
return verify_fullchain(renewable_cert)
def test_fullchain_matches(self):
def test_fullchain_matches(self) -> None:
assert self._call(self.renewable_cert) is None
def test_fullchain_mismatch(self):
def test_fullchain_mismatch(self) -> None:
with pytest.raises(errors.Error):
self._call(self.bad_renewable_cert)
def test_fullchain_ioerror(self):
def test_fullchain_ioerror(self) -> None:
self.bad_renewable_cert.chain = "dog"
with pytest.raises(errors.Error):
self._call(self.bad_renewable_cert)
@@ -281,16 +284,16 @@ class VerifyFullchainTest(VerifyCertSetup):
class VerifyCertMatchesPrivKeyTest(VerifyCertSetup):
"""Tests for certbot.crypto_util.verify_cert_matches_priv_key."""
def _call(self, renewable_cert):
def _call(self, renewable_cert: MagicMock) -> None:
from certbot.crypto_util import verify_cert_matches_priv_key
return verify_cert_matches_priv_key(renewable_cert.cert, renewable_cert.privkey)
def test_cert_priv_key_match(self):
def test_cert_priv_key_match(self) -> None:
self.renewable_cert.cert = SS_CERT_PATH
self.renewable_cert.privkey = RSA2048_KEY_PATH
assert self._call(self.renewable_cert) is None
def test_cert_priv_key_mismatch(self):
def test_cert_priv_key_mismatch(self) -> None:
self.bad_renewable_cert.privkey = RSA256_KEY_PATH
self.bad_renewable_cert.cert = SS_CERT_PATH
@@ -302,17 +305,17 @@ class ValidPrivkeyTest(unittest.TestCase):
"""Tests for certbot.crypto_util.valid_privkey."""
@classmethod
def _call(cls, privkey):
def _call(cls, privkey: Union[bytes, str]) -> bool:
from certbot.crypto_util import valid_privkey
return valid_privkey(privkey)
def test_valid_true(self):
def test_valid_true(self) -> None:
assert self._call(RSA512_KEY)
def test_empty_false(self):
def test_empty_false(self) -> None:
assert not self._call('')
def test_random_false(self):
def test_random_false(self) -> None:
assert not self._call('foo bar')
@@ -320,14 +323,14 @@ class GetSANsFromCertTest(unittest.TestCase):
"""Tests for certbot.crypto_util.get_sans_from_cert."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> List[Union[str, Any]]:
from certbot.crypto_util import get_sans_from_cert
return get_sans_from_cert(*args, **kwargs)
def test_single(self):
def test_single(self) -> None:
assert [] == self._call(test_util.load_vector('cert_512.pem'))
def test_san(self):
def test_san(self) -> None:
assert ['example.com', 'www.example.com'] == \
self._call(test_util.load_vector('cert-san_512.pem'))
@@ -336,25 +339,25 @@ class GetNamesFromCertTest(unittest.TestCase):
"""Tests for certbot.crypto_util.get_names_from_cert."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> List[str]:
from certbot.crypto_util import get_names_from_cert
return get_names_from_cert(*args, **kwargs)
def test_single(self):
def test_single(self) -> None:
assert ['example.com'] == \
self._call(test_util.load_vector('cert_512.pem'))
def test_san(self):
def test_san(self) -> None:
assert ['example.com', 'www.example.com'] == \
self._call(test_util.load_vector('cert-san_512.pem'))
def test_common_name_sans_order(self):
def test_common_name_sans_order(self) -> None:
# Tests that the common name comes first
# followed by the SANS in alphabetical order
assert ['example.com'] + ['{0}.example.com'.format(c) for c in 'abcd'] == \
self._call(test_util.load_vector('cert-5sans_512.pem'))
def test_parse_non_cert(self):
def test_parse_non_cert(self) -> None:
with pytest.raises(OpenSSL.crypto.Error):
self._call("hello there")
@@ -363,24 +366,24 @@ class GetNamesFromReqTest(unittest.TestCase):
"""Tests for certbot.crypto_util.get_names_from_req."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> List[Union[str, Any]]:
from certbot.crypto_util import get_names_from_req
return get_names_from_req(*args, **kwargs)
def test_nonames(self):
def test_nonames(self) -> None:
assert [] == \
self._call(test_util.load_vector('csr-nonames_512.pem'))
def test_nosans(self):
def test_nosans(self) -> None:
assert ['example.com'] == \
self._call(test_util.load_vector('csr-nosans_512.pem'))
def test_sans(self):
def test_sans(self) -> None:
assert ['example.com', 'example.org', 'example.net', 'example.info',
'subdomain.example.com', 'other.subdomain.example.com'] == \
self._call(test_util.load_vector('csr-6sans_512.pem'))
def test_der(self):
def test_der(self) -> None:
from OpenSSL.crypto import FILETYPE_ASN1
assert ['Example.com'] == \
self._call(test_util.load_vector('csr_512.der'), typ=FILETYPE_ASN1)
@@ -389,14 +392,14 @@ class GetNamesFromReqTest(unittest.TestCase):
class CertLoaderTest(unittest.TestCase):
"""Tests for certbot.crypto_util.pyopenssl_load_certificate"""
def test_load_valid_cert(self):
def test_load_valid_cert(self) -> None:
from certbot.crypto_util import pyopenssl_load_certificate
cert, file_type = pyopenssl_load_certificate(CERT)
assert cert.digest('sha256') == \
OpenSSL.crypto.load_certificate(file_type, CERT).digest('sha256')
def test_load_invalid_cert(self):
def test_load_invalid_cert(self) -> None:
from certbot.crypto_util import pyopenssl_load_certificate
bad_cert_data = CERT.replace(b"BEGIN CERTIFICATE", b"ASDFASDFASDF!!!")
with pytest.raises(errors.Error):
@@ -406,7 +409,7 @@ class CertLoaderTest(unittest.TestCase):
class NotBeforeTest(unittest.TestCase):
"""Tests for certbot.crypto_util.notBefore"""
def test_notBefore(self):
def test_notBefore(self) -> None:
from certbot.crypto_util import notBefore
assert notBefore(CERT_PATH).isoformat() == \
'2014-12-11T22:34:45+00:00'
@@ -415,7 +418,7 @@ class NotBeforeTest(unittest.TestCase):
class NotAfterTest(unittest.TestCase):
"""Tests for certbot.crypto_util.notAfter"""
def test_notAfter(self):
def test_notAfter(self) -> None:
from certbot.crypto_util import notAfter
assert notAfter(CERT_PATH).isoformat() == \
'2014-12-18T22:34:45+00:00'
@@ -423,7 +426,7 @@ class NotAfterTest(unittest.TestCase):
class Sha256sumTest(unittest.TestCase):
"""Tests for certbot.crypto_util.notAfter"""
def test_sha256sum(self):
def test_sha256sum(self) -> None:
from certbot.crypto_util import sha256sum
assert sha256sum(CERT_PATH) == \
'914ffed8daf9e2c99d90ac95c77d54f32cbd556672facac380f0c063498df84e'
@@ -432,12 +435,12 @@ class Sha256sumTest(unittest.TestCase):
class CertAndChainFromFullchainTest(unittest.TestCase):
"""Tests for certbot.crypto_util.cert_and_chain_from_fullchain"""
def _parse_and_reencode_pem(self, cert_pem):
def _parse_and_reencode_pem(self, cert_pem: str) -> str:
from OpenSSL import crypto
return crypto.dump_certificate(crypto.FILETYPE_PEM,
crypto.load_certificate(crypto.FILETYPE_PEM, cert_pem)).decode()
def test_cert_and_chain_from_fullchain(self):
def test_cert_and_chain_from_fullchain(self) -> None:
cert_pem = CERT.decode()
chain_pem = cert_pem + SS_CERT.decode()
fullchain_pem = cert_pem + chain_pem
@@ -465,22 +468,22 @@ class FindChainWithIssuerTest(unittest.TestCase):
"""Tests for certbot.crypto_util.find_chain_with_issuer"""
@classmethod
def _call(cls, fullchains, issuer_cn, **kwargs):
def _call(cls, fullchains: List[str], issuer_cn: str, **kwargs) -> str:
from certbot.crypto_util import find_chain_with_issuer
return find_chain_with_issuer(fullchains, issuer_cn, kwargs)
def _all_fullchains(self):
def _all_fullchains(self) -> List[str]:
return [CERT_LEAF.decode() + CERT_ISSUER.decode(),
CERT_LEAF.decode() + CERT_ALT_ISSUER.decode()]
def test_positive_match(self):
def test_positive_match(self) -> None:
"""Correctly pick the chain based on the root's CN"""
fullchains = self._all_fullchains()
matched = self._call(fullchains, "Pebble Root CA 0cc6f0")
assert matched == fullchains[1]
@mock.patch('certbot.crypto_util.logger.info')
def test_intermediate_match(self, mock_info):
def test_intermediate_match(self, mock_info: MagicMock) -> None:
"""Don't pick a chain where only an intermediate matches"""
fullchains = self._all_fullchains()
# Make the second chain actually only contain "Pebble Root CA 0cc6f0"
@@ -493,14 +496,14 @@ class FindChainWithIssuerTest(unittest.TestCase):
mock_info.assert_not_called()
@mock.patch('certbot.crypto_util.logger.info')
def test_no_match(self, mock_info):
def test_no_match(self, mock_info: MagicMock) -> None:
fullchains = self._all_fullchains()
matched = self._call(fullchains, "non-existent issuer")
assert matched == fullchains[0]
mock_info.assert_not_called()
@mock.patch('certbot.crypto_util.logger.warning')
def test_warning_on_no_match(self, mock_warning):
def test_warning_on_no_match(self, mock_warning: MagicMock) -> None:
fullchains = self._all_fullchains()
matched = self._call(fullchains, "non-existent issuer",
warn_on_no_match=True)

View File

@@ -11,6 +11,7 @@ import pytest
from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
from unittest.mock import NonCallableMagicMock
try:
import readline # pylint: disable=import-error
@@ -22,7 +23,7 @@ except ImportError:
class CompleterTest(test_util.TempDirTestCase):
"""Test certbot._internal.display.completer.Completer."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
# directories must end with os.sep for completer to
@@ -41,7 +42,7 @@ class CompleterTest(test_util.TempDirTestCase):
with open(path, 'w'):
pass
def test_complete(self):
def test_complete(self) -> None:
from certbot._internal.display import completer
my_completer = completer.Completer()
num_paths = len(self.paths)
@@ -57,7 +58,7 @@ class CompleterTest(test_util.TempDirTestCase):
@unittest.skipIf('readline' not in sys.modules,
reason='Not relevant if readline is not available.')
def test_import_error(self):
def test_import_error(self) -> None:
original_readline = sys.modules['readline']
sys.modules['readline'] = None
@@ -65,7 +66,7 @@ class CompleterTest(test_util.TempDirTestCase):
sys.modules['readline'] = original_readline
def test_context_manager_with_unmocked_readline(self):
def test_context_manager_with_unmocked_readline(self) -> None:
from certbot._internal.display import completer
reload_module(completer)
@@ -79,16 +80,16 @@ class CompleterTest(test_util.TempDirTestCase):
assert readline.get_completer_delims() == original_delims
@mock.patch('certbot._internal.display.completer.readline', autospec=True)
def test_context_manager_libedit(self, mock_readline):
def test_context_manager_libedit(self, mock_readline: NonCallableMagicMock) -> None:
mock_readline.__doc__ = 'libedit'
self._test_context_manager_with_mock_readline(mock_readline)
@mock.patch('certbot._internal.display.completer.readline', autospec=True)
def test_context_manager_readline(self, mock_readline):
def test_context_manager_readline(self, mock_readline: NonCallableMagicMock) -> None:
mock_readline.__doc__ = 'GNU readline'
self._test_context_manager_with_mock_readline(mock_readline)
def _test_context_manager_with_mock_readline(self, mock_readline):
def _test_context_manager_with_mock_readline(self, mock_readline: NonCallableMagicMock) -> None:
from certbot._internal.display import completer
mock_readline.parse_and_bind.side_effect = enable_tab_completion
@@ -99,7 +100,7 @@ class CompleterTest(test_util.TempDirTestCase):
assert mock_readline.parse_and_bind.called is True
def enable_tab_completion(unused_command):
def enable_tab_completion(unused_command: str) -> None:
"""Enables readline tab completion using the system specific syntax."""
libedit = readline.__doc__ is not None and 'libedit' in readline.__doc__
command = 'bind ^I rl_complete' if libedit else 'tab: complete'

View File

@@ -10,10 +10,12 @@ import pytest
from acme import messages as acme_messages
from certbot import errors
from typing import Any, List, Optional, Union
from unittest.mock import MagicMock
class WrapLinesTest(unittest.TestCase):
def test_wrap_lines(self):
def test_wrap_lines(self) -> None:
from certbot._internal.display.util import wrap_lines
msg = ("This is just a weak test{0}"
"This function is only meant to be for easy viewing{0}"
@@ -26,14 +28,14 @@ class WrapLinesTest(unittest.TestCase):
class PlaceParensTest(unittest.TestCase):
@classmethod
def _call(cls, label):
def _call(cls, label: str) -> str:
from certbot._internal.display.util import parens_around_char
return parens_around_char(label)
def test_single_letter(self):
def test_single_letter(self) -> None:
assert "(a)" == self._call("a")
def test_multiple(self):
def test_multiple(self) -> None:
assert "(L)abel" == self._call("Label")
assert "(y)es please" == self._call("yes please")
@@ -41,17 +43,17 @@ class PlaceParensTest(unittest.TestCase):
class InputWithTimeoutTest(unittest.TestCase):
"""Tests for certbot._internal.display.util.input_with_timeout."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> str:
from certbot._internal.display.util import input_with_timeout
return input_with_timeout(*args, **kwargs)
def test_eof(self):
def test_eof(self) -> None:
with tempfile.TemporaryFile("r+") as f:
with mock.patch("certbot._internal.display.util.sys.stdin", new=f):
with pytest.raises(EOFError):
self._call()
def test_input(self, prompt=None):
def test_input(self, prompt: Optional[str]=None) -> None:
expected = "foo bar"
stdin = io.StringIO(expected + "\n")
with mock.patch("certbot.compat.misc.select.select") as mock_select:
@@ -59,13 +61,13 @@ class InputWithTimeoutTest(unittest.TestCase):
assert self._call(prompt) == expected
@mock.patch("certbot._internal.display.util.sys.stdout")
def test_input_with_prompt(self, mock_stdout):
def test_input_with_prompt(self, mock_stdout: MagicMock) -> None:
prompt = "test prompt: "
self.test_input(prompt)
mock_stdout.write.assert_called_once_with(prompt)
mock_stdout.flush.assert_called_once_with()
def test_timeout(self):
def test_timeout(self) -> None:
stdin = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
stdin.bind(('', 0))
stdin.listen(1)
@@ -77,24 +79,24 @@ class InputWithTimeoutTest(unittest.TestCase):
class SeparateListInputTest(unittest.TestCase):
"""Test Module functions."""
def setUp(self):
def setUp(self) -> None:
self.exp = ["a", "b", "c", "test"]
@classmethod
def _call(cls, input_):
def _call(cls, input_: str) -> List[str]:
from certbot._internal.display.util import separate_list_input
return separate_list_input(input_)
def test_commas(self):
def test_commas(self) -> None:
assert self._call("a,b,c,test") == self.exp
def test_spaces(self):
def test_spaces(self) -> None:
assert self._call("a b c test") == self.exp
def test_both(self):
def test_both(self) -> None:
assert self._call("a, b, c, test") == self.exp
def test_mess(self):
def test_mess(self) -> None:
actual = [
self._call(" a , b c \t test"),
self._call(",a, ,, , b c test "),
@@ -107,22 +109,22 @@ class SeparateListInputTest(unittest.TestCase):
class SummarizeDomainListTest(unittest.TestCase):
@classmethod
def _call(cls, domains):
def _call(cls, domains: List[Union[Any, str]]) -> str:
from certbot._internal.display.util import summarize_domain_list
return summarize_domain_list(domains)
def test_single_domain(self):
def test_single_domain(self) -> None:
assert "example.com" == self._call(["example.com"])
def test_two_domains(self):
def test_two_domains(self) -> None:
assert "example.com and example.org" == \
self._call(["example.com", "example.org"])
def test_many_domains(self):
def test_many_domains(self) -> None:
assert "example.com and 2 more domains" == \
self._call(["example.com", "example.org", "a.example.com"])
def test_empty_domains(self):
def test_empty_domains(self) -> None:
assert "" == self._call([])
@@ -130,21 +132,21 @@ class DescribeACMEErrorTest(unittest.TestCase):
@classmethod
def _call(cls, typ: str = "urn:ietf:params:acme:error:badCSR",
title: str = "Unacceptable CSR",
detail: str = "CSR contained unknown extensions"):
detail: str = "CSR contained unknown extensions") -> str:
from certbot._internal.display.util import describe_acme_error
return describe_acme_error(
acme_messages.Error(typ=typ, title=title, detail=detail))
def test_title_and_detail(self):
def test_title_and_detail(self) -> None:
assert "Unacceptable CSR :: CSR contained unknown extensions" == self._call()
def test_detail(self):
def test_detail(self) -> None:
assert "CSR contained unknown extensions" == self._call(title=None)
def test_description(self):
def test_description(self) -> None:
assert acme_messages.ERROR_CODES["badCSR"] == self._call(title=None, detail=None)
def test_unknown_type(self):
def test_unknown_type(self) -> None:
assert "urn:ietf:params:acme:error:unknownErrorType" == \
self._call(typ="urn:ietf:params:acme:error:unknownErrorType", title=None, detail=None)

View File

@@ -8,6 +8,8 @@ import pytest
from certbot import errors
from certbot._internal.display import obj as display_obj
from certbot.display import util as display_util
from typing import Callable, List, Optional, Tuple, Union
from unittest.mock import MagicMock
CHOICES = [("First", "Description1"), ("Second", "Description2")]
TAGS = ["tag1", "tag2", "tag3"]
@@ -20,32 +22,32 @@ class FileOutputDisplayTest(unittest.TestCase):
functions look to a user, uncomment the test_visual function.
"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.mock_stdout = mock.MagicMock()
self.displayer = display_obj.FileDisplay(self.mock_stdout, False)
@mock.patch("certbot._internal.display.obj.logger")
def test_notification_no_pause(self, mock_logger):
def test_notification_no_pause(self, mock_logger: MagicMock) -> None:
self.displayer.notification("message", False)
string = self.mock_stdout.write.call_args[0][0]
assert "message" in string
mock_logger.debug.assert_called_with("Notifying user: %s", "message")
def test_notification_pause(self):
def test_notification_pause(self) -> None:
input_with_timeout = "certbot._internal.display.util.input_with_timeout"
with mock.patch(input_with_timeout, return_value="enter"):
self.displayer.notification("message", force_interactive=True)
assert "message" in self.mock_stdout.write.call_args[0][0]
def test_notification_noninteractive(self):
def test_notification_noninteractive(self) -> None:
self._force_noninteractive(self.displayer.notification, "message")
string = self.mock_stdout.write.call_args[0][0]
assert "message" in string
def test_notification_noninteractive2(self):
def test_notification_noninteractive2(self) -> None:
# The main purpose of this test is to make sure we only call
# logger.warning once which _force_noninteractive checks internally
self._force_noninteractive(self.displayer.notification, "message")
@@ -58,7 +60,7 @@ class FileOutputDisplayTest(unittest.TestCase):
string = self.mock_stdout.write.call_args[0][0]
assert "message2" in string
def test_notification_decoration(self):
def test_notification_decoration(self) -> None:
from certbot.compat import os
self.displayer.notification("message", pause=False, decorate=False)
string = self.mock_stdout.write.call_args[0][0]
@@ -71,25 +73,25 @@ class FileOutputDisplayTest(unittest.TestCase):
@mock.patch("certbot._internal.display.obj."
"FileDisplay._get_valid_int_ans")
def test_menu(self, mock_ans):
def test_menu(self, mock_ans: MagicMock) -> None:
mock_ans.return_value = (display_util.OK, 1)
ret = self.displayer.menu("message", CHOICES, force_interactive=True)
assert ret == (display_util.OK, 0)
def test_menu_noninteractive(self):
def test_menu_noninteractive(self) -> None:
default = 0
result = self._force_noninteractive(
self.displayer.menu, "msg", CHOICES, default=default)
assert result == (display_util.OK, default)
def test_input_cancel(self):
def test_input_cancel(self) -> None:
input_with_timeout = "certbot._internal.display.util.input_with_timeout"
with mock.patch(input_with_timeout, return_value="c"):
code, _ = self.displayer.input("message", force_interactive=True)
assert code, display_util.CANCEL
def test_input_normal(self):
def test_input_normal(self) -> None:
input_with_timeout = "certbot._internal.display.util.input_with_timeout"
with mock.patch(input_with_timeout, return_value="domain.com"):
code, input_ = self.displayer.input("message", force_interactive=True)
@@ -97,7 +99,7 @@ class FileOutputDisplayTest(unittest.TestCase):
assert code == display_util.OK
assert input_ == "domain.com"
def test_input_noninteractive(self):
def test_input_noninteractive(self) -> None:
default = "foo"
code, input_ = self._force_noninteractive(
self.displayer.input, "message", default=default)
@@ -105,18 +107,18 @@ class FileOutputDisplayTest(unittest.TestCase):
assert code == display_util.OK
assert input_ == default
def test_input_assertion_fail(self):
def test_input_assertion_fail(self) -> None:
# If the call to util.assert_valid_call is commented out, an
# error.Error is raised, otherwise, an AssertionError is raised.
with pytest.raises(Exception):
self._force_noninteractive(self.displayer.input, "message", cli_flag="--flag")
def test_input_assertion_fail2(self):
def test_input_assertion_fail2(self) -> None:
with mock.patch("certbot.display.util.assert_valid_call"):
with pytest.raises(errors.Error):
self._force_noninteractive(self.displayer.input, "msg", cli_flag="--flag")
def test_yesno(self):
def test_yesno(self) -> None:
input_with_timeout = "certbot._internal.display.util.input_with_timeout"
with mock.patch(input_with_timeout, return_value="Yes"):
assert self.displayer.yesno(
@@ -138,38 +140,38 @@ class FileOutputDisplayTest(unittest.TestCase):
assert self.displayer.yesno(
"msg", yes_label="Agree", force_interactive=True)
def test_yesno_noninteractive(self):
def test_yesno_noninteractive(self) -> None:
assert self._force_noninteractive(
self.displayer.yesno, "message", default=True)
@mock.patch("certbot._internal.display.util.input_with_timeout")
def test_checklist_valid(self, mock_input):
def test_checklist_valid(self, mock_input: MagicMock) -> None:
mock_input.return_value = "2 1"
code, tag_list = self.displayer.checklist(
"msg", TAGS, force_interactive=True)
assert (code, set(tag_list)) == (display_util.OK, {"tag1", "tag2"})
@mock.patch("certbot._internal.display.util.input_with_timeout")
def test_checklist_empty(self, mock_input):
def test_checklist_empty(self, mock_input: MagicMock) -> None:
mock_input.return_value = ""
code, tag_list = self.displayer.checklist("msg", TAGS, force_interactive=True)
assert (code, set(tag_list)) == (display_util.OK, {"tag1", "tag2", "tag3"})
@mock.patch("certbot._internal.display.util.input_with_timeout")
def test_checklist_miss_valid(self, mock_input):
def test_checklist_miss_valid(self, mock_input: MagicMock) -> None:
mock_input.side_effect = ["10", "tag1 please", "1"]
ret = self.displayer.checklist("msg", TAGS, force_interactive=True)
assert ret == (display_util.OK, ["tag1"])
@mock.patch("certbot._internal.display.util.input_with_timeout")
def test_checklist_miss_quit(self, mock_input):
def test_checklist_miss_quit(self, mock_input: MagicMock) -> None:
mock_input.side_effect = ["10", "c"]
ret = self.displayer.checklist("msg", TAGS, force_interactive=True)
assert ret == (display_util.CANCEL, [])
def test_checklist_noninteractive(self):
def test_checklist_noninteractive(self) -> None:
default = TAGS
code, input_ = self._force_noninteractive(
self.displayer.checklist, "msg", TAGS, default=default)
@@ -177,7 +179,7 @@ class FileOutputDisplayTest(unittest.TestCase):
assert code == display_util.OK
assert input_ == default
def test_scrub_checklist_input_valid(self):
def test_scrub_checklist_input_valid(self) -> None:
# pylint: disable=protected-access
indices = [
["1"],
@@ -195,7 +197,7 @@ class FileOutputDisplayTest(unittest.TestCase):
assert set_tags == exp[i]
@mock.patch("certbot._internal.display.util.input_with_timeout")
def test_directory_select(self, mock_input):
def test_directory_select(self, mock_input: MagicMock) -> None:
args = ["msg", "/var/www/html", "--flag", True]
user_input = "/var/www/html"
mock_input.return_value = user_input
@@ -203,7 +205,7 @@ class FileOutputDisplayTest(unittest.TestCase):
returned = self.displayer.directory_select(*args)
assert returned == (display_util.OK, user_input)
def test_directory_select_noninteractive(self):
def test_directory_select_noninteractive(self) -> None:
default = "/var/www/html"
code, input_ = self._force_noninteractive(
self.displayer.directory_select, "msg", default=default)
@@ -211,7 +213,7 @@ class FileOutputDisplayTest(unittest.TestCase):
assert code == display_util.OK
assert input_ == default
def _force_noninteractive(self, func, *args, **kwargs):
def _force_noninteractive(self, func: Callable, *args, **kwargs) -> Optional[Union[Tuple[str, str], Tuple[str, List[str]], Tuple[str, int], bool]]:
skipped_interaction = self.displayer.skipped_interaction
with mock.patch("certbot._internal.display.obj.sys.stdin") as mock_stdin:
@@ -226,7 +228,7 @@ class FileOutputDisplayTest(unittest.TestCase):
return result
def test_scrub_checklist_input_invalid(self):
def test_scrub_checklist_input_invalid(self) -> None:
# pylint: disable=protected-access
indices = [
["0"],
@@ -238,13 +240,13 @@ class FileOutputDisplayTest(unittest.TestCase):
for list_ in indices:
assert self.displayer._scrub_checklist_input(list_, TAGS) == []
def test_print_menu(self):
def test_print_menu(self) -> None:
# pylint: disable=protected-access
# This is purely cosmetic... just make sure there aren't any exceptions
self.displayer._print_menu("msg", CHOICES)
self.displayer._print_menu("msg", TAGS)
def test_get_valid_int_ans_valid(self):
def test_get_valid_int_ans_valid(self) -> None:
# pylint: disable=protected-access
input_with_timeout = "certbot._internal.display.util.input_with_timeout"
with mock.patch(input_with_timeout, return_value="1"):
@@ -254,7 +256,7 @@ class FileOutputDisplayTest(unittest.TestCase):
assert self.displayer._get_valid_int_ans(3) == \
(display_util.OK, int(ans))
def test_get_valid_int_ans_invalid(self):
def test_get_valid_int_ans_invalid(self) -> None:
# pylint: disable=protected-access
answers = [
["0", "c"],
@@ -270,19 +272,19 @@ class FileOutputDisplayTest(unittest.TestCase):
class NoninteractiveDisplayTest(unittest.TestCase):
"""Test non-interactive display. These tests are pretty easy!"""
def setUp(self):
def setUp(self) -> None:
self.mock_stdout = mock.MagicMock()
self.displayer = display_obj.NoninteractiveDisplay(self.mock_stdout)
@mock.patch("certbot._internal.display.obj.logger")
def test_notification_no_pause(self, mock_logger):
def test_notification_no_pause(self, mock_logger: MagicMock) -> None:
self.displayer.notification("message", 10)
string = self.mock_stdout.write.call_args[0][0]
assert "message" in string
mock_logger.debug.assert_called_with("Notifying user: %s", "message")
def test_notification_decoration(self):
def test_notification_decoration(self) -> None:
from certbot.compat import os
self.displayer.notification("message", pause=False, decorate=False)
string = self.mock_stdout.write.call_args[0][0]
@@ -293,34 +295,34 @@ class NoninteractiveDisplayTest(unittest.TestCase):
assert "- - - " in string
assert "message2" + os.linesep in string
def test_input(self):
def test_input(self) -> None:
d = "an incomputable value"
ret = self.displayer.input("message", default=d)
assert ret == (display_util.OK, d)
with pytest.raises(errors.MissingCommandlineFlag):
self.displayer.input("message")
def test_menu(self):
def test_menu(self) -> None:
ret = self.displayer.menu("message", CHOICES, default=1)
assert ret == (display_util.OK, 1)
with pytest.raises(errors.MissingCommandlineFlag):
self.displayer.menu("message", CHOICES)
def test_yesno(self):
def test_yesno(self) -> None:
d = False
ret = self.displayer.yesno("message", default=d)
assert ret == d
with pytest.raises(errors.MissingCommandlineFlag):
self.displayer.yesno("message")
def test_checklist(self):
def test_checklist(self) -> None:
d = [1, 3]
ret = self.displayer.checklist("message", TAGS, default=d)
assert ret == (display_util.OK, d)
with pytest.raises(errors.MissingCommandlineFlag):
self.displayer.checklist("message", TAGS)
def test_directory_select(self):
def test_directory_select(self) -> None:
default = "/var/www/html"
expected = (display_util.OK, default)
actual = self.displayer.directory_select("msg", default)

View File

@@ -16,6 +16,10 @@ 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
from certbot._internal.account import Account
from certbot.tests.util import FreezableMock
from typing import Any, List, Optional, Union
from unittest.mock import MagicMock
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
@@ -24,12 +28,12 @@ class GetEmailTest(unittest.TestCase):
"""Tests for certbot.display.ops.get_email."""
@classmethod
def _call(cls, **kwargs):
def _call(cls, **kwargs) -> str:
from certbot.display.ops import get_email
return get_email(**kwargs)
@test_util.patch_display_util()
def test_cancel_none(self, mock_get_utility):
def test_cancel_none(self, mock_get_utility: FreezableMock) -> None:
mock_input = mock_get_utility().input
mock_input.return_value = (display_util.CANCEL, "foo@bar.baz")
with pytest.raises(errors.Error):
@@ -38,7 +42,7 @@ class GetEmailTest(unittest.TestCase):
self._call(optional=False)
@test_util.patch_display_util()
def test_ok_safe(self, mock_get_utility):
def test_ok_safe(self, mock_get_utility: FreezableMock) -> None:
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:
@@ -46,7 +50,7 @@ class GetEmailTest(unittest.TestCase):
assert self._call() == "foo@bar.baz"
@test_util.patch_display_util()
def test_ok_not_safe(self, mock_get_utility):
def test_ok_not_safe(self, mock_get_utility: FreezableMock) -> None:
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:
@@ -54,7 +58,7 @@ class GetEmailTest(unittest.TestCase):
assert self._call() == "foo@bar.baz"
@test_util.patch_display_util()
def test_invalid_flag(self, mock_get_utility):
def test_invalid_flag(self, mock_get_utility: FreezableMock) -> None:
invalid_txt = "There seem to be problems"
mock_input = mock_get_utility().input
mock_input.return_value = (display_util.OK, "foo@bar.baz")
@@ -66,7 +70,7 @@ class GetEmailTest(unittest.TestCase):
assert invalid_txt in mock_input.call_args[0][0]
@test_util.patch_display_util()
def test_optional_flag(self, mock_get_utility):
def test_optional_flag(self, mock_get_utility: FreezableMock) -> None:
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:
@@ -76,7 +80,7 @@ class GetEmailTest(unittest.TestCase):
assert "--register-unsafely-without-email" not in call[0][0]
@test_util.patch_display_util()
def test_optional_invalid_unsafe(self, mock_get_utility):
def test_optional_invalid_unsafe(self, mock_get_utility: FreezableMock) -> None:
invalid_txt = "There seem to be problems"
mock_input = mock_get_utility().input
mock_input.return_value = (display_util.OK, "foo@bar.baz")
@@ -88,7 +92,7 @@ class GetEmailTest(unittest.TestCase):
class ChooseAccountTest(test_util.TempDirTestCase):
"""Tests for certbot.display.ops.choose_account."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
display_obj.set_display(display_obj.FileDisplay(sys.stdout, False))
@@ -110,39 +114,39 @@ class ChooseAccountTest(test_util.TempDirTestCase):
email="email2@g.com", phone="phone")), self.key)
@classmethod
def _call(cls, accounts):
def _call(cls, accounts: List[Account]) -> Optional[Account]:
return ops.choose_account(accounts)
@test_util.patch_display_util()
def test_one(self, mock_util):
def test_one(self, mock_util: FreezableMock) -> None:
mock_util().menu.return_value = (display_util.OK, 0)
assert self._call([self.acc1]) == self.acc1
@test_util.patch_display_util()
def test_two(self, mock_util):
def test_two(self, mock_util: FreezableMock) -> None:
mock_util().menu.return_value = (display_util.OK, 1)
assert self._call([self.acc1, self.acc2]) == self.acc2
@test_util.patch_display_util()
def test_cancel(self, mock_util):
def test_cancel(self, mock_util: FreezableMock) -> None:
mock_util().menu.return_value = (display_util.CANCEL, 1)
assert self._call([self.acc1, self.acc2]) is None
class GenHttpsNamesTest(unittest.TestCase):
"""Test _gen_https_names."""
def setUp(self):
def setUp(self) -> None:
display_obj.set_display(display_obj.FileDisplay(sys.stdout, False))
@classmethod
def _call(cls, domains):
def _call(cls, domains: List[Union[Any, str]]) -> str:
from certbot.display.ops import _gen_https_names
return _gen_https_names(domains)
def test_zero(self):
def test_zero(self) -> None:
assert self._call([]) == ""
def test_one(self):
def test_one(self) -> None:
doms = [
"example.com",
"asllkjsadfljasdf.c",
@@ -150,7 +154,7 @@ class GenHttpsNamesTest(unittest.TestCase):
for dom in doms:
assert self._call([dom]) == "https://%s" % dom
def test_two(self):
def test_two(self) -> None:
domains_list = [
["foo.bar.org", "bar.org"],
["paypal.google.facebook.live.com", "*.zombo.example.com"],
@@ -159,14 +163,14 @@ class GenHttpsNamesTest(unittest.TestCase):
assert self._call(doms) == \
"https://{dom[0]} and https://{dom[1]}".format(dom=doms)
def test_three(self):
def test_three(self) -> None:
doms = ["a.org", "b.org", "c.org"]
# We use an oxford comma
assert self._call(doms) == \
"https://{dom[0]}, https://{dom[1]}, and https://{dom[2]}".format(
dom=doms)
def test_four(self):
def test_four(self) -> None:
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))
@@ -176,27 +180,27 @@ class GenHttpsNamesTest(unittest.TestCase):
class ChooseNamesTest(unittest.TestCase):
"""Test choose names."""
def setUp(self):
def setUp(self) -> None:
display_obj.set_display(display_obj.FileDisplay(sys.stdout, False))
self.mock_install = mock.MagicMock()
@classmethod
def _call(cls, installer, question=None):
def _call(cls, installer: Optional[MagicMock], question: Optional[str]=None) -> Union[List[str], MagicMock]:
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):
def test_no_installer(self, mock_manual: MagicMock) -> None:
self._call(None)
assert mock_manual.call_count == 1
@test_util.patch_display_util()
def test_no_installer_cancel(self, mock_util):
def test_no_installer_cancel(self, mock_util: FreezableMock) -> None:
mock_util().input.return_value = (display_util.CANCEL, [])
assert self._call(None) == []
@test_util.patch_display_util()
def test_no_names_choose(self, mock_util):
def test_no_names_choose(self, mock_util: FreezableMock) -> None:
self.mock_install().get_all_names.return_value = set()
domain = "example.com"
mock_util().input.return_value = (display_util.OK, domain)
@@ -205,7 +209,7 @@ class ChooseNamesTest(unittest.TestCase):
assert mock_util().input.call_count == 1
assert actual_doms == [domain]
def test_sort_names_trivial(self):
def test_sort_names_trivial(self) -> None:
from certbot.display.ops import _sort_names
#sort an empty list
@@ -225,7 +229,7 @@ class ChooseNamesTest(unittest.TestCase):
assert _sort_names(unsorted_long) == sorted_long
def test_sort_names_many(self):
def test_sort_names_many(self) -> None:
from certbot.display.ops import _sort_names
unsorted_domains = [".cx.com", ".bx.com", ".ax.com", ".dx.com"]
@@ -245,7 +249,7 @@ class ChooseNamesTest(unittest.TestCase):
@test_util.patch_display_util()
def test_filter_names_valid_return(self, mock_util):
def test_filter_names_valid_return(self, mock_util: FreezableMock) -> None:
self.mock_install.get_all_names.return_value = {"example.com"}
mock_util().checklist.return_value = (display_util.OK, ["example.com"])
@@ -254,7 +258,7 @@ class ChooseNamesTest(unittest.TestCase):
assert mock_util().checklist.call_count == 1
@test_util.patch_display_util()
def test_filter_namees_override_question(self, mock_util):
def test_filter_namees_override_question(self, mock_util: FreezableMock) -> None:
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")
@@ -263,21 +267,21 @@ class ChooseNamesTest(unittest.TestCase):
assert mock_util().checklist.call_args[0][0] == "Custom"
@test_util.patch_display_util()
def test_filter_names_nothing_selected(self, mock_util):
def test_filter_names_nothing_selected(self, mock_util: FreezableMock) -> None:
self.mock_install.get_all_names.return_value = {"example.com"}
mock_util().checklist.return_value = (display_util.OK, [])
assert self._call(self.mock_install) == []
@test_util.patch_display_util()
def test_filter_names_cancel(self, mock_util):
def test_filter_names_cancel(self, mock_util: FreezableMock) -> None:
self.mock_install.get_all_names.return_value = {"example.com"}
mock_util().checklist.return_value = (
display_util.CANCEL, ["example.com"])
assert self._call(self.mock_install) == []
def test_get_valid_domains(self):
def test_get_valid_domains(self) -> None:
from certbot.display.ops import get_valid_domains
all_valid = ["example.com", "second.example.com",
"also.example.com", "under_score.example.com",
@@ -289,7 +293,7 @@ class ChooseNamesTest(unittest.TestCase):
assert len(get_valid_domains(two_valid)) == 2
@test_util.patch_display_util()
def test_choose_manually(self, mock_util):
def test_choose_manually(self, mock_util: FreezableMock) -> None:
from certbot.display.ops import _choose_names_manually
utility_mock = mock_util()
# No retry
@@ -316,7 +320,7 @@ class ChooseNamesTest(unittest.TestCase):
"justtld", "valid.example.com"]
@test_util.patch_display_util()
def test_choose_manually_retry(self, mock_util):
def test_choose_manually_retry(self, mock_util: FreezableMock) -> None:
from certbot.display.ops import _choose_names_manually
utility_mock = mock_util()
# Three iterations
@@ -330,13 +334,13 @@ class ChooseNamesTest(unittest.TestCase):
class SuccessInstallationTest(unittest.TestCase):
"""Test the success installation message."""
@classmethod
def _call(cls, names):
def _call(cls, names: List[str]) -> None:
from certbot.display.ops import success_installation
success_installation(names)
@test_util.patch_display_util()
@mock.patch("certbot.display.util.notify")
def test_success_installation(self, mock_notify, mock_display):
def test_success_installation(self, mock_notify: MagicMock, mock_display: FreezableMock) -> None:
mock_display().notification.return_value = None
names = ["example.com", "abc.com"]
@@ -352,13 +356,13 @@ class SuccessInstallationTest(unittest.TestCase):
class SuccessRenewalTest(unittest.TestCase):
"""Test the success renewal message."""
@classmethod
def _call(cls, names):
def _call(cls, names: List[str]) -> None:
from certbot.display.ops import success_renewal
success_renewal(names)
@test_util.patch_display_util()
@mock.patch("certbot.display.util.notify")
def test_success_renewal(self, mock_notify, mock_display):
def test_success_renewal(self, mock_notify: MagicMock, mock_display: FreezableMock) -> None:
mock_display().notification.return_value = None
names = ["example.com", "abc.com"]
@@ -370,13 +374,13 @@ class SuccessRenewalTest(unittest.TestCase):
class SuccessRevocationTest(unittest.TestCase):
"""Test the success revocation message."""
@classmethod
def _call(cls, path):
def _call(cls, path: str) -> None:
from certbot.display.ops import success_revocation
success_revocation(path)
@test_util.patch_display_util()
@mock.patch("certbot.display.util.notify")
def test_success_revocation(self, mock_notify, unused_mock_display):
def test_success_revocation(self, mock_notify: MagicMock, unused_mock_display: FreezableMock) -> None:
path = "/path/to/cert.pem"
self._call(path)
mock_notify.assert_called_once_with(
@@ -399,7 +403,7 @@ class ValidatorTests(unittest.TestCase):
raise errors.PluginError(ValidatorTests.__ERROR)
@test_util.patch_display_util()
def test_input_blank_with_validator(self, mock_util):
def test_input_blank_with_validator(self, mock_util: FreezableMock) -> None:
mock_util().input.side_effect = [(display_util.OK, ""),
(display_util.OK, ""),
(display_util.OK, ""),
@@ -410,28 +414,28 @@ class ValidatorTests(unittest.TestCase):
assert returned == (display_util.OK, self.valid_input)
@test_util.patch_display_util()
def test_input_validation_with_default(self, mock_util):
def test_input_validation_with_default(self, mock_util: FreezableMock) -> None:
mock_util().input.side_effect = [(display_util.OK, self.valid_input)]
returned = ops.validated_input(self.__validator, "msg", default="other")
assert returned == (display_util.OK, self.valid_input)
@test_util.patch_display_util()
def test_input_validation_with_bad_default(self, mock_util):
def test_input_validation_with_bad_default(self, mock_util: FreezableMock) -> None:
mock_util().input.side_effect = [(display_util.OK, self.valid_input)]
with pytest.raises(AssertionError):
ops.validated_input(self.__validator, "msg", default="")
@test_util.patch_display_util()
def test_input_cancel_with_validator(self, mock_util):
def test_input_cancel_with_validator(self, mock_util: FreezableMock) -> None:
mock_util().input.side_effect = [(display_util.CANCEL, "")]
code, unused_raw = ops.validated_input(self.__validator, "message", force_interactive=True)
assert code == display_util.CANCEL
@test_util.patch_display_util()
def test_directory_select_validation(self, mock_util):
def test_directory_select_validation(self, mock_util: FreezableMock) -> None:
mock_util().directory_select.side_effect = [(display_util.OK, ""),
(display_util.OK, self.valid_directory)]
@@ -440,14 +444,14 @@ class ValidatorTests(unittest.TestCase):
assert returned == (display_util.OK, self.valid_directory)
@test_util.patch_display_util()
def test_directory_select_validation_with_default(self, mock_util):
def test_directory_select_validation_with_default(self, mock_util: FreezableMock) -> None:
mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)]
returned = ops.validated_directory(self.__validator, "msg", default="other")
assert returned == (display_util.OK, self.valid_directory)
@test_util.patch_display_util()
def test_directory_select_validation_with_bad_default(self, mock_util):
def test_directory_select_validation_with_bad_default(self, mock_util: FreezableMock) -> None:
mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)]
with pytest.raises(AssertionError):
@@ -457,12 +461,12 @@ class ValidatorTests(unittest.TestCase):
class ChooseValuesTest(unittest.TestCase):
"""Test choose_values."""
@classmethod
def _call(cls, values, question):
def _call(cls, values: List[str], question: Optional[str]) -> List[Union[Any, str]]:
from certbot.display.ops import choose_values
return choose_values(values, question)
@test_util.patch_display_util()
def test_choose_names_success(self, mock_util):
def test_choose_names_success(self, mock_util: FreezableMock) -> None:
items = ["first", "second", "third"]
mock_util().checklist.return_value = (display_util.OK, [items[2]])
result = self._call(items, None)
@@ -471,7 +475,7 @@ class ChooseValuesTest(unittest.TestCase):
assert mock_util().checklist.call_args[0][0] == ""
@test_util.patch_display_util()
def test_choose_names_success_question(self, mock_util):
def test_choose_names_success_question(self, mock_util: FreezableMock) -> None:
items = ["first", "second", "third"]
question = "Which one?"
mock_util().checklist.return_value = (display_util.OK, [items[1]])
@@ -481,7 +485,7 @@ class ChooseValuesTest(unittest.TestCase):
assert mock_util().checklist.call_args[0][0] == question
@test_util.patch_display_util()
def test_choose_names_user_cancel(self, mock_util):
def test_choose_names_user_cancel(self, mock_util: FreezableMock) -> None:
items = ["first", "second", "third"]
question = "Want to cancel?"
mock_util().checklist.return_value = (display_util.CANCEL, [])
@@ -496,21 +500,21 @@ class ChooseValuesTest(unittest.TestCase):
class ReportExecutedCommand(unittest.TestCase):
"""Test report_executed_command"""
@classmethod
def _call(cls, cmd_name: str, rc: int, out: str, err: str):
def _call(cls, cmd_name: str, rc: int, out: str, err: str) -> None:
from certbot.display.ops import report_executed_command
report_executed_command(cmd_name, rc, out, err)
def test_mixed_success(self, mock_notify, mock_logger):
def test_mixed_success(self, mock_notify: MagicMock, mock_logger: MagicMock) -> None:
self._call("some-hook", 0, "Did a thing", "Some warning")
assert mock_logger.warning.call_count == 1
assert mock_notify.call_count == 1
def test_mixed_error(self, mock_notify, mock_logger):
def test_mixed_error(self, mock_notify: MagicMock, mock_logger: MagicMock) -> None:
self._call("some-hook", -127, "Did a thing", "Some warning")
assert mock_logger.warning.call_count == 2
assert mock_notify.call_count == 1
def test_empty_success(self, mock_notify, mock_logger):
def test_empty_success(self, mock_notify: MagicMock, mock_logger: MagicMock) -> None:
self._call("some-hook", 0, "\n", " ")
assert mock_logger.warning.call_count == 0
assert mock_notify.call_count == 0

View File

@@ -9,10 +9,11 @@ import pytest
from certbot import errors
import certbot.tests.util as test_util
from certbot.tests.util import FreezableMock
@test_util.patch_display_util()
def test_notify(mock_util):
def test_notify(mock_util: FreezableMock) -> None:
from certbot.display.util import notify
notify("Hello World")
mock_util().notification.assert_called_with(
@@ -21,7 +22,7 @@ def test_notify(mock_util):
@test_util.patch_display_util()
def test_notification(mock_util):
def test_notification(mock_util: FreezableMock) -> None:
from certbot.display.util import notification
notification("Hello World")
mock_util().notification.assert_called_with(
@@ -30,7 +31,7 @@ def test_notification(mock_util):
@test_util.patch_display_util()
def test_menu(mock_util):
def test_menu(mock_util: FreezableMock) -> None:
from certbot.display.util import menu
menu("Hello World", ["one", "two"], default=0)
mock_util().menu.assert_called_with(
@@ -39,7 +40,7 @@ def test_menu(mock_util):
@test_util.patch_display_util()
def test_input_text(mock_util):
def test_input_text(mock_util: FreezableMock) -> None:
from certbot.display.util import input_text
input_text("Hello World", default="something")
mock_util().input.assert_called_with(
@@ -48,7 +49,7 @@ def test_input_text(mock_util):
@test_util.patch_display_util()
def test_yesno(mock_util):
def test_yesno(mock_util: FreezableMock) -> None:
from certbot.display.util import yesno
yesno("Hello World", default=True)
mock_util().yesno.assert_called_with(
@@ -58,7 +59,7 @@ def test_yesno(mock_util):
@test_util.patch_display_util()
def test_checklist(mock_util):
def test_checklist(mock_util: FreezableMock) -> None:
from certbot.display.util import checklist
checklist("Hello World", ["one", "two"], default="one")
mock_util().checklist.assert_called_with(
@@ -67,7 +68,7 @@ def test_checklist(mock_util):
@test_util.patch_display_util()
def test_directory_select(mock_util):
def test_directory_select(mock_util: FreezableMock) -> None:
from certbot.display.util import directory_select
directory_select("Hello World", default="something")
mock_util().directory_select.assert_called_with(

View File

@@ -13,13 +13,15 @@ from acme import messages
from certbot._internal import account
from certbot._internal import constants
import certbot.tests.util as test_util
from certbot.tests.util import FreezableMock
from unittest.mock import MagicMock
_KEY = josepy.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
class SubscriptionTest(test_util.ConfigTestCase):
"""Abstract class for subscription tests."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.account = account.Account(
regr=messages.RegistrationResource(
@@ -36,13 +38,13 @@ class SubscriptionTest(test_util.ConfigTestCase):
class PrepareSubscriptionTest(SubscriptionTest):
"""Tests for certbot._internal.eff.prepare_subscription."""
def _call(self):
def _call(self) -> None:
from certbot._internal.eff import prepare_subscription
prepare_subscription(self.config, self.account)
@test_util.patch_display_util()
@mock.patch("certbot._internal.eff.display_util.notify")
def test_failure(self, mock_notify, mock_get_utility):
def test_failure(self, mock_notify: MagicMock, mock_get_utility: FreezableMock) -> None:
self.config.email = None
self.config.eff_email = True
self._call()
@@ -52,21 +54,21 @@ class PrepareSubscriptionTest(SubscriptionTest):
assert self.account.meta.register_to_eff is None
@test_util.patch_display_util()
def test_will_not_subscribe_with_no_prompt(self, mock_get_utility):
def test_will_not_subscribe_with_no_prompt(self, mock_get_utility: FreezableMock) -> None:
self.config.eff_email = False
self._call()
self._assert_no_get_utility_calls(mock_get_utility)
assert self.account.meta.register_to_eff is None
@test_util.patch_display_util()
def test_will_subscribe_with_no_prompt(self, mock_get_utility):
def test_will_subscribe_with_no_prompt(self, mock_get_utility: FreezableMock) -> None:
self.config.eff_email = True
self._call()
self._assert_no_get_utility_calls(mock_get_utility)
assert self.account.meta.register_to_eff == self.config.email
@test_util.patch_display_util()
def test_will_not_subscribe_with_prompt(self, mock_get_utility):
def test_will_not_subscribe_with_prompt(self, mock_get_utility: FreezableMock) -> None:
mock_get_utility().yesno.return_value = False
self._call()
assert not mock_get_utility().add_message.called
@@ -74,18 +76,18 @@ class PrepareSubscriptionTest(SubscriptionTest):
assert self.account.meta.register_to_eff is None
@test_util.patch_display_util()
def test_will_subscribe_with_prompt(self, mock_get_utility):
def test_will_subscribe_with_prompt(self, mock_get_utility: FreezableMock) -> None:
mock_get_utility().yesno.return_value = True
self._call()
assert not mock_get_utility().add_message.called
self._assert_correct_yesno_call(mock_get_utility)
assert self.account.meta.register_to_eff == self.config.email
def _assert_no_get_utility_calls(self, mock_get_utility):
def _assert_no_get_utility_calls(self, mock_get_utility: FreezableMock) -> None:
assert not mock_get_utility().yesno.called
assert not mock_get_utility().add_message.called
def _assert_correct_yesno_call(self, mock_get_utility):
def _assert_correct_yesno_call(self, mock_get_utility: FreezableMock) -> None:
assert mock_get_utility().yesno.called
call_args, call_kwargs = mock_get_utility().yesno.call_args
actual = call_args[0]
@@ -96,17 +98,17 @@ class PrepareSubscriptionTest(SubscriptionTest):
class HandleSubscriptionTest(SubscriptionTest):
"""Tests for certbot._internal.eff.handle_subscription."""
def _call(self):
def _call(self) -> None:
from certbot._internal.eff import handle_subscription
handle_subscription(self.config, self.account)
@mock.patch('certbot._internal.eff.subscribe')
def test_no_subscribe(self, mock_subscribe):
def test_no_subscribe(self, mock_subscribe: MagicMock) -> None:
self._call()
assert mock_subscribe.called is False
@mock.patch('certbot._internal.eff.subscribe')
def test_subscribe(self, mock_subscribe):
def test_subscribe(self, mock_subscribe: MagicMock) -> None:
self.account.meta = self.account.meta.update(register_to_eff=self.config.email)
self._call()
assert mock_subscribe.called
@@ -115,7 +117,7 @@ class HandleSubscriptionTest(SubscriptionTest):
class SubscribeTest(unittest.TestCase):
"""Tests for certbot._internal.eff.subscribe."""
def setUp(self):
def setUp(self) -> None:
self.email = 'certbot@example.org'
self.json = {'status': True}
self.response = mock.Mock(ok=True)
@@ -125,14 +127,14 @@ class SubscribeTest(unittest.TestCase):
self.addCleanup(patcher.stop)
@mock.patch('certbot._internal.eff.requests.post')
def _call(self, mock_post):
def _call(self, mock_post: MagicMock) -> None:
mock_post.return_value = self.response
from certbot._internal.eff import subscribe
subscribe(self.email)
self._check_post_call(mock_post)
def _check_post_call(self, mock_post):
def _check_post_call(self, mock_post: MagicMock) -> None:
assert mock_post.call_count == 1
call_args, call_kwargs = mock_post.call_args
assert call_args[0] == constants.EFF_SUBSCRIBE_URI
@@ -141,14 +143,14 @@ class SubscribeTest(unittest.TestCase):
assert data is not None
assert data.get('email') == self.email
def test_bad_status(self):
def test_bad_status(self) -> None:
self.json['status'] = False
self._call()
actual = self._get_reported_message()
expected_part = 'because your e-mail address appears to be invalid.'
assert expected_part in actual
def test_not_ok(self):
def test_not_ok(self) -> None:
self.response.ok = False
self.response.raise_for_status.side_effect = requests.exceptions.HTTPError
self._call()
@@ -156,26 +158,26 @@ class SubscribeTest(unittest.TestCase):
unexpected_part = 'because'
assert unexpected_part not in actual
def test_response_not_json(self):
def test_response_not_json(self) -> None:
self.response.json.side_effect = ValueError()
self._call()
actual = self._get_reported_message()
expected_part = 'problem'
assert expected_part in actual
def test_response_json_missing_status_element(self):
def test_response_json_missing_status_element(self) -> None:
self.json.clear()
self._call()
actual = self._get_reported_message()
expected_part = 'problem'
assert expected_part in actual
def _get_reported_message(self):
def _get_reported_message(self) -> str:
assert self.mock_notify.called
return self.mock_notify.call_args[0][0]
@test_util.patch_display_util()
def test_subscribe(self, mock_get_utility):
def test_subscribe(self, mock_get_utility: FreezableMock) -> None:
self._call()
assert mock_get_utility.called is False

View File

@@ -2,7 +2,7 @@
import contextlib
import signal
import sys
from typing import Callable
from typing import List, Callable
from typing import Dict
from typing import Union
import unittest
@@ -13,19 +13,19 @@ import pytest
from certbot.compat import os
def get_signals(signums):
def get_signals(signums: List[signal.Signals]) -> Dict[signal.Signals, Callable]:
"""Get the handlers for an iterable of signums."""
return {s: signal.getsignal(s) for s in signums}
def set_signals(sig_handler_dict):
def set_signals(sig_handler_dict: Dict[signal.Signals, Callable]) -> None:
"""Set the signal (keys) with the handler (values) from the input dict."""
for s, h in sig_handler_dict.items():
signal.signal(s, h)
@contextlib.contextmanager
def signal_receiver(signums):
def signal_receiver(signums: List[signal.Signals]) -> None:
"""Context manager to catch signals"""
signals = []
prev_handlers: Dict[int, Union[int, None, Callable]] = get_signals(signums)
@@ -34,7 +34,7 @@ def signal_receiver(signums):
set_signals(prev_handlers)
def send_signal(signum):
def send_signal(signum: signal.Signals) -> None:
"""Send the given signal"""
os.kill(os.getpid(), signum)
@@ -42,7 +42,7 @@ def send_signal(signum):
class ErrorHandlerTest(unittest.TestCase):
"""Tests for certbot._internal.error_handler.ErrorHandler."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal import error_handler
self.init_func = mock.MagicMock()
@@ -55,7 +55,7 @@ class ErrorHandlerTest(unittest.TestCase):
# pylint: disable=protected-access
self.signals = error_handler._SIGNALS
def test_context_manager(self):
def test_context_manager(self) -> None:
exception_raised = False
try:
with self.handler:
@@ -67,7 +67,7 @@ class ErrorHandlerTest(unittest.TestCase):
self.init_func.assert_called_once_with(*self.init_args,
**self.init_kwargs)
def test_context_manager_with_signal(self):
def test_context_manager_with_signal(self) -> None:
if not self.signals:
self.skipTest(reason='Signals cannot be handled on Windows.')
init_signals = get_signals(self.signals)
@@ -87,7 +87,7 @@ class ErrorHandlerTest(unittest.TestCase):
for signum in self.signals:
assert init_signals[signum] == signal.getsignal(signum)
def test_bad_recovery(self):
def test_bad_recovery(self) -> None:
bad_func = mock.MagicMock(side_effect=[ValueError])
self.handler.register(bad_func)
try:
@@ -99,7 +99,7 @@ class ErrorHandlerTest(unittest.TestCase):
**self.init_kwargs)
bad_func.assert_called_once_with()
def test_bad_recovery_with_signal(self):
def test_bad_recovery_with_signal(self) -> None:
if not self.signals:
self.skipTest(reason='Signals cannot be handled on Windows.')
sig1 = self.signals[0]
@@ -114,7 +114,7 @@ class ErrorHandlerTest(unittest.TestCase):
**self.init_kwargs)
bad_func.assert_called_once_with()
def test_sysexit_ignored(self):
def test_sysexit_ignored(self) -> None:
try:
with self.handler:
sys.exit(0)
@@ -122,7 +122,7 @@ class ErrorHandlerTest(unittest.TestCase):
pass
assert self.init_func.called is False
def test_regular_exit(self):
def test_regular_exit(self) -> None:
func = mock.MagicMock()
self.handler.register(func)
with self.handler:
@@ -134,14 +134,14 @@ class ErrorHandlerTest(unittest.TestCase):
class ExitHandlerTest(ErrorHandlerTest):
"""Tests for certbot._internal.error_handler.ExitHandler."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal import error_handler
super().setUp()
self.handler = error_handler.ExitHandler(self.init_func,
*self.init_args,
**self.init_kwargs)
def test_regular_exit(self):
def test_regular_exit(self) -> None:
func = mock.MagicMock()
self.handler.register(func)
with self.handler:

View File

@@ -13,19 +13,19 @@ from certbot.tests import acme_util
class FailedChallengesTest(unittest.TestCase):
"""Tests for certbot.errors.FailedChallenges."""
def setUp(self):
def setUp(self) -> None:
from certbot.errors import FailedChallenges
self.error = FailedChallenges({achallenges.DNS(
domain="example.com", challb=messages.ChallengeBody(
chall=acme_util.DNS01, uri=None,
error=messages.Error.with_code("tls", detail="detail")))})
def test_str(self):
def test_str(self) -> None:
assert str(self.error).startswith(
"Failed authorization procedure. example.com (dns-01): "
"urn:ietf:params:acme:error:tls")
def test_unicode(self):
def test_unicode(self) -> None:
from certbot.errors import FailedChallenges
arabic_detail = u'\u0639\u062f\u0627\u0644\u0629'
arabic_error = FailedChallenges({achallenges.DNS(
@@ -41,15 +41,15 @@ class FailedChallengesTest(unittest.TestCase):
class StandaloneBindErrorTest(unittest.TestCase):
"""Tests for certbot.errors.StandaloneBindError."""
def setUp(self):
def setUp(self) -> None:
from certbot.errors import StandaloneBindError
self.error = StandaloneBindError(mock.sentinel.error, 1234)
def test_instance_args(self):
def test_instance_args(self) -> None:
assert mock.sentinel.error == self.error.socket_error
assert 1234 == self.error.port
def test_str(self):
def test_str(self) -> None:
assert str(self.error).startswith(
"Problem binding to port 1234: ")

View File

@@ -8,11 +8,12 @@ from certbot import errors
from certbot._internal import constants
from certbot._internal.cli import _DomainsAction
from certbot._internal.cli import HelpfulArgumentParser
from unittest.mock import MagicMock
class TestScanningFlags:
'''Test the prescan_for_flag method of HelpfulArgumentParser'''
def test_prescan_no_help_flag(self):
def test_prescan_no_help_flag(self) -> None:
arg_parser = HelpfulArgumentParser(['run'], {})
detected_flag = arg_parser.prescan_for_flag('--help',
['all', 'certonly'])
@@ -21,7 +22,7 @@ class TestScanningFlags:
['all, certonly'])
assert detected_flag is False
def test_prescan_unvalid_topic(self):
def test_prescan_unvalid_topic(self) -> None:
arg_parser = HelpfulArgumentParser(['--help', 'all'], {})
detected_flag = arg_parser.prescan_for_flag('--help',
['potato'])
@@ -30,7 +31,7 @@ class TestScanningFlags:
arg_parser.help_topics)
assert detected_flag is False
def test_prescan_valid_topic(self):
def test_prescan_valid_topic(self) -> None:
arg_parser = HelpfulArgumentParser(['-h', 'all'], {})
detected_flag = arg_parser.prescan_for_flag('-h',
arg_parser.help_topics)
@@ -41,12 +42,12 @@ class TestScanningFlags:
class TestDetermineVerbs:
'''Tests for determine_verb methods of HelpfulArgumentParser'''
def test_determine_verb_wrong_verb(self):
def test_determine_verb_wrong_verb(self) -> None:
arg_parser = HelpfulArgumentParser(['potato'], {})
assert arg_parser.verb == "run"
assert arg_parser.args == ["potato"]
def test_determine_verb_help(self):
def test_determine_verb_help(self) -> None:
arg_parser = HelpfulArgumentParser(['--help', 'everything'], {})
assert arg_parser.verb == "help"
assert arg_parser.args == ["--help", "everything"]
@@ -56,7 +57,7 @@ class TestDetermineVerbs:
assert arg_parser.args == ['-d', 'some_domain', '--help',
'all']
def test_determine_verb(self):
def test_determine_verb(self) -> None:
arg_parser = HelpfulArgumentParser(['certonly'], {})
assert arg_parser.verb == 'certonly'
assert arg_parser.args == []
@@ -72,7 +73,7 @@ class TestDetermineVerbs:
class TestAdd:
'''Tests for add method in HelpfulArgumentParser'''
def test_add_trivial_argument(self):
def test_add_trivial_argument(self) -> None:
arg_parser = HelpfulArgumentParser(['run'], {})
arg_parser.add(None, "--hello-world")
parsed_args = arg_parser.parser.parse_args(['--hello-world',
@@ -80,7 +81,7 @@ class TestAdd:
assert parsed_args.hello_world is 'Hello World!'
assert not hasattr(parsed_args, 'potato')
def test_add_expected_argument(self):
def test_add_expected_argument(self) -> None:
arg_parser = HelpfulArgumentParser(['--help', 'run'], {})
arg_parser.add(
[None, "run", "certonly", "register"],
@@ -94,12 +95,12 @@ class TestAdd:
class TestAddGroup:
'''Test add_group method of HelpfulArgumentParser'''
def test_add_group_no_input(self):
def test_add_group_no_input(self) -> None:
arg_parser = HelpfulArgumentParser(['run'], {})
with pytest.raises(TypeError):
arg_parser.add_group()
def test_add_group_topic_not_visible(self):
def test_add_group_topic_not_visible(self) -> None:
# The user request help on run. A topic that given somewhere in the
# args won't be added to the groups in the parser.
arg_parser = HelpfulArgumentParser(['--help', 'run'], {})
@@ -107,7 +108,7 @@ class TestAddGroup:
description="description of auth")
assert arg_parser.groups == {}
def test_add_group_topic_requested_help(self):
def test_add_group_topic_requested_help(self) -> None:
arg_parser = HelpfulArgumentParser(['--help', 'run'], {})
arg_parser.add_group("run",
description="description of run")
@@ -120,7 +121,7 @@ class TestAddGroup:
class TestParseArgsErrors:
'''Tests for errors that should be met for some cases in parse_args method
in HelpfulArgumentParser'''
def test_parse_args_renew_force_interactive(self):
def test_parse_args_renew_force_interactive(self) -> None:
arg_parser = HelpfulArgumentParser(['renew', '--force-interactive'],
{})
arg_parser.add(
@@ -129,7 +130,7 @@ class TestParseArgsErrors:
with pytest.raises(errors.Error):
arg_parser.parse_args()
def test_parse_args_non_interactive_and_force_interactive(self):
def test_parse_args_non_interactive_and_force_interactive(self) -> None:
arg_parser = HelpfulArgumentParser(['--force-interactive',
'--non-interactive'], {})
arg_parser.add(
@@ -142,7 +143,7 @@ class TestParseArgsErrors:
with pytest.raises(errors.Error):
arg_parser.parse_args()
def test_parse_args_subset_names_wildcard_domain(self):
def test_parse_args_subset_names_wildcard_domain(self) -> None:
arg_parser = HelpfulArgumentParser(['--domain',
'*.example.com,potato.example.com',
'--allow-subset-of-names'], {})
@@ -168,7 +169,7 @@ class TestParseArgsErrors:
# with self.assertRaises(errors.Error):
# arg_parser.parse_args()
def test_parse_args_hosts_and_auto_hosts(self):
def test_parse_args_hosts_and_auto_hosts(self) -> None:
arg_parser = HelpfulArgumentParser(['--hsts', '--auto-hsts'], {})
arg_parser.add(
@@ -197,7 +198,7 @@ class TestAddDeprecatedArgument:
"""Tests for add_deprecated_argument method of HelpfulArgumentParser"""
@mock.patch.object(HelpfulArgumentParser, "modify_kwargs_for_default_detection")
def test_no_default_detection_modifications(self, mock_modify):
def test_no_default_detection_modifications(self, mock_modify: MagicMock) -> None:
arg_parser = HelpfulArgumentParser(["run"], {}, detect_defaults=True)
arg_parser.add_deprecated_argument("--foo", 0)
arg_parser.parse_args()

View File

@@ -10,18 +10,20 @@ from certbot import util
from certbot.compat import filesystem
from certbot.compat import os
from certbot.tests import util as test_util
from typing import Any, List, Union
from unittest.mock import MagicMock
class ValidateHooksTest(unittest.TestCase):
"""Tests for certbot._internal.hooks.validate_hooks."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import validate_hooks
return validate_hooks(*args, **kwargs)
@mock.patch("certbot._internal.hooks.validate_hook")
def test_it(self, mock_validate_hook):
def test_it(self, mock_validate_hook: MagicMock) -> None:
config = mock.MagicMock()
self._call(config)
@@ -35,11 +37,11 @@ class ValidateHookTest(test_util.TempDirTestCase):
"""Tests for certbot._internal.hooks.validate_hook."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import validate_hook
return validate_hook(*args, **kwargs)
def test_hook_not_executable(self):
def test_hook_not_executable(self) -> None:
# prevent unnecessary modifications to PATH
with mock.patch("certbot._internal.hooks.plug_util.path_surgery"):
# We just mock out filesystem.is_executable since on Windows, it is difficult
@@ -50,7 +52,7 @@ class ValidateHookTest(test_util.TempDirTestCase):
self._call('dummy', "foo")
@mock.patch("certbot._internal.hooks.util.exe_exists")
def test_not_found(self, mock_exe_exists):
def test_not_found(self, mock_exe_exists: MagicMock) -> None:
mock_exe_exists.return_value = False
with mock.patch("certbot._internal.hooks.plug_util.path_surgery") as mock_ps:
with pytest.raises(errors.HookCommandNotFound):
@@ -58,7 +60,7 @@ class ValidateHookTest(test_util.TempDirTestCase):
assert mock_ps.called
@mock.patch("certbot._internal.hooks._prog")
def test_unset(self, mock_prog):
def test_unset(self, mock_prog: MagicMock) -> None:
self._call(None, "foo")
assert mock_prog.called is False
@@ -72,7 +74,7 @@ class HookTest(test_util.ConfigTestCase):
raise NotImplementedError
@classmethod
def _call_with_mock_execute(cls, *args, **kwargs):
def _call_with_mock_execute(cls, *args, **kwargs) -> MagicMock:
"""Calls self._call after mocking out certbot.compat.misc.execute_command_status.
The mock execute object is returned rather than the return value
@@ -89,11 +91,11 @@ class PreHookTest(HookTest):
"""Tests for certbot._internal.hooks.pre_hook."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import pre_hook
return pre_hook(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.pre_hook = "foo"
@@ -104,29 +106,29 @@ class PreHookTest(HookTest):
# Reset this value as it may have been modified by past tests
self._reset_pre_hook_already()
def tearDown(self):
def tearDown(self) -> None:
# Reset this value so it's unmodified for future tests
self._reset_pre_hook_already()
super().tearDown()
def _reset_pre_hook_already(self):
def _reset_pre_hook_already(self) -> None:
from certbot._internal.hooks import executed_pre_hooks
executed_pre_hooks.clear()
def test_certonly(self):
def test_certonly(self) -> None:
self.config.verb = "certonly"
self._test_nonrenew_common()
def test_run(self):
def test_run(self) -> None:
self.config.verb = "run"
self._test_nonrenew_common()
def _test_nonrenew_common(self):
def _test_nonrenew_common(self) -> None:
mock_execute = self._call_with_mock_execute(self.config)
mock_execute.assert_called_once_with("pre-hook", self.config.pre_hook, env=mock.ANY)
self._test_no_executions_common()
def test_no_hooks(self):
def test_no_hooks(self) -> None:
self.config.pre_hook = None
self.config.verb = "renew"
os.remove(self.dir_hook)
@@ -136,27 +138,27 @@ class PreHookTest(HookTest):
assert mock_execute.called is False
assert mock_logger.info.called is False
def test_renew_disabled_dir_hooks(self):
def test_renew_disabled_dir_hooks(self) -> None:
self.config.directory_hooks = False
mock_execute = self._call_with_mock_execute(self.config)
mock_execute.assert_called_once_with("pre-hook", self.config.pre_hook, env=mock.ANY)
self._test_no_executions_common()
def test_renew_no_overlap(self):
def test_renew_no_overlap(self) -> None:
self.config.verb = "renew"
mock_execute = self._call_with_mock_execute(self.config)
mock_execute.assert_any_call("pre-hook", self.dir_hook, env=mock.ANY)
mock_execute.assert_called_with("pre-hook", self.config.pre_hook, env=mock.ANY)
self._test_no_executions_common()
def test_renew_with_overlap(self):
def test_renew_with_overlap(self) -> None:
self.config.pre_hook = self.dir_hook
self.config.verb = "renew"
mock_execute = self._call_with_mock_execute(self.config)
mock_execute.assert_called_once_with("pre-hook", self.dir_hook, env=mock.ANY)
self._test_no_executions_common()
def _test_no_executions_common(self):
def _test_no_executions_common(self) -> None:
with mock.patch("certbot._internal.hooks.logger") as mock_logger:
mock_execute = self._call_with_mock_execute(self.config)
assert mock_execute.called is False
@@ -167,11 +169,11 @@ class PostHookTest(HookTest):
"""Tests for certbot._internal.hooks.post_hook."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import post_hook
return post_hook(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.post_hook = "bar"
@@ -182,47 +184,47 @@ class PostHookTest(HookTest):
# Reset this value as it may have been modified by past tests
self._reset_post_hook_eventually()
def tearDown(self):
def tearDown(self) -> None:
# Reset this value so it's unmodified for future tests
self._reset_post_hook_eventually()
super().tearDown()
def _reset_post_hook_eventually(self):
def _reset_post_hook_eventually(self) -> None:
from certbot._internal.hooks import post_hooks
del post_hooks[:]
def test_certonly_and_run_with_hook(self):
def test_certonly_and_run_with_hook(self) -> None:
for verb in ("certonly", "run",):
self.config.verb = verb
mock_execute = self._call_with_mock_execute(self.config)
mock_execute.assert_called_once_with("post-hook", self.config.post_hook, env=mock.ANY)
assert not self._get_eventually()
def test_cert_only_and_run_without_hook(self):
def test_cert_only_and_run_without_hook(self) -> None:
self.config.post_hook = None
for verb in ("certonly", "run",):
self.config.verb = verb
assert not self._call_with_mock_execute(self.config).called
assert not self._get_eventually()
def test_renew_disabled_dir_hooks(self):
def test_renew_disabled_dir_hooks(self) -> None:
self.config.directory_hooks = False
self._test_renew_common([self.config.post_hook])
def test_renew_no_config_hook(self):
def test_renew_no_config_hook(self) -> None:
self.config.post_hook = None
self._test_renew_common([self.dir_hook])
def test_renew_no_dir_hook(self):
def test_renew_no_dir_hook(self) -> None:
os.remove(self.dir_hook)
self._test_renew_common([self.config.post_hook])
def test_renew_no_hooks(self):
def test_renew_no_hooks(self) -> None:
self.config.post_hook = None
os.remove(self.dir_hook)
self._test_renew_common([])
def test_renew_no_overlap(self):
def test_renew_no_overlap(self) -> None:
expected = [self.dir_hook, self.config.post_hook]
self._test_renew_common(expected)
@@ -230,18 +232,18 @@ class PostHookTest(HookTest):
expected.append(self.config.post_hook)
self._test_renew_common(expected)
def test_renew_with_overlap(self):
def test_renew_with_overlap(self) -> None:
self.config.post_hook = self.dir_hook
self._test_renew_common([self.dir_hook])
def _test_renew_common(self, expected):
def _test_renew_common(self, expected: List[Union[str, Any]]) -> None:
self.config.verb = "renew"
for _ in range(2):
self._call(self.config)
assert self._get_eventually() == expected
def _get_eventually(self):
def _get_eventually(self) -> List[Union[str, Any]]:
from certbot._internal.hooks import post_hooks
return post_hooks
@@ -250,11 +252,11 @@ class RunSavedPostHooksTest(HookTest):
"""Tests for certbot._internal.hooks.run_saved_post_hooks."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import run_saved_post_hooks
return run_saved_post_hooks()
def _call_with_mock_execute_and_eventually(self, *args, **kwargs):
def _call_with_mock_execute_and_eventually(self, *args, **kwargs) -> MagicMock:
"""Call run_saved_post_hooks but mock out execute and eventually
certbot._internal.hooks.post_hooks is replaced with
@@ -266,14 +268,14 @@ class RunSavedPostHooksTest(HookTest):
with mock.patch(eventually_path, new=self.eventually):
return self._call_with_mock_execute(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.eventually: List[str] = []
def test_empty(self):
def test_empty(self) -> None:
assert not self._call_with_mock_execute_and_eventually().called
def test_multiple(self):
def test_multiple(self) -> None:
self.eventually = ["foo", "bar", "baz", "qux"]
mock_execute = self._call_with_mock_execute_and_eventually()
@@ -281,7 +283,7 @@ class RunSavedPostHooksTest(HookTest):
for actual_call, expected_arg in zip(calls, self.eventually):
assert actual_call[0][1] == expected_arg
def test_single(self):
def test_single(self) -> None:
self.eventually = ["foo"]
mock_execute = self._call_with_mock_execute_and_eventually()
mock_execute.assert_called_once_with("post-hook", self.eventually[0], env=mock.ANY)
@@ -292,7 +294,7 @@ class RenewalHookTest(HookTest):
# Needed for https://github.com/PyCQA/pylint/issues/179
# pylint: disable=abstract-method
def _call_with_mock_execute(self, *args, **kwargs):
def _call_with_mock_execute(self, *args, **kwargs) -> MagicMock:
"""Calls self._call after mocking out certbot.compat.misc.execute_command_status.
The mock execute object is returned rather than the return value
@@ -319,14 +321,14 @@ class RenewalHookTest(HookTest):
self._call(*args, **kwargs)
return mock_execute
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.vars_to_clear = {
var for var in ("RENEWED_DOMAINS", "RENEWED_LINEAGE",)
if var not in os.environ
}
def tearDown(self):
def tearDown(self) -> None:
for var in self.vars_to_clear:
os.environ.pop(var, None)
super().tearDown()
@@ -336,12 +338,12 @@ class DeployHookTest(RenewalHookTest):
"""Tests for certbot._internal.hooks.deploy_hook."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import deploy_hook
return deploy_hook(*args, **kwargs)
@mock.patch("certbot._internal.hooks.logger")
def test_dry_run(self, mock_logger):
def test_dry_run(self, mock_logger: MagicMock) -> None:
self.config.deploy_hook = "foo"
self.config.dry_run = True
mock_execute = self._call_with_mock_execute(
@@ -350,14 +352,14 @@ class DeployHookTest(RenewalHookTest):
assert mock_logger.info.called
@mock.patch("certbot._internal.hooks.logger")
def test_no_hook(self, mock_logger):
def test_no_hook(self, mock_logger: MagicMock) -> None:
self.config.deploy_hook = None
mock_execute = self._call_with_mock_execute(
self.config, ["example.org"], "/foo/bar")
assert mock_execute.called is False
assert mock_logger.info.called is False
def test_success(self):
def test_success(self) -> None:
domains = ["example.org", "example.net"]
lineage = "/foo/bar"
self.config.deploy_hook = "foo"
@@ -370,11 +372,11 @@ class RenewHookTest(RenewalHookTest):
"""Tests for certbot._internal.hooks.renew_hook"""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.hooks import renew_hook
return renew_hook(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.renew_hook = "foo"
@@ -383,21 +385,21 @@ class RenewHookTest(RenewalHookTest):
"bar")
create_hook(self.dir_hook)
def test_disabled_dir_hooks(self):
def test_disabled_dir_hooks(self) -> None:
self.config.directory_hooks = False
mock_execute = self._call_with_mock_execute(
self.config, ["example.org"], "/foo/bar")
mock_execute.assert_called_once_with("deploy-hook", self.config.renew_hook, env=mock.ANY)
@mock.patch("certbot._internal.hooks.logger")
def test_dry_run(self, mock_logger):
def test_dry_run(self, mock_logger: MagicMock) -> None:
self.config.dry_run = True
mock_execute = self._call_with_mock_execute(
self.config, ["example.org"], "/foo/bar")
assert mock_execute.called is False
assert mock_logger.info.call_count == 2
def test_no_hooks(self):
def test_no_hooks(self) -> None:
self.config.renew_hook = None
os.remove(self.dir_hook)
@@ -407,13 +409,13 @@ class RenewHookTest(RenewalHookTest):
assert mock_execute.called is False
assert mock_logger.info.called is False
def test_overlap(self):
def test_overlap(self) -> None:
self.config.renew_hook = self.dir_hook
mock_execute = self._call_with_mock_execute(
self.config, ["example.net", "example.org"], "/foo/bar")
mock_execute.assert_called_once_with("deploy-hook", self.dir_hook, env=mock.ANY)
def test_no_overlap(self):
def test_no_overlap(self) -> None:
mock_execute = self._call_with_mock_execute(
self.config, ["example.org"], "/foo/bar")
mock_execute.assert_any_call("deploy-hook", self.dir_hook, env=mock.ANY)
@@ -424,14 +426,14 @@ class ListHooksTest(test_util.TempDirTestCase):
"""Tests for certbot._internal.hooks.list_hooks."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> List[Union[str, Any]]:
from certbot._internal.hooks import list_hooks
return list_hooks(*args, **kwargs)
def test_empty(self):
def test_empty(self) -> None:
assert not self._call(self.tempdir)
def test_multiple(self):
def test_multiple(self) -> None:
names = sorted(
os.path.join(self.tempdir, basename)
for basename in ("foo", "bar", "baz", "qux")
@@ -441,20 +443,20 @@ class ListHooksTest(test_util.TempDirTestCase):
assert self._call(self.tempdir) == names
def test_single(self):
def test_single(self) -> None:
name = os.path.join(self.tempdir, "foo")
create_hook(name)
assert self._call(self.tempdir) == [name]
def test_ignore_tilde(self):
def test_ignore_tilde(self) -> None:
name = os.path.join(self.tempdir, "foo~")
create_hook(name)
assert self._call(self.tempdir) == []
def create_hook(file_path):
def create_hook(file_path: str) -> None:
"""Creates an executable file at the specified path.
:param str file_path: path to create the file at

View File

@@ -8,6 +8,7 @@ from unittest import mock
import pytest
from certbot import errors
from certbot._internal import lock
from certbot.compat import os
from certbot.tests import util as test_util
@@ -23,32 +24,22 @@ else:
class LockDirTest(test_util.TempDirTestCase):
"""Tests for certbot._internal.lock.lock_dir."""
@classmethod
def _call(cls, *args, **kwargs):
from certbot._internal.lock import lock_dir
return lock_dir(*args, **kwargs)
def test_it(self):
def test_it(self) -> None:
assert_raises = functools.partial(
self.assertRaises, errors.LockError, self._call, self.tempdir)
self.assertRaises, errors.LockError, lock.lock_dir, self.tempdir)
lock_path = os.path.join(self.tempdir, '.certbot.lock')
test_util.lock_and_call(assert_raises, lock_path)
class LockFileTest(test_util.TempDirTestCase):
"""Tests for certbot._internal.lock.LockFile."""
@classmethod
def _call(cls, *args, **kwargs):
from certbot._internal.lock import LockFile
return LockFile(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.lock_path = os.path.join(self.tempdir, 'test.lock')
def test_acquire_without_deletion(self):
def test_acquire_without_deletion(self) -> None:
# acquire the lock in another process but don't delete the file
child = multiprocessing.Process(target=self._call,
child = multiprocessing.Process(target=lock.LockFile,
args=(self.lock_path,))
child.start()
child.join()
@@ -58,13 +49,13 @@ class LockFileTest(test_util.TempDirTestCase):
# Test we're still able to properly acquire and release the lock
self.test_removed()
def test_contention(self):
def test_contention(self) -> None:
assert_raises = functools.partial(
self.assertRaises, errors.LockError, self._call, self.lock_path)
self.assertRaises, errors.LockError, lock.LockFile, self.lock_path)
test_util.lock_and_call(assert_raises, self.lock_path)
def test_locked_repr(self):
lock_file = self._call(self.lock_path)
def test_locked_repr(self) -> None:
lock_file = lock.LockFile(self.lock_path)
try:
locked_repr = repr(lock_file)
self._test_repr_common(lock_file, locked_repr)
@@ -72,14 +63,14 @@ class LockFileTest(test_util.TempDirTestCase):
finally:
lock_file.release()
def test_released_repr(self):
lock_file = self._call(self.lock_path)
def test_released_repr(self) -> None:
lock_file = lock.LockFile(self.lock_path)
lock_file.release()
released_repr = repr(lock_file)
self._test_repr_common(lock_file, released_repr)
assert 'released' in released_repr
def _test_repr_common(self, lock_file, lock_repr):
def _test_repr_common(self, lock_file: lock.LockFile, lock_repr: str) -> None:
assert lock_file.__class__.__name__ in lock_repr
assert self.lock_path in lock_repr
@@ -100,15 +91,15 @@ class LockFileTest(test_util.TempDirTestCase):
with mock.patch('certbot._internal.lock.filesystem.os.stat') as mock_stat:
mock_stat.side_effect = delete_and_stat
self._call(self.lock_path)
lock.LockFile(self.lock_path)
assert len(should_delete) == 0
def test_removed(self):
lock_file = self._call(self.lock_path)
def test_removed(self) -> None:
lock_file = lock.LockFile(self.lock_path)
lock_file.release()
assert not os.path.exists(self.lock_path)
def test_unexpected_lockf_or_locking_err(self):
def test_unexpected_lockf_or_locking_err(self) -> None:
if POSIX_MODE:
mocked_function = 'certbot._internal.lock.fcntl.lockf'
else:
@@ -117,13 +108,13 @@ class LockFileTest(test_util.TempDirTestCase):
with mock.patch(mocked_function) as mock_lock:
mock_lock.side_effect = IOError(msg)
try:
self._call(self.lock_path)
lock.LockFile(self.lock_path)
except IOError as err:
assert msg in str(err)
else: # pragma: no cover
self.fail('IOError not raised')
def test_unexpected_os_err(self):
def test_unexpected_os_err(self) -> None:
if POSIX_MODE:
mock_function = 'certbot._internal.lock.filesystem.os.stat'
else:
@@ -133,7 +124,7 @@ class LockFileTest(test_util.TempDirTestCase):
with mock.patch(mock_function) as mock_os:
mock_os.side_effect = OSError(msg)
try:
self._call(self.lock_path)
lock.LockFile(self.lock_path)
except OSError as err:
assert msg in str(err)
else: # pragma: no cover

View File

@@ -4,7 +4,7 @@ import logging
import logging.handlers
import sys
import time
from typing import Optional
from typing import Callable, Tuple, Type, Union, Optional
import unittest
from unittest import mock
@@ -17,17 +17,18 @@ from certbot._internal import constants
from certbot.compat import filesystem
from certbot.compat import os
from certbot.tests import util as test_util
from unittest.mock import MagicMock
class PreArgParseSetupTest(unittest.TestCase):
"""Tests for certbot._internal.log.pre_arg_parse_setup."""
@classmethod
def _call(cls, *args, **kwargs): # pylint: disable=unused-argument
def _call(cls, *args, **kwargs) -> None: # pylint: disable=unused-argument
from certbot._internal.log import pre_arg_parse_setup
return pre_arg_parse_setup()
def tearDown(self):
def tearDown(self) -> None:
# We need to call logging.shutdown() at the end of this test to
# properly clean up any resources created by pre_arg_parse_setup.
logging.shutdown()
@@ -37,7 +38,7 @@ class PreArgParseSetupTest(unittest.TestCase):
@mock.patch('certbot._internal.log.pre_arg_parse_except_hook')
@mock.patch('certbot._internal.log.logging.getLogger')
@mock.patch('certbot._internal.log.util.atexit_register')
def test_it(self, mock_register, mock_get, mock_except_hook, mock_sys):
def test_it(self, mock_register: MagicMock, mock_get: MagicMock, mock_except_hook: MagicMock, mock_sys: MagicMock) -> None:
mock_sys.argv = ['--debug']
mock_sys.version_info = sys.version_info
self._call()
@@ -66,11 +67,11 @@ class PostArgParseSetupTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.log.post_arg_parse_setup."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.log import post_arg_parse_setup
return post_arg_parse_setup(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.debug = False
self.config.max_log_backups = 1000
@@ -88,14 +89,14 @@ class PostArgParseSetupTest(test_util.ConfigTestCase):
self.root_logger = mock.MagicMock(
handlers=[self.memory_handler, self.stream_handler])
def tearDown(self):
def tearDown(self) -> None:
self.memory_handler.close()
self.stream_handler.close()
self.temp_handler.close()
self.devnull.close()
super().tearDown()
def test_common(self):
def test_common(self) -> None:
with mock.patch('certbot._internal.log.logging.getLogger') as mock_get_logger:
mock_get_logger.return_value = self.root_logger
except_hook_path = 'certbot._internal.log.post_arg_parse_except_hook'
@@ -122,11 +123,11 @@ class PostArgParseSetupTest(test_util.ConfigTestCase):
else:
assert level == constants.DEFAULT_LOGGING_LEVEL
def test_debug(self):
def test_debug(self) -> None:
self.config.debug = True
self.test_common()
def test_quiet(self):
def test_quiet(self) -> None:
self.config.quiet = True
self.test_common()
@@ -135,16 +136,16 @@ class SetupLogFileHandlerTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.log.setup_log_file_handler."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> Union[Tuple[MagicMock, str], Tuple[ logging.handlers.RotatingFileHandler, str]]:
from certbot._internal.log import setup_log_file_handler
return setup_log_file_handler(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config.max_log_backups = 42
@mock.patch('certbot._internal.main.logging.handlers.RotatingFileHandler')
def test_failure(self, mock_handler):
def test_failure(self, mock_handler: MagicMock) -> None:
mock_handler.side_effect = IOError
try:
@@ -154,14 +155,14 @@ class SetupLogFileHandlerTest(test_util.ConfigTestCase):
else: # pragma: no cover
self.fail('Error not raised.')
def test_success_with_rollover(self):
def test_success_with_rollover(self) -> None:
self._test_success_common(should_rollover=True)
def test_success_without_rollover(self):
def test_success_without_rollover(self) -> None:
self.config.max_log_backups = 0
self._test_success_common(should_rollover=False)
def _test_success_common(self, should_rollover):
def _test_success_common(self, should_rollover: bool) -> None:
log_file = 'test.log'
handler, log_path = self._call(self.config, log_file, '%(message)s')
handler.close()
@@ -176,7 +177,7 @@ class SetupLogFileHandlerTest(test_util.ConfigTestCase):
assert os.path.exists(backup_path) == should_rollover
@mock.patch('certbot._internal.log.logging.handlers.RotatingFileHandler')
def test_max_log_backups_used(self, mock_handler):
def test_max_log_backups_used(self, mock_handler: MagicMock) -> None:
self._call(self.config, 'test.log', '%(message)s')
backup_count = mock_handler.call_args[1]['backupCount']
assert self.config.max_log_backups == backup_count
@@ -185,7 +186,7 @@ class SetupLogFileHandlerTest(test_util.ConfigTestCase):
class ColoredStreamHandlerTest(unittest.TestCase):
"""Tests for certbot._internal.log.ColoredStreamHandler"""
def setUp(self):
def setUp(self) -> None:
self.stream = io.StringIO()
self.stream.isatty = lambda: True
self.logger = logging.getLogger()
@@ -195,15 +196,15 @@ class ColoredStreamHandlerTest(unittest.TestCase):
self.handler = ColoredStreamHandler(self.stream)
self.logger.addHandler(self.handler)
def tearDown(self):
def tearDown(self) -> None:
self.handler.close()
def test_format(self):
def test_format(self) -> None:
msg = 'I did a thing'
self.logger.debug(msg)
assert self.stream.getvalue() == '{0}\n'.format(msg)
def test_format_and_red_level(self):
def test_format_and_red_level(self) -> None:
msg = 'I did another thing'
self.handler.red_level = logging.DEBUG
self.logger.debug(msg)
@@ -216,7 +217,7 @@ class ColoredStreamHandlerTest(unittest.TestCase):
class MemoryHandlerTest(unittest.TestCase):
"""Tests for certbot._internal.log.MemoryHandler"""
def setUp(self):
def setUp(self) -> None:
self.logger = logging.getLogger(__name__)
self.logger.setLevel(logging.DEBUG)
self.msg = 'hi there'
@@ -227,22 +228,22 @@ class MemoryHandlerTest(unittest.TestCase):
self.handler = MemoryHandler(self.stream_handler)
self.logger.addHandler(self.handler)
def tearDown(self):
def tearDown(self) -> None:
self.handler.close()
self.stream_handler.close()
def test_flush(self):
def test_flush(self) -> None:
self._test_log_debug()
self.handler.flush(force=True)
assert self.stream.getvalue() == self.msg + '\n'
def test_not_flushed(self):
def test_not_flushed(self) -> None:
# By default, logging.ERROR messages and higher are flushed
self.logger.critical(self.msg)
self.handler.flush()
assert self.stream.getvalue() == ''
def test_target_reset(self):
def test_target_reset(self) -> None:
self._test_log_debug()
new_stream = io.StringIO()
@@ -253,28 +254,28 @@ class MemoryHandlerTest(unittest.TestCase):
assert new_stream.getvalue() == self.msg + '\n'
new_stream_handler.close()
def _test_log_debug(self):
def _test_log_debug(self) -> None:
self.logger.debug(self.msg)
class TempHandlerTest(unittest.TestCase):
"""Tests for certbot._internal.log.TempHandler."""
def setUp(self):
def setUp(self) -> None:
self.closed = False
from certbot._internal.log import TempHandler
self.handler = TempHandler()
def tearDown(self):
def tearDown(self) -> None:
self.handler.close()
def test_permissions(self):
def test_permissions(self) -> None:
assert filesystem.check_permissions(self.handler.path, 0o600)
def test_delete(self):
def test_delete(self) -> None:
self.handler.close()
assert not os.path.exists(self.handler.path)
def test_no_delete(self):
def test_no_delete(self) -> None:
self.handler.emit(mock.MagicMock())
self.handler.close()
assert os.path.exists(self.handler.path)
@@ -284,12 +285,12 @@ class TempHandlerTest(unittest.TestCase):
class PreArgParseExceptHookTest(unittest.TestCase):
"""Tests for certbot._internal.log.pre_arg_parse_except_hook."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.log import pre_arg_parse_except_hook
return pre_arg_parse_except_hook(*args, **kwargs)
@mock.patch('certbot._internal.log.post_arg_parse_except_hook')
def test_it(self, mock_post_arg_parse_except_hook):
def test_it(self, mock_post_arg_parse_except_hook: MagicMock) -> None:
memory_handler = mock.MagicMock()
args = ('some', 'args',)
kwargs = {'some': 'kwargs'}
@@ -308,35 +309,35 @@ class PostArgParseExceptHookTest(unittest.TestCase):
from certbot._internal.log import post_arg_parse_except_hook
return post_arg_parse_except_hook(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
self.error_msg = 'test error message'
self.log_path = 'foo.log'
def test_base_exception(self):
def test_base_exception(self) -> None:
exc_type = BaseException
mock_logger, output = self._test_common(exc_type, debug=False)
self._assert_exception_logged(mock_logger.error, exc_type)
self._assert_logfile_output(output)
def test_debug(self):
def test_debug(self) -> None:
exc_type = ValueError
mock_logger, output = self._test_common(exc_type, debug=True)
self._assert_exception_logged(mock_logger.error, exc_type)
self._assert_logfile_output(output)
def test_quiet(self):
def test_quiet(self) -> None:
exc_type = ValueError
mock_logger, output = self._test_common(exc_type, debug=True, quiet=True)
self._assert_exception_logged(mock_logger.error, exc_type)
assert 'See the logfile' not in output
def test_custom_error(self):
def test_custom_error(self) -> None:
exc_type = errors.PluginError
mock_logger, output = self._test_common(exc_type, debug=False)
self._assert_exception_logged(mock_logger.debug, exc_type)
self._assert_quiet_output(mock_logger, output)
def test_acme_error(self):
def test_acme_error(self) -> None:
# Get an arbitrary error code
acme_code = next(iter(messages.ERROR_CODES))
@@ -349,18 +350,18 @@ class PostArgParseExceptHookTest(unittest.TestCase):
self._assert_quiet_output(mock_logger, output)
assert messages.ERROR_PREFIX not in output
def test_other_error(self):
def test_other_error(self) -> None:
exc_type = ValueError
mock_logger, output = self._test_common(exc_type, debug=False)
self._assert_exception_logged(mock_logger.debug, exc_type)
self._assert_quiet_output(mock_logger, output)
def test_keyboardinterrupt(self):
def test_keyboardinterrupt(self) -> None:
exc_type = KeyboardInterrupt
mock_logger, output = self._test_common(exc_type, debug=False)
mock_logger.error.assert_called_once_with('Exiting due to user request.')
def _test_common(self, error_type, debug, quiet=False):
def _test_common(self, error_type: Union[Type[BaseException], Callable], debug: bool, quiet: bool=False) -> Tuple[MagicMock, str]:
"""Returns the mocked logger and stderr output."""
mock_err = io.StringIO()
@@ -386,7 +387,7 @@ class PostArgParseExceptHookTest(unittest.TestCase):
output = mock_err.getvalue()
return mock_logger, output
def _assert_exception_logged(self, log_func, exc_type):
def _assert_exception_logged(self, log_func: MagicMock, exc_type: Type[BaseException]) -> None:
assert log_func.called
call_kwargs = log_func.call_args[1]
assert 'exc_info' in call_kwargs
@@ -395,11 +396,11 @@ class PostArgParseExceptHookTest(unittest.TestCase):
expected_exc_info = (exc_type, mock.ANY, mock.ANY)
assert actual_exc_info == expected_exc_info
def _assert_logfile_output(self, output):
def _assert_logfile_output(self, output: str) -> None:
assert 'See the logfile' in output
assert self.log_path in output
def _assert_quiet_output(self, mock_logger, output):
def _assert_quiet_output(self, mock_logger: MagicMock, output: str) -> None:
assert mock_logger.exception.called is False
assert mock_logger.debug.called
assert self.error_msg in output
@@ -412,7 +413,7 @@ class ExitWithAdviceTest(test_util.TempDirTestCase):
from certbot._internal.log import exit_with_advice
return exit_with_advice(*args, **kwargs)
def test_log_file(self):
def test_log_file(self) -> None:
log_file = os.path.join(self.tempdir, 'test.log')
open(log_file, 'w').close()
@@ -420,13 +421,13 @@ class ExitWithAdviceTest(test_util.TempDirTestCase):
assert 'logfiles' not in err_str
assert log_file in err_str
def test_log_dir(self):
def test_log_dir(self) -> None:
err_str = self._test_common(self.tempdir)
assert 'logfiles' in err_str
assert self.tempdir in err_str
# pylint: disable=inconsistent-return-statements
def _test_common(self, *args, **kwargs):
def _test_common(self, *args, **kwargs) -> str:
try:
self._call(*args, **kwargs)
except SystemExit as err:

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,9 @@ import pytz
from certbot import errors
from certbot.tests import util as test_util
from cryptography.x509.ocsp import OCSPCertStatus, OCSPResponseStatus
from typing import Optional, Union
from unittest.mock import MagicMock, Mock
out = """Missing = in header key=value
ocsp: Use -help for summary.
@@ -29,7 +32,7 @@ class OCSPTestOpenSSL(unittest.TestCase):
OCSP revocation tests using OpenSSL binary.
"""
def setUp(self):
def setUp(self) -> None:
from certbot import ocsp
with mock.patch('certbot.ocsp.subprocess.run') as mock_run:
with mock.patch('certbot.util.exe_exists') as mock_exists:
@@ -40,7 +43,7 @@ class OCSPTestOpenSSL(unittest.TestCase):
@mock.patch('certbot.ocsp.logger.info')
@mock.patch('certbot.ocsp.subprocess.run')
@mock.patch('certbot.util.exe_exists')
def test_init(self, mock_exists, mock_run, mock_log):
def test_init(self, mock_exists: MagicMock, mock_run: MagicMock, mock_log: MagicMock) -> None:
mock_run.return_value.stderr = out
mock_exists.return_value = True
@@ -64,7 +67,7 @@ class OCSPTestOpenSSL(unittest.TestCase):
@mock.patch('certbot.ocsp._determine_ocsp_server')
@mock.patch('certbot.ocsp.crypto_util.notAfter')
@mock.patch('certbot.util.run_script')
def test_ocsp_revoked(self, mock_run, mock_na, mock_determine):
def test_ocsp_revoked(self, mock_run: MagicMock, mock_na: MagicMock, mock_determine: MagicMock) -> None:
now = pytz.UTC.fromutc(datetime.utcnow())
cert_obj = mock.MagicMock()
cert_obj.cert_path = "x"
@@ -93,7 +96,7 @@ class OCSPTestOpenSSL(unittest.TestCase):
assert self.checker.ocsp_revoked(cert_obj) is False
assert mock_determine.call_count == count_before
def test_determine_ocsp_server(self):
def test_determine_ocsp_server(self) -> None:
cert_path = test_util.vector_path('ocsp_certificate.pem')
from certbot import ocsp
@@ -102,7 +105,7 @@ class OCSPTestOpenSSL(unittest.TestCase):
@mock.patch('certbot.ocsp.logger')
@mock.patch('certbot.util.run_script')
def test_translate_ocsp(self, mock_run, mock_log):
def test_translate_ocsp(self, mock_run: MagicMock, mock_log: MagicMock) -> None:
# pylint: disable=protected-access
mock_run.return_value = openssl_confused
from certbot import ocsp
@@ -130,7 +133,7 @@ class OSCPTestCryptography(unittest.TestCase):
OCSP revokation tests using Cryptography >= 2.4.0
"""
def setUp(self):
def setUp(self) -> None:
from certbot import ocsp
self.checker = ocsp.RevocationChecker()
self.cert_path = test_util.vector_path('ocsp_certificate.pem')
@@ -147,18 +150,18 @@ class OSCPTestCryptography(unittest.TestCase):
@mock.patch('certbot.ocsp._determine_ocsp_server')
@mock.patch('certbot.ocsp._check_ocsp_cryptography')
def test_ensure_cryptography_toggled(self, mock_check, mock_determine):
def test_ensure_cryptography_toggled(self, mock_check: MagicMock, mock_determine: MagicMock) -> None:
mock_determine.return_value = ('http://example.com', 'example.com')
self.checker.ocsp_revoked(self.cert_obj)
mock_check.assert_called_once_with(self.cert_path, self.chain_path, 'http://example.com', 10)
def test_revoke(self):
def test_revoke(self) -> None:
with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED, ocsp_lib.OCSPResponseStatus.SUCCESSFUL):
revoked = self.checker.ocsp_revoked(self.cert_obj)
assert revoked
def test_responder_is_issuer(self):
def test_responder_is_issuer(self) -> None:
issuer = x509.load_pem_x509_certificate(
test_util.load_vector('ocsp_issuer_certificate.pem'), default_backend())
@@ -182,7 +185,7 @@ class OSCPTestCryptography(unittest.TestCase):
assert mocks['mock_check'].call_args_list[1][0][0].public_numbers() == \
issuer.public_key().public_numbers()
def test_responder_is_authorized_delegate(self):
def test_responder_is_authorized_delegate(self) -> None:
issuer = x509.load_pem_x509_certificate(
test_util.load_vector('ocsp_issuer_certificate.pem'), default_backend())
responder = x509.load_pem_x509_certificate(
@@ -213,7 +216,7 @@ class OSCPTestCryptography(unittest.TestCase):
assert mocks['mock_check'].call_args_list[3][0][0].public_numbers() == \
responder.public_key().public_numbers()
def test_revoke_resiliency(self):
def test_revoke_resiliency(self) -> None:
# Server return an invalid HTTP response
with _ocsp_mock(ocsp_lib.OCSPCertStatus.UNKNOWN, ocsp_lib.OCSPResponseStatus.SUCCESSFUL,
http_status_code=400):
@@ -286,8 +289,8 @@ class OSCPTestCryptography(unittest.TestCase):
@contextlib.contextmanager
def _ocsp_mock(certificate_status, response_status,
http_status_code=200, check_signature_side_effect=None):
def _ocsp_mock(certificate_status: OCSPCertStatus, response_status: OCSPResponseStatus,
http_status_code: int=200, check_signature_side_effect: Optional[Union[InvalidSignature, AssertionError, UnsupportedAlgorithm]]=None) -> None:
with mock.patch('certbot.ocsp.ocsp.load_der_ocsp_response') as mock_response:
mock_response.return_value = _construct_mock_ocsp_response(
certificate_status, response_status)
@@ -304,7 +307,7 @@ def _ocsp_mock(certificate_status, response_status,
}
def _construct_mock_ocsp_response(certificate_status, response_status):
def _construct_mock_ocsp_response(certificate_status: OCSPCertStatus, response_status: OCSPResponseStatus) -> Mock:
cert = x509.load_pem_x509_certificate(
test_util.load_vector('ocsp_certificate.pem'), default_backend())
issuer = x509.load_pem_x509_certificate(

View File

@@ -16,6 +16,7 @@ from certbot.compat import filesystem
from certbot.compat import os
from certbot.tests import acme_util
from certbot.tests import util as test_util
from typing import Callable, Union
AUTH_KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
ACHALL = achallenges.KeyAuthorizationAnnotatedChallenge(
@@ -27,15 +28,15 @@ ACHALL = achallenges.KeyAuthorizationAnnotatedChallenge(
class NamespaceFunctionsTest(unittest.TestCase):
"""Tests for certbot.plugins.common.*_namespace functions."""
def test_option_namespace(self):
def test_option_namespace(self) -> None:
from certbot.plugins.common import option_namespace
assert "foo-" == option_namespace("foo")
def test_dest_namespace(self):
def test_dest_namespace(self) -> None:
from certbot.plugins.common import dest_namespace
assert "foo_" == dest_namespace("foo")
def test_dest_namespace_with_dashes(self):
def test_dest_namespace_with_dashes(self) -> None:
from certbot.plugins.common import dest_namespace
assert "foo_bar_" == dest_namespace("foo-bar")
@@ -43,7 +44,7 @@ class NamespaceFunctionsTest(unittest.TestCase):
class PluginTest(unittest.TestCase):
"""Test for certbot.plugins.common.Plugin."""
def setUp(self):
def setUp(self) -> None:
from certbot.plugins.common import Plugin
class MockPlugin(Plugin): # pylint: disable=missing-docstring
@@ -61,27 +62,27 @@ class PluginTest(unittest.TestCase):
self.config = mock.MagicMock()
self.plugin = MockPlugin(config=self.config, name="mock")
def test_init(self):
def test_init(self) -> None:
assert "mock" == self.plugin.name
assert self.config == self.plugin.config
def test_option_namespace(self):
def test_option_namespace(self) -> None:
assert "mock-" == self.plugin.option_namespace
def test_option_name(self):
def test_option_name(self) -> None:
assert "mock-foo_bar" == self.plugin.option_name("foo_bar")
def test_dest_namespace(self):
def test_dest_namespace(self) -> None:
assert "mock_" == self.plugin.dest_namespace
def test_dest(self):
def test_dest(self) -> None:
assert "mock_foo_bar" == self.plugin.dest("foo-bar")
assert "mock_foo_bar" == self.plugin.dest("foo_bar")
def test_conf(self):
def test_conf(self) -> None:
assert self.config.mock_foo_bar == self.plugin.conf("foo-bar")
def test_inject_parser_options(self):
def test_inject_parser_options(self) -> None:
parser = mock.MagicMock()
self.plugin_cls.inject_parser_options(parser, "mock")
# note that inject_parser_options doesn't check if dest has
@@ -89,7 +90,7 @@ class PluginTest(unittest.TestCase):
parser.add_argument.assert_called_once_with(
"--mock-foo-bar", dest="different_to_foo_bar", x=1, y=None)
def test_fallback_auth_hint(self):
def test_fallback_auth_hint(self) -> None:
assert "the mock plugin completed the required dns-01 challenges" in \
self.plugin.auth_hint([acme_util.DNS01_A, acme_util.DNS01_A])
assert "the mock plugin completed the required dns-01 and http-01 challenges" in \
@@ -100,7 +101,7 @@ class PluginTest(unittest.TestCase):
class InstallerTest(test_util.ConfigTestCase):
"""Tests for certbot.plugins.common.Installer."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
filesystem.mkdir(self.config.config_dir)
from certbot.tests.util import DummyInstaller
@@ -109,18 +110,18 @@ class InstallerTest(test_util.ConfigTestCase):
name="Installer")
self.reverter = self.installer.reverter
def test_add_to_real_checkpoint(self):
def test_add_to_real_checkpoint(self) -> None:
files = {"foo.bar", "baz.qux",}
save_notes = "foo bar baz qux"
self._test_wrapped_method("add_to_checkpoint", files, save_notes)
def test_add_to_real_checkpoint2(self):
def test_add_to_real_checkpoint2(self) -> None:
self._test_add_to_checkpoint_common(False)
def test_add_to_temporary_checkpoint(self):
def test_add_to_temporary_checkpoint(self) -> None:
self._test_add_to_checkpoint_common(True)
def _test_add_to_checkpoint_common(self, temporary):
def _test_add_to_checkpoint_common(self, temporary: bool) -> None:
files = {"foo.bar", "baz.qux",}
save_notes = "foo bar baz qux"
@@ -134,19 +135,19 @@ class InstallerTest(test_util.ConfigTestCase):
self._test_adapted_method(installer_func, reverter_func_name, files, save_notes)
def test_finalize_checkpoint(self):
def test_finalize_checkpoint(self) -> None:
self._test_wrapped_method("finalize_checkpoint", "foo")
def test_recovery_routine(self):
def test_recovery_routine(self) -> None:
self._test_wrapped_method("recovery_routine")
def test_revert_temporary_config(self):
def test_revert_temporary_config(self) -> None:
self._test_wrapped_method("revert_temporary_config")
def test_rollback_checkpoints(self):
def test_rollback_checkpoints(self) -> None:
self._test_wrapped_method("rollback_checkpoints", 42)
def _test_wrapped_method(self, name, *args, **kwargs):
def _test_wrapped_method(self, name: str, *args, **kwargs) -> None:
"""Test a wrapped reverter method.
:param str name: name of the method to test
@@ -157,8 +158,8 @@ class InstallerTest(test_util.ConfigTestCase):
installer_func = getattr(self.installer, name)
self._test_adapted_method(installer_func, name, *args, **kwargs)
def _test_adapted_method(self, installer_func,
reverter_func_name, *passed_args, **passed_kwargs):
def _test_adapted_method(self, installer_func: Union[Callable, functools.partial],
reverter_func_name: str, *passed_args, **passed_kwargs) -> None:
"""Test an adapted reverter method
:param callable installer_func: installer method to test
@@ -177,15 +178,15 @@ class InstallerTest(test_util.ConfigTestCase):
with pytest.raises(errors.PluginError):
installer_func(*passed_args, **passed_kwargs)
def test_install_ssl_dhparams(self):
def test_install_ssl_dhparams(self) -> None:
self.installer.install_ssl_dhparams()
assert os.path.isfile(self.installer.ssl_dhparams)
def _current_ssl_dhparams_hash(self):
def _current_ssl_dhparams_hash(self) -> str:
from certbot._internal.constants import SSL_DHPARAMS_SRC
return crypto_util.sha256sum(SSL_DHPARAMS_SRC)
def test_current_file_hash_in_all_hashes(self):
def test_current_file_hash_in_all_hashes(self) -> None:
from certbot._internal.constants import ALL_SSL_DHPARAMS_HASHES
assert self._current_ssl_dhparams_hash() in ALL_SSL_DHPARAMS_HASHES, \
"Constants.ALL_SSL_DHPARAMS_HASHES must be appended" \
@@ -195,7 +196,7 @@ class InstallerTest(test_util.ConfigTestCase):
class AddrTest(unittest.TestCase):
"""Tests for certbot.plugins.common.Addr."""
def setUp(self):
def setUp(self) -> None:
from certbot.plugins.common import Addr
self.addr1 = Addr.fromstring("192.168.1.1")
self.addr2 = Addr.fromstring("192.168.1.1:*")
@@ -206,7 +207,7 @@ class AddrTest(unittest.TestCase):
self.addr7 = Addr.fromstring("[fe00::1]:5")
self.addr8 = Addr.fromstring("[fe00:1:2:3:4:5:6:7:8:9]:8080")
def test_fromstring(self):
def test_fromstring(self) -> None:
assert self.addr1.get_addr() == "192.168.1.1"
assert self.addr1.get_port() == ""
assert self.addr2.get_addr() == "192.168.1.1"
@@ -227,7 +228,7 @@ class AddrTest(unittest.TestCase):
assert self.addr8.get_ipv6_exploded() == \
"fe00:1:2:3:4:5:6:7"
def test_str(self):
def test_str(self) -> None:
assert str(self.addr1) == "192.168.1.1"
assert str(self.addr2) == "192.168.1.1:*"
assert str(self.addr3) == "192.168.1.1:80"
@@ -235,7 +236,7 @@ class AddrTest(unittest.TestCase):
assert str(self.addr5) == "[fe00::1]:*"
assert str(self.addr6) == "[fe00::1]:80"
def test_get_addr_obj(self):
def test_get_addr_obj(self) -> None:
assert str(self.addr1.get_addr_obj("443")) == "192.168.1.1:443"
assert str(self.addr2.get_addr_obj("")) == "192.168.1.1"
assert str(self.addr1.get_addr_obj("*")) == "192.168.1.1:*"
@@ -243,7 +244,7 @@ class AddrTest(unittest.TestCase):
assert str(self.addr5.get_addr_obj("")) == "[fe00::1]"
assert str(self.addr4.get_addr_obj("*")) == "[fe00::1]:*"
def test_eq(self):
def test_eq(self) -> None:
assert self.addr1 == self.addr2.get_addr_obj("")
assert self.addr1 != self.addr2
assert self.addr1 != 3333
@@ -256,7 +257,7 @@ class AddrTest(unittest.TestCase):
assert self.addr4 == Addr.fromstring("[fe00:0::0:0:1]")
def test_set_inclusion(self):
def test_set_inclusion(self) -> None:
from certbot.plugins.common import Addr
set_a = {self.addr1, self.addr2}
addr1b = Addr.fromstring("192.168.1.1")
@@ -276,18 +277,18 @@ class AddrTest(unittest.TestCase):
class ChallengePerformerTest(unittest.TestCase):
"""Tests for certbot.plugins.common.ChallengePerformer."""
def setUp(self):
def setUp(self) -> None:
configurator = mock.MagicMock()
from certbot.plugins.common import ChallengePerformer
self.performer = ChallengePerformer(configurator)
def test_add_chall(self):
def test_add_chall(self) -> None:
self.performer.add_chall(ACHALL, 0)
assert 1 == len(self.performer.achalls)
assert [0] == self.performer.indices
def test_perform(self):
def test_perform(self) -> None:
with pytest.raises(NotImplementedError):
self.performer.perform()
@@ -295,7 +296,7 @@ class ChallengePerformerTest(unittest.TestCase):
class InstallVersionControlledFileTest(test_util.TempDirTestCase):
"""Tests for certbot.plugins.common.install_version_controlled_file."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.hashes = ["someotherhash"]
self.dest_path = os.path.join(self.tempdir, "options-ssl-dest.conf")
@@ -307,38 +308,38 @@ class InstallVersionControlledFileTest(test_util.TempDirTestCase):
f.write(path)
self.hashes.append(crypto_util.sha256sum(path))
def _call(self):
def _call(self) -> None:
from certbot.plugins.common import install_version_controlled_file
install_version_controlled_file(self.dest_path,
self.hash_path,
self.source_path,
self.hashes)
def _current_file_hash(self):
def _current_file_hash(self) -> str:
return crypto_util.sha256sum(self.source_path)
def _assert_current_file(self):
def _assert_current_file(self) -> None:
assert os.path.isfile(self.dest_path)
assert crypto_util.sha256sum(self.dest_path) == \
self._current_file_hash()
def test_no_file(self):
def test_no_file(self) -> None:
assert not os.path.isfile(self.dest_path)
self._call()
self._assert_current_file()
def test_current_file(self):
def test_current_file(self) -> None:
# 1st iteration installs the file, the 2nd checks if it needs updating
for _ in range(2):
self._call()
self._assert_current_file()
def test_prev_file_updates_to_current(self):
def test_prev_file_updates_to_current(self) -> None:
shutil.copyfile(self.old_path, self.dest_path)
self._call()
self._assert_current_file()
def test_manually_modified_current_file_does_not_update(self):
def test_manually_modified_current_file_does_not_update(self) -> None:
self._call()
with open(self.dest_path, "a") as mod_ssl_conf:
mod_ssl_conf.write("a new line for the wrong hash\n")
@@ -351,7 +352,7 @@ class InstallVersionControlledFileTest(test_util.TempDirTestCase):
assert crypto_util.sha256sum(self.dest_path) != \
self._current_file_hash()
def test_manually_modified_past_file_warns(self):
def test_manually_modified_past_file_warns(self) -> None:
with open(self.dest_path, "a") as mod_ssl_conf:
mod_ssl_conf.write("a new line for the wrong hash\n")
with open(self.hash_path, "w") as f:

View File

@@ -2,7 +2,7 @@
import functools
import string
import sys
from typing import List
from typing import Dict, Union, List
import unittest
from unittest import mock
@@ -11,9 +11,11 @@ import pytest
from certbot import errors
from certbot import interfaces
from certbot._internal.plugins import disco
from certbot._internal.plugins import null
from certbot._internal.plugins import standalone
from certbot._internal.plugins import webroot
from unittest.mock import MagicMock
EP_SA = pkg_resources.EntryPoint(
"sa", "certbot._internal.plugins.standalone",
@@ -28,7 +30,7 @@ EP_WR = pkg_resources.EntryPoint(
class PluginEntryPointTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.disco.PluginEntryPoint."""
def setUp(self):
def setUp(self) -> None:
self.ep1 = pkg_resources.EntryPoint(
"ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
self.ep1prim = pkg_resources.EntryPoint(
@@ -40,12 +42,9 @@ class PluginEntryPointTest(unittest.TestCase):
self.ep3 = pkg_resources.EntryPoint(
"ep3", "a.ep3", dist=mock.MagicMock(key="p3"))
from certbot._internal.plugins.disco import PluginEntryPoint
self.plugin_ep = PluginEntryPoint(EP_SA)
def test_entry_point_to_plugin_name_not_prefixed(self):
from certbot._internal.plugins.disco import PluginEntryPoint
self.plugin_ep = disco.PluginEntryPoint(EP_SA)
def test_entry_point_to_plugin_name_not_prefixed(self) -> None:
names = {
self.ep1: "ep1",
self.ep1prim: "ep1",
@@ -55,32 +54,32 @@ class PluginEntryPointTest(unittest.TestCase):
}
for entry_point, name in names.items():
assert name == PluginEntryPoint.entry_point_to_plugin_name(entry_point)
assert name == disco.PluginEntryPoint.entry_point_to_plugin_name(entry_point)
def test_description(self):
def test_description(self) -> None:
assert "server locally" in self.plugin_ep.description
def test_description_with_name(self):
def test_description_with_name(self) -> None:
self.plugin_ep.plugin_cls = mock.MagicMock(description="Desc")
assert "Desc (sa)" == self.plugin_ep.description_with_name
def test_long_description(self):
def test_long_description(self) -> None:
self.plugin_ep.plugin_cls = mock.MagicMock(
long_description="Long desc")
assert "Long desc" == self.plugin_ep.long_description
def test_long_description_nonexistent(self):
def test_long_description_nonexistent(self) -> None:
self.plugin_ep.plugin_cls = mock.MagicMock(
description="Long desc not found", spec=["description"])
assert "Long desc not found" == self.plugin_ep.long_description
def test_ifaces(self):
def test_ifaces(self) -> None:
assert self.plugin_ep.ifaces((interfaces.Authenticator,))
assert not self.plugin_ep.ifaces((interfaces.Installer,))
assert not self.plugin_ep.ifaces((
interfaces.Installer, interfaces.Authenticator))
def test__init__(self):
def test__init__(self) -> None:
assert self.plugin_ep.initialized is False
assert self.plugin_ep.prepared is False
assert self.plugin_ep.misconfigured is False
@@ -91,7 +90,7 @@ class PluginEntryPointTest(unittest.TestCase):
assert self.plugin_ep.plugin_cls is standalone.Authenticator
def test_init(self):
def test_init(self) -> None:
config = mock.MagicMock()
plugin = self.plugin_ep.init(config=config)
assert self.plugin_ep.initialized is True
@@ -107,7 +106,7 @@ class PluginEntryPointTest(unittest.TestCase):
assert self.plugin_ep.misconfigured is False
assert self.plugin_ep.available is False
def test_prepare(self):
def test_prepare(self) -> None:
config = mock.MagicMock()
self.plugin_ep.init(config=config)
self.plugin_ep.prepare()
@@ -117,7 +116,7 @@ class PluginEntryPointTest(unittest.TestCase):
# output doesn't matter that much, just test if it runs
str(self.plugin_ep)
def test_prepare_misconfigured(self):
def test_prepare_misconfigured(self) -> None:
plugin = mock.MagicMock()
plugin.prepare.side_effect = errors.MisconfigurationError
# pylint: disable=protected-access
@@ -128,7 +127,7 @@ class PluginEntryPointTest(unittest.TestCase):
assert isinstance(self.plugin_ep.problem, errors.MisconfigurationError)
assert self.plugin_ep.available
def test_prepare_no_installation(self):
def test_prepare_no_installation(self) -> None:
plugin = mock.MagicMock()
plugin.prepare.side_effect = errors.NoInstallationError
# pylint: disable=protected-access
@@ -138,7 +137,7 @@ class PluginEntryPointTest(unittest.TestCase):
assert self.plugin_ep.misconfigured is False
assert self.plugin_ep.available is False
def test_prepare_generic_plugin_error(self):
def test_prepare_generic_plugin_error(self) -> None:
plugin = mock.MagicMock()
plugin.prepare.side_effect = errors.PluginError
# pylint: disable=protected-access
@@ -148,35 +147,29 @@ class PluginEntryPointTest(unittest.TestCase):
assert self.plugin_ep.misconfigured is False
assert self.plugin_ep.available is False
def test_str(self):
def test_str(self) -> None:
output = str(self.plugin_ep)
assert "Authenticator" in output
assert "Installer" not in output
assert "Plugin" in output
def test_repr(self):
def test_repr(self) -> None:
assert "PluginEntryPoint#sa" == repr(self.plugin_ep)
class PluginsRegistryTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.disco.PluginsRegistry."""
@classmethod
def _create_new_registry(cls, plugins):
from certbot._internal.plugins.disco import PluginsRegistry
return PluginsRegistry(plugins)
def setUp(self):
def setUp(self) -> None:
self.plugin_ep = mock.MagicMock()
self.plugin_ep.name = "mock"
self.plugin_ep.__hash__.side_effect = TypeError
self.plugins = {self.plugin_ep.name: self.plugin_ep}
self.reg = self._create_new_registry(self.plugins)
self.reg = disco.PluginsRegistry(self.plugins)
self.ep1 = pkg_resources.EntryPoint(
"ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
def test_find_all(self):
from certbot._internal.plugins.disco import PluginsRegistry
def test_find_all(self) -> None:
with mock.patch("certbot._internal.plugins.disco.pkg_resources") as mock_pkg:
mock_pkg.iter_entry_points.side_effect = [
iter([EP_SA]), iter([EP_WR, self.ep1])
@@ -185,7 +178,7 @@ class PluginsRegistryTest(unittest.TestCase):
mock_load.side_effect = [
standalone.Authenticator, webroot.Authenticator,
null.Installer, null.Installer]
plugins = PluginsRegistry.find_all()
plugins = disco.PluginsRegistry.find_all()
assert plugins["sa"].plugin_cls is standalone.Authenticator
assert plugins["sa"].entry_point is EP_SA
assert plugins["wr"].plugin_cls is webroot.Authenticator
@@ -194,73 +187,73 @@ class PluginsRegistryTest(unittest.TestCase):
assert plugins["ep1"].entry_point is self.ep1
assert "p1:ep1" not in plugins
def test_getitem(self):
def test_getitem(self) -> None:
assert self.plugin_ep == self.reg["mock"]
def test_iter(self):
def test_iter(self) -> None:
assert ["mock"] == list(self.reg)
def test_len(self):
assert 0 == len(self._create_new_registry({}))
def test_len(self) -> None:
assert 0 == len(disco.PluginsRegistry({}))
assert 1 == len(self.reg)
def test_init(self):
def test_init(self) -> None:
self.plugin_ep.init.return_value = "baz"
assert ["baz"] == self.reg.init("bar")
self.plugin_ep.init.assert_called_once_with("bar")
def test_filter(self):
def test_filter(self) -> None:
assert self.plugins == \
self.reg.filter(lambda p_ep: p_ep.name.startswith("m"))
assert {} == self.reg.filter(lambda p_ep: p_ep.name.startswith("b"))
def test_ifaces(self):
def test_ifaces(self) -> None:
self.plugin_ep.ifaces.return_value = True
# pylint: disable=protected-access
assert self.plugins == self.reg.ifaces()._plugins
self.plugin_ep.ifaces.return_value = False
assert {} == self.reg.ifaces()._plugins
def test_prepare(self):
def test_prepare(self) -> None:
self.plugin_ep.prepare.return_value = "baz"
assert ["baz"] == self.reg.prepare()
self.plugin_ep.prepare.assert_called_once_with()
def test_prepare_order(self):
def test_prepare_order(self) -> None:
order: List[str] = []
plugins = {
c: mock.MagicMock(prepare=functools.partial(order.append, c))
for c in string.ascii_letters
}
reg = self._create_new_registry(plugins)
reg = disco.PluginsRegistry(plugins)
reg.prepare()
# order of prepare calls must be sorted to prevent deadlock
# caused by plugins acquiring locks during prepare
assert order == sorted(string.ascii_letters)
def test_available(self):
def test_available(self) -> None:
self.plugin_ep.available = True
# pylint: disable=protected-access
assert self.plugins == self.reg.available()._plugins
self.plugin_ep.available = False
assert {} == self.reg.available()._plugins
def test_find_init(self):
def test_find_init(self) -> None:
assert self.reg.find_init(mock.Mock()) is None
self.plugin_ep.initialized = True
assert self.reg.find_init(self.plugin_ep.init()) is self.plugin_ep
def test_repr(self):
def test_repr(self) -> None:
self.plugin_ep.__repr__ = lambda _: "PluginEntryPoint#mock"
assert "PluginsRegistry(PluginEntryPoint#mock)" == \
repr(self.reg)
def test_str(self):
assert "No plugins" == str(self._create_new_registry({}))
def test_str(self) -> None:
assert "No plugins" == str(disco.PluginsRegistry({}))
self.plugin_ep.__str__ = lambda _: "Mock"
assert "Mock" == str(self.reg)
plugins = {self.plugin_ep.name: self.plugin_ep, "foo": "Bar"}
reg = self._create_new_registry(plugins)
reg = disco.PluginsRegistry(plugins)
assert "Bar\n\nMock" == str(reg)

View File

@@ -15,7 +15,7 @@ class LexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconCl
class _FakeLexiconClient(dns_common_lexicon.LexiconClient):
pass
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.client = LexiconClientTest._FakeLexiconClient()

View File

@@ -15,39 +15,43 @@ from certbot.display import util as display_util
from certbot.plugins import dns_common
from certbot.plugins import dns_test_common
from certbot.tests import util as test_util
from certbot.tests.util import FreezableMock
from typing import Dict, Union
class _FakeDNSAuthenticator(dns_common.DNSAuthenticator):
_setup_credentials = mock.MagicMock()
_perform = mock.MagicMock()
_cleanup = mock.MagicMock()
def more_info(self) -> str: # pylint: disable=missing-docstring,no-self-use
return 'A fake authenticator for testing.'
class _FakeConfig:
fake_propagation_seconds = 0
fake_config_key = 1
fake_other_key = None
fake_file_path = None
class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthenticatorTest):
# pylint: disable=protected-access
class _FakeDNSAuthenticator(dns_common.DNSAuthenticator):
_setup_credentials = mock.MagicMock()
_perform = mock.MagicMock()
_cleanup = mock.MagicMock()
def more_info(self): # pylint: disable=missing-docstring,no-self-use
return 'A fake authenticator for testing.'
class _FakeConfig:
fake_propagation_seconds = 0
fake_config_key = 1
fake_other_key = None
fake_file_path = None
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config = DNSAuthenticatorTest._FakeConfig()
self.config = _FakeConfig()
self.auth = DNSAuthenticatorTest._FakeDNSAuthenticator(self.config, "fake")
self.auth = _FakeDNSAuthenticator(self.config, "fake")
@test_util.patch_display_util()
def test_perform(self, unused_mock_get_utility):
def test_perform(self, unused_mock_get_utility: FreezableMock) -> None:
self.auth.perform([self.achall])
self.auth._perform.assert_called_once_with(dns_test_common.DOMAIN, mock.ANY, mock.ANY)
def test_cleanup(self):
def test_cleanup(self) -> None:
self.auth._attempt_cleanup = True
self.auth.cleanup([self.achall])
@@ -55,7 +59,7 @@ class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthen
self.auth._cleanup.assert_called_once_with(dns_test_common.DOMAIN, mock.ANY, mock.ANY)
@test_util.patch_display_util()
def test_prompt(self, mock_get_utility):
def test_prompt(self, mock_get_utility: FreezableMock) -> None:
mock_display = mock_get_utility()
mock_display.input.side_effect = ((display_util.OK, "",),
(display_util.OK, "value",))
@@ -64,7 +68,7 @@ class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthen
assert self.auth.config.fake_other_key == "value"
@test_util.patch_display_util()
def test_prompt_canceled(self, mock_get_utility):
def test_prompt_canceled(self, mock_get_utility: FreezableMock) -> None:
mock_display = mock_get_utility()
mock_display.input.side_effect = ((display_util.CANCEL, "c",),)
@@ -72,7 +76,7 @@ class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthen
self.auth._configure("other_key", "")
@test_util.patch_display_util()
def test_prompt_file(self, mock_get_utility):
def test_prompt_file(self, mock_get_utility: FreezableMock) -> None:
path = os.path.join(self.tempdir, 'file.ini')
open(path, "wb").close()
@@ -86,14 +90,14 @@ class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthen
assert self.auth.config.fake_file_path == path
@test_util.patch_display_util()
def test_prompt_file_canceled(self, mock_get_utility):
def test_prompt_file_canceled(self, mock_get_utility: FreezableMock) -> None:
mock_display = mock_get_utility()
mock_display.directory_select.side_effect = ((display_util.CANCEL, "c",),)
with pytest.raises(errors.PluginError):
self.auth._configure_file("file_path", "")
def test_configure_credentials(self):
def test_configure_credentials(self) -> None:
path = os.path.join(self.tempdir, 'file.ini')
dns_test_common.write({"fake_test": "value"}, path)
setattr(self.config, "fake_credentials", path)
@@ -103,7 +107,7 @@ class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthen
assert credentials.conf("test") == "value"
@test_util.patch_display_util()
def test_prompt_credentials(self, mock_get_utility):
def test_prompt_credentials(self, mock_get_utility: FreezableMock) -> None:
bad_path = os.path.join(self.tempdir, 'bad-file.ini')
dns_test_common.write({"fake_other": "other_value"}, bad_path)
@@ -121,27 +125,28 @@ class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthen
credentials = self.auth._configure_credentials("credentials", "", {"test": ""})
assert credentials.conf("test") == "value"
def test_auth_hint(self):
def test_auth_hint(self) -> None:
assert 'try increasing --fake-propagation-seconds (currently 0 seconds).' in \
self.auth.auth_hint([mock.MagicMock()])
class _MockLoggingHandler(logging.Handler):
messages = None
def __init__(self, *args, **kwargs) -> None:
self.reset()
super().__init__(*args, **kwargs)
def emit(self, record: logging.LogRecord) -> None:
self.messages[record.levelname.lower()].append(record.getMessage())
def reset(self) -> None:
"""Allows the handler to be reset between tests."""
self.messages = collections.defaultdict(list)
class CredentialsConfigurationTest(test_util.TempDirTestCase):
class _MockLoggingHandler(logging.Handler):
messages = None
def __init__(self, *args, **kwargs):
self.reset()
super().__init__(*args, **kwargs)
def emit(self, record):
self.messages[record.levelname.lower()].append(record.getMessage())
def reset(self):
"""Allows the handler to be reset between tests."""
self.messages = collections.defaultdict(list)
def test_valid_file(self):
def test_valid_file(self) -> None:
path = os.path.join(self.tempdir, 'too-permissive-file.ini')
dns_test_common.write({"test": "value", "other": 1}, path)
@@ -150,14 +155,14 @@ class CredentialsConfigurationTest(test_util.TempDirTestCase):
assert "value" == credentials_configuration.conf("test")
assert "1" == credentials_configuration.conf("other")
def test_nonexistent_file(self):
def test_nonexistent_file(self) -> None:
path = os.path.join(self.tempdir, 'not-a-file.ini')
with pytest.raises(errors.PluginError):
dns_common.CredentialsConfiguration(path)
def test_valid_file_with_unsafe_permissions(self):
log = self._MockLoggingHandler()
def test_valid_file_with_unsafe_permissions(self) -> None:
log = _MockLoggingHandler()
dns_common.logger.addHandler(log)
path = os.path.join(self.tempdir, 'too-permissive-file.ini')
@@ -170,47 +175,47 @@ class CredentialsConfigurationTest(test_util.TempDirTestCase):
class CredentialsConfigurationRequireTest(test_util.TempDirTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.path = os.path.join(self.tempdir, 'file.ini')
def _write(self, values):
def _write(self, values: Dict[str, Union[str, int]]) -> None:
dns_test_common.write(values, self.path)
def test_valid(self):
def test_valid(self) -> None:
self._write({"test": "value", "other": 1})
credentials_configuration = dns_common.CredentialsConfiguration(self.path)
credentials_configuration.require({"test": "", "other": ""})
def test_valid_but_extra(self):
def test_valid_but_extra(self) -> None:
self._write({"test": "value", "other": 1})
credentials_configuration = dns_common.CredentialsConfiguration(self.path)
credentials_configuration.require({"test": ""})
def test_valid_empty(self):
def test_valid_empty(self) -> None:
self._write({})
credentials_configuration = dns_common.CredentialsConfiguration(self.path)
credentials_configuration.require({})
def test_missing(self):
def test_missing(self) -> None:
self._write({})
credentials_configuration = dns_common.CredentialsConfiguration(self.path)
with pytest.raises(errors.PluginError):
credentials_configuration.require({"test": ""})
def test_blank(self):
def test_blank(self) -> None:
self._write({"test": ""})
credentials_configuration = dns_common.CredentialsConfiguration(self.path)
with pytest.raises(errors.PluginError):
credentials_configuration.require({"test": ""})
def test_typo(self):
def test_typo(self) -> None:
self._write({"tets": "typo!"})
credentials_configuration = dns_common.CredentialsConfiguration(self.path)
@@ -220,15 +225,15 @@ class CredentialsConfigurationRequireTest(test_util.TempDirTestCase):
class DomainNameGuessTest(unittest.TestCase):
def test_simple_case(self):
def test_simple_case(self) -> None:
assert 'example.com' in \
dns_common.base_domain_name_guesses("example.com")
def test_sub_domain(self):
def test_sub_domain(self) -> None:
assert 'example.com' in \
dns_common.base_domain_name_guesses("foo.bar.baz.example.com")
def test_second_level_domain(self):
def test_second_level_domain(self) -> None:
assert 'example.co.uk' in \
dns_common.base_domain_name_guesses("foo.bar.baz.example.co.uk")

View File

@@ -8,18 +8,19 @@ import pytest
from certbot._internal.plugins import null
from certbot.plugins import enhancements
import certbot.tests.util as test_util
from certbot.tests.util import FreezableMock
class EnhancementTest(test_util.ConfigTestCase):
"""Tests for new style enhancements in certbot.plugins.enhancements"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.mockinstaller = mock.MagicMock(spec=enhancements.AutoHSTSEnhancement)
@test_util.patch_display_util()
def test_enhancement_enabled_enhancements(self, _):
def test_enhancement_enabled_enhancements(self, _: FreezableMock) -> None:
FAKEINDEX = [
{
"name": "autohsts",
@@ -38,20 +39,20 @@ class EnhancementTest(test_util.ConfigTestCase):
assert [i for i in enabled if i["name"] == "autohsts"]
assert [i for i in enabled if i["name"] == "somethingelse"]
def test_are_requested(self):
def test_are_requested(self) -> None:
assert len(list(enhancements.enabled_enhancements(self.config))) == 0
assert not enhancements.are_requested(self.config)
self.config.auto_hsts = True
assert len(list(enhancements.enabled_enhancements(self.config))) == 1
assert enhancements.are_requested(self.config)
def test_are_supported(self):
def test_are_supported(self) -> None:
self.config.auto_hsts = True
unsupported = null.Installer(self.config, "null")
assert enhancements.are_supported(self.config, self.mockinstaller)
assert not enhancements.are_supported(self.config, unsupported)
def test_enable(self):
def test_enable(self) -> None:
self.config.auto_hsts = True
domains = ["example.com", "www.example.com"]
lineage = "lineage"

View File

@@ -17,7 +17,7 @@ from certbot.tests import util as test_util
class AuthenticatorTest(test_util.TempDirTestCase):
"""Tests for certbot._internal.plugins.manual.Authenticator."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
get_display_patch = test_util.patch_display_util()
self.mock_get_display = get_display_patch.start()
@@ -45,25 +45,25 @@ class AuthenticatorTest(test_util.TempDirTestCase):
from certbot._internal.plugins.manual import Authenticator
self.auth = Authenticator(self.config, name='manual')
def test_prepare_no_hook_noninteractive(self):
def test_prepare_no_hook_noninteractive(self) -> None:
self.config.noninteractive_mode = True
with pytest.raises(errors.PluginError):
self.auth.prepare()
def test_prepare_bad_hook(self):
def test_prepare_bad_hook(self) -> None:
self.config.manual_auth_hook = os.path.abspath(os.sep) # is / on UNIX
self.config.validate_hooks = True
with pytest.raises(errors.HookCommandNotFound):
self.auth.prepare()
def test_more_info(self):
def test_more_info(self) -> None:
assert isinstance(self.auth.more_info(), str)
def test_get_chall_pref(self):
def test_get_chall_pref(self) -> None:
assert self.auth.get_chall_pref('example.org') == \
[challenges.HTTP01, challenges.DNS01]
def test_script_perform(self):
def test_script_perform(self) -> None:
self.config.manual_auth_hook = (
'{0} -c "'
'from certbot.compat import os;'
@@ -97,7 +97,7 @@ class AuthenticatorTest(test_util.TempDirTestCase):
needle = textwrap.indent(self.auth.env[self.achalls[i]]['CERTBOT_AUTH_OUTPUT'], ' ')
assert needle in args[0]
def test_manual_perform(self):
def test_manual_perform(self) -> None:
assert self.auth.perform(self.achalls) == \
[achall.response(achall.account_key) for achall in self.achalls]
@@ -107,7 +107,7 @@ class AuthenticatorTest(test_util.TempDirTestCase):
assert achall.validation(achall.account_key) in args[0]
assert kwargs['wrap'] is False
def test_cleanup(self):
def test_cleanup(self) -> None:
self.config.manual_auth_hook = ('{0} -c "import sys; sys.stdout.write(\'foo\')"'
.format(sys.executable))
self.config.manual_cleanup_hook = '# cleanup'
@@ -126,7 +126,7 @@ class AuthenticatorTest(test_util.TempDirTestCase):
else:
assert 'CERTBOT_TOKEN' not in os.environ
def test_auth_hint_hook(self):
def test_auth_hint_hook(self) -> None:
self.config.manual_auth_hook = '/bin/true'
assert self.auth.auth_hint([acme_util.DNS01_A, acme_util.HTTP01_A]) == \
'The Certificate Authority failed to verify the DNS TXT records and challenge ' \
@@ -138,7 +138,7 @@ class AuthenticatorTest(test_util.TempDirTestCase):
'--manual-auth-hook. Ensure that this hook is functioning correctly. Refer to ' \
'"certbot --help manual" and the Certbot User Guide.'
def test_auth_hint_no_hook(self):
def test_auth_hint_no_hook(self) -> None:
assert self.auth.auth_hint([acme_util.DNS01_A, acme_util.HTTP01_A]) == \
'The Certificate Authority failed to verify the manually created DNS TXT records ' \
'and challenge files. Ensure that you created these in the correct location, or ' \

View File

@@ -9,11 +9,11 @@ import pytest
class InstallerTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.null.Installer."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal.plugins.null import Installer
self.installer = Installer(config=mock.MagicMock(), name="null")
def test_it(self):
def test_it(self) -> None:
assert isinstance(self.installer.more_info(), str)
assert [] == self.installer.get_all_names()
assert [] == self.installer.supported_enhancements()

View File

@@ -1,6 +1,6 @@
"""Tests for letsencrypt.plugins.selection"""
import sys
from typing import List
from typing import Callable, Optional, Tuple, Type, Union, List
import unittest
from unittest import mock
@@ -12,12 +12,15 @@ from certbot._internal.display import obj as display_obj
from certbot._internal.plugins.disco import PluginsRegistry
from certbot.display import util as display_util
from certbot.tests import util as test_util
from certbot.configuration import NamespaceConfig
from certbot.tests.util import FreezableMock
from unittest.mock import MagicMock, Mock
class ConveniencePickPluginTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.selection.pick_*."""
def _test(self, fun, ifaces):
def _test(self, fun: Callable, ifaces: Tuple[Type[interfaces.Plugin], ...]) -> None:
config = mock.Mock()
default = mock.Mock()
plugins = mock.Mock()
@@ -28,15 +31,15 @@ class ConveniencePickPluginTest(unittest.TestCase):
mock_p.assert_called_once_with(
config, default, plugins, "Question?", ifaces)
def test_authenticator(self):
def test_authenticator(self) -> None:
from certbot._internal.plugins.selection import pick_authenticator
self._test(pick_authenticator, (interfaces.Authenticator,))
def test_installer(self):
def test_installer(self) -> None:
from certbot._internal.plugins.selection import pick_installer
self._test(pick_installer, (interfaces.Installer,))
def test_configurator(self):
def test_configurator(self) -> None:
from certbot._internal.plugins.selection import pick_configurator
self._test(pick_configurator,
(interfaces.Authenticator, interfaces.Installer))
@@ -45,31 +48,31 @@ class ConveniencePickPluginTest(unittest.TestCase):
class PickPluginTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.selection.pick_plugin."""
def setUp(self):
def setUp(self) -> None:
self.config = mock.Mock(noninteractive_mode=False)
self.default = None
self.reg = mock.MagicMock()
self.question = "Question?"
self.ifaces: List[interfaces.Plugin] = []
def _call(self):
def _call(self) -> Optional[str]:
from certbot._internal.plugins.selection import pick_plugin
return pick_plugin(self.config, self.default, self.reg,
self.question, self.ifaces)
def test_default_provided(self):
def test_default_provided(self) -> None:
self.default = "foo"
self._call()
assert 1 == self.reg.filter.call_count
def test_no_default(self):
def test_no_default(self) -> None:
self._call()
assert 1 == self.reg.visible().ifaces.call_count
def test_no_candidate(self):
def test_no_candidate(self) -> None:
assert self._call() is None
def test_single(self):
def test_single(self) -> None:
plugin_ep = mock.MagicMock()
plugin_ep.init.return_value = "foo"
plugin_ep.misconfigured = False
@@ -78,7 +81,7 @@ class PickPluginTest(unittest.TestCase):
"bar": plugin_ep}
assert "foo" == self._call()
def test_single_misconfigured(self):
def test_single_misconfigured(self) -> None:
plugin_ep = mock.MagicMock()
plugin_ep.init.return_value = "foo"
plugin_ep.misconfigured = True
@@ -87,7 +90,7 @@ class PickPluginTest(unittest.TestCase):
"bar": plugin_ep}
assert self._call() is None
def test_multiple(self):
def test_multiple(self) -> None:
plugin_ep = mock.MagicMock()
plugin_ep.init.return_value = "foo"
self.reg.visible().ifaces().available.return_value = {
@@ -100,7 +103,7 @@ class PickPluginTest(unittest.TestCase):
mock_choose.assert_called_once_with(
[plugin_ep, plugin_ep], self.question)
def test_choose_plugin_none(self):
def test_choose_plugin_none(self) -> None:
self.reg.visible().ifaces().available.return_value = {
"bar": None,
"baz": None,
@@ -114,7 +117,7 @@ class PickPluginTest(unittest.TestCase):
class ChoosePluginTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.selection.choose_plugin."""
def setUp(self):
def setUp(self) -> None:
display_obj.set_display(display_obj.FileDisplay(sys.stdout, False))
self.mock_apache = mock.Mock(
@@ -128,19 +131,19 @@ class ChoosePluginTest(unittest.TestCase):
self.mock_stand,
]
def _call(self):
def _call(self) -> Optional[Mock]:
from certbot._internal.plugins.selection import choose_plugin
return choose_plugin(self.plugins, "Question?")
@test_util.patch_display_util()
def test_selection(self, mock_util):
def test_selection(self, mock_util: FreezableMock) -> None:
mock_util().menu.side_effect = [(display_util.OK, 0),
(display_util.OK, 1)]
assert self.mock_stand == self._call()
assert mock_util().notification.call_count == 1
@test_util.patch_display_util()
def test_more_info(self, mock_util):
def test_more_info(self, mock_util: FreezableMock) -> None:
mock_util().menu.side_effect = [
(display_util.OK, 1),
]
@@ -148,7 +151,7 @@ class ChoosePluginTest(unittest.TestCase):
assert self.mock_stand == self._call()
@test_util.patch_display_util()
def test_no_choice(self, mock_util):
def test_no_choice(self, mock_util: FreezableMock) -> None:
mock_util().menu.return_value = (display_util.CANCEL, 0)
assert self._call() is None
@@ -156,7 +159,7 @@ class ChoosePluginTest(unittest.TestCase):
class GetUnpreparedInstallerTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.plugins.selection.get_unprepared_installer."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.mock_apache_fail_ep = mock.Mock(
description_with_name="afail")
@@ -171,26 +174,26 @@ class GetUnpreparedInstallerTest(test_util.ConfigTestCase):
"apache": self.mock_apache_ep,
})
def _call(self):
def _call(self) -> Optional[MagicMock]:
from certbot._internal.plugins.selection import get_unprepared_installer
return get_unprepared_installer(self.config, self.plugins)
def test_no_installer_defined(self):
def test_no_installer_defined(self) -> None:
self.config.configurator = None
assert self._call() is None
def test_no_available_installers(self):
def test_no_available_installers(self) -> None:
self.config.configurator = "apache"
self.plugins = PluginsRegistry({})
with pytest.raises(errors.PluginSelectionError):
self._call()
def test_get_plugin(self):
def test_get_plugin(self) -> None:
self.config.configurator = "apache"
installer = self._call()
assert installer is self.mock_apache_plugin
def test_multiple_installers_returned(self):
def test_multiple_installers_returned(self) -> None:
self.config.configurator = "apache"
# Two plugins with the same name
self.mock_apache_fail_ep.check_name = lambda name: name == "apache"
@@ -201,7 +204,7 @@ class GetUnpreparedInstallerTest(test_util.ConfigTestCase):
class TestChooseConfiguratorPlugins(unittest.TestCase):
"""Tests for certbot._internal.plugins.selection.choose_configurator_plugins."""
def _setupMockPlugin(self, name):
def _setupMockPlugin(self, name: str) -> Mock:
mock_ep = mock.Mock(
description_with_name=name)
mock_ep.check_name = lambda n: n == name
@@ -211,24 +214,24 @@ class TestChooseConfiguratorPlugins(unittest.TestCase):
mock_ep.misconfigured = False
return mock_ep
def _parseArgs(self, args):
def _parseArgs(self, args: str) -> NamespaceConfig:
from certbot import configuration
from certbot._internal import cli
return configuration.NamespaceConfig(
cli.prepare_and_parse_args(self.plugins, args.split()))
def setUp(self):
def setUp(self) -> None:
self.plugins = PluginsRegistry({
"nginx": self._setupMockPlugin("nginx"),
"apache": self._setupMockPlugin("apache"),
"manual": self._setupMockPlugin("manual"),
})
def _runWithArgs(self, args):
def _runWithArgs(self, args: str) -> Union[Tuple[MagicMock, MagicMock], Tuple[None, MagicMock]]:
from certbot._internal.plugins.selection import choose_configurator_plugins
return choose_configurator_plugins(self._parseArgs(args), self.plugins, "certonly")
def test_noninteractive_configurator(self):
def test_noninteractive_configurator(self) -> None:
# For certonly, setting either the nginx or apache configurators should
# return both an installer and authenticator
inst, auth = self._runWithArgs("certonly --nginx")
@@ -239,7 +242,7 @@ class TestChooseConfiguratorPlugins(unittest.TestCase):
assert inst.name == "apache"
assert auth.name == "apache"
def test_noninteractive_inst_arg(self):
def test_noninteractive_inst_arg(self) -> None:
# For certonly, if an installer arg is set, it should be returned as expected
inst, auth = self._runWithArgs("certonly -a nginx -i nginx")
assert inst.name == "nginx"

View File

@@ -2,7 +2,7 @@
import errno
import socket
import sys
from typing import Dict
from typing import List, Type, Dict
from typing import Set
from typing import Tuple
import unittest
@@ -18,32 +18,34 @@ from certbot import achallenges
from certbot import errors
from certbot.tests import acme_util
from certbot.tests import util as test_util
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
from certbot.tests.util import FreezableMock
class ServerManagerTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.standalone.ServerManager."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal.plugins.standalone import ServerManager
self.certs: Dict[bytes, Tuple[OpenSSL.crypto.PKey, OpenSSL.crypto.X509]] = {}
self.http_01_resources: Set[acme_standalone.HTTP01RequestHandler.HTTP01Resource] = {}
self.mgr = ServerManager(self.certs, self.http_01_resources)
def test_init(self):
def test_init(self) -> None:
assert self.mgr.certs is self.certs
assert self.mgr.http_01_resources is self.http_01_resources
def _test_run_stop(self, challenge_type):
def _test_run_stop(self, challenge_type: Type[challenges.HTTP01]) -> None:
server = self.mgr.run(port=0, challenge_type=challenge_type)
port = server.getsocknames()[0][1]
assert self.mgr.running() == {port: server}
self.mgr.stop(port=port)
assert self.mgr.running() == {}
def test_run_stop_http_01(self):
def test_run_stop_http_01(self) -> None:
self._test_run_stop(challenges.HTTP01)
def test_run_idempotent(self):
def test_run_idempotent(self) -> None:
server = self.mgr.run(port=0, challenge_type=challenges.HTTP01)
port = server.getsocknames()[0][1]
server2 = self.mgr.run(port=port, challenge_type=challenges.HTTP01)
@@ -52,7 +54,7 @@ class ServerManagerTest(unittest.TestCase):
self.mgr.stop(port)
assert self.mgr.running() == {}
def test_run_bind_error(self):
def test_run_bind_error(self) -> None:
some_server = socket.socket(socket.AF_INET6)
some_server.bind(("", 0))
port = some_server.getsockname()[1]
@@ -69,7 +71,7 @@ class ServerManagerTest(unittest.TestCase):
maybe_another_server.close()
def get_open_port():
def get_open_port() -> int:
"""Gets an open port number from the OS."""
open_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
open_socket.bind(("", 0))
@@ -81,21 +83,21 @@ def get_open_port():
class AuthenticatorTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.standalone.Authenticator."""
def setUp(self):
def setUp(self) -> None:
from certbot._internal.plugins.standalone import Authenticator
self.config = mock.MagicMock(http01_port=get_open_port())
self.auth = Authenticator(self.config, name="standalone")
self.auth.servers = mock.MagicMock()
def test_more_info(self):
def test_more_info(self) -> None:
assert isinstance(self.auth.more_info(), str)
def test_get_chall_pref(self):
def test_get_chall_pref(self) -> None:
assert self.auth.get_chall_pref(domain=None) == \
[challenges.HTTP01]
def test_perform(self):
def test_perform(self) -> None:
achalls = self._get_achalls()
response = self.auth.perform(achalls)
@@ -103,7 +105,7 @@ class AuthenticatorTest(unittest.TestCase):
assert response == expected
@test_util.patch_display_util()
def test_perform_eaddrinuse_retry(self, mock_get_utility):
def test_perform_eaddrinuse_retry(self, mock_get_utility: FreezableMock) -> None:
mock_utility = mock_get_utility()
encountered_errno = errno.EADDRINUSE
error = errors.StandaloneBindError(mock.MagicMock(errno=encountered_errno), -1)
@@ -115,7 +117,7 @@ class AuthenticatorTest(unittest.TestCase):
self._assert_correct_yesno_call(mock_yesno)
@test_util.patch_display_util()
def test_perform_eaddrinuse_no_retry(self, mock_get_utility):
def test_perform_eaddrinuse_no_retry(self, mock_get_utility: FreezableMock) -> None:
mock_utility = mock_get_utility()
mock_yesno = mock_utility.yesno
mock_yesno.return_value = False
@@ -125,28 +127,28 @@ class AuthenticatorTest(unittest.TestCase):
self._fail_perform(encountered_errno)
self._assert_correct_yesno_call(mock_yesno)
def _assert_correct_yesno_call(self, mock_yesno):
def _assert_correct_yesno_call(self, mock_yesno: FreezableMock) -> None:
yesno_args, yesno_kwargs = mock_yesno.call_args
assert "in use" in yesno_args[0]
assert not yesno_kwargs.get("default", True)
def test_perform_eacces(self):
def test_perform_eacces(self) -> None:
encountered_errno = errno.EACCES
with pytest.raises(errors.PluginError):
self._fail_perform(encountered_errno)
def test_perform_unexpected_socket_error(self):
def test_perform_unexpected_socket_error(self) -> None:
encountered_errno = errno.ENOTCONN
with pytest.raises(errors.StandaloneBindError):
self._fail_perform(encountered_errno)
def _fail_perform(self, encountered_errno):
def _fail_perform(self, encountered_errno: int):
error = errors.StandaloneBindError(mock.MagicMock(errno=encountered_errno), -1)
self.auth.servers.run.side_effect = error
self.auth.perform(self._get_achalls())
@classmethod
def _get_achalls(cls):
def _get_achalls(cls) -> List[KeyAuthorizationAnnotatedChallenge]:
domain = b'localhost'
key = jose.JWK.load(test_util.load_vector('rsa512_key.pem'))
http_01 = achallenges.KeyAuthorizationAnnotatedChallenge(
@@ -154,7 +156,7 @@ class AuthenticatorTest(unittest.TestCase):
return [http_01]
def test_cleanup(self):
def test_cleanup(self) -> None:
self.auth.servers.running.return_value = {
1: "server1",
2: "server2",
@@ -180,7 +182,7 @@ class AuthenticatorTest(unittest.TestCase):
"server1": set(), "server2": set()}
self.auth.servers.stop.assert_called_with(2)
def test_auth_hint(self):
def test_auth_hint(self) -> None:
self.config.http01_port = "80"
self.config.http01_address = None
assert "on port 80" in self.auth.auth_hint([])

View File

@@ -18,14 +18,14 @@ from certbot.tests import util as test_util
class PluginStorageTest(test_util.ConfigTestCase):
"""Test for certbot.plugins.storage.PluginStorage"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.plugin_cls = test_util.DummyInstaller
filesystem.mkdir(self.config.config_dir)
with mock.patch("certbot.reverter.util"):
self.plugin = self.plugin_cls(config=self.config, name="mockplugin")
def test_load_errors_cant_read(self):
def test_load_errors_cant_read(self) -> None:
with open(os.path.join(self.config.config_dir,
".pluginstorage.json"), "w") as fh:
fh.write("dummy")
@@ -40,7 +40,7 @@ class PluginStorageTest(test_util.ConfigTestCase):
with pytest.raises(errors.PluginStorageError):
self.plugin.storage._load() # pylint: disable=protected-access
def test_load_errors_empty(self):
def test_load_errors_empty(self) -> None:
with open(os.path.join(self.config.config_dir, ".pluginstorage.json"), "w") as fh:
fh.write('')
with mock.patch("certbot.plugins.storage.logger.debug") as mock_log:
@@ -52,7 +52,7 @@ class PluginStorageTest(test_util.ConfigTestCase):
assert mock_log.called
assert "no values loaded" in mock_log.call_args[0][0]
def test_load_errors_corrupted(self):
def test_load_errors_corrupted(self) -> None:
with open(os.path.join(self.config.config_dir,
".pluginstorage.json"), "w") as fh:
fh.write('invalid json')
@@ -63,7 +63,7 @@ class PluginStorageTest(test_util.ConfigTestCase):
corrupted.storage.fetch("value")
assert "is corrupted" in mock_log.call_args[0][0]
def test_save_errors_cant_serialize(self):
def test_save_errors_cant_serialize(self) -> None:
with mock.patch("certbot.plugins.storage.logger.error") as mock_log:
# Set data as something that can't be serialized
self.plugin.storage._initialized = True # pylint: disable=protected-access
@@ -73,7 +73,7 @@ class PluginStorageTest(test_util.ConfigTestCase):
self.plugin.storage.save()
assert "Could not serialize" in mock_log.call_args[0][0]
def test_save_errors_unable_to_write_file(self):
def test_save_errors_unable_to_write_file(self) -> None:
mock_open = mock.mock_open()
mock_open.side_effect = IOError
with mock.patch("certbot.compat.filesystem.open", mock_open):
@@ -85,12 +85,12 @@ class PluginStorageTest(test_util.ConfigTestCase):
self.plugin.storage.save()
assert "Could not write" in mock_log.call_args[0][0]
def test_save_uninitialized(self):
def test_save_uninitialized(self) -> None:
with mock.patch("certbot.reverter.util"):
with pytest.raises(errors.PluginStorageError):
self.plugin_cls(self.config, "x").storage.save()
def test_namespace_isolation(self):
def test_namespace_isolation(self) -> None:
with mock.patch("certbot.reverter.util"):
plugin1 = self.plugin_cls(self.config, "first")
plugin2 = self.plugin_cls(self.config, "second")
@@ -101,7 +101,7 @@ class PluginStorageTest(test_util.ConfigTestCase):
plugin2.storage.fetch("first")
assert plugin1.storage.fetch("first_key") == "first_value"
def test_saved_state(self):
def test_saved_state(self) -> None:
self.plugin.storage.put("testkey", "testvalue")
# Write to disk
self.plugin.storage.save()

View File

@@ -5,9 +5,10 @@ from unittest import mock
import pytest
from certbot.compat import os
from unittest.mock import MagicMock
def test_get_prefix():
def test_get_prefix() -> None:
from certbot.plugins.util import get_prefixes
assert get_prefixes('/a/b/c') == \
[os.path.normpath(path) for path in ['/a/b/c', '/a/b', '/a', '/']]
@@ -16,7 +17,7 @@ def test_get_prefix():
@mock.patch("certbot.plugins.util.logger.debug")
def test_path_surgery(mock_debug):
def test_path_surgery(mock_debug: MagicMock) -> None:
from certbot.plugins.util import path_surgery
all_path = {"PATH": "/usr/local/bin:/bin/:/usr/sbin/:/usr/local/sbin/"}
with mock.patch.dict('os.environ', all_path):

View File

@@ -22,6 +22,8 @@ from certbot.compat import os
from certbot.display import util as display_util
from certbot.tests import acme_util
from certbot.tests import util as test_util
from certbot.tests.util import FreezableMock
from unittest.mock import MagicMock
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
@@ -32,7 +34,7 @@ class AuthenticatorTest(unittest.TestCase):
achall = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY)
def setUp(self):
def setUp(self) -> None:
from certbot._internal.plugins.webroot import Authenticator
# On Linux directories created by tempfile.mkdtemp inherit their permissions from their
@@ -53,24 +55,24 @@ class AuthenticatorTest(unittest.TestCase):
webroot_map={"thing.com": self.path})
self.auth = Authenticator(self.config, "webroot")
def tearDown(self):
def tearDown(self) -> None:
shutil.rmtree(self.path)
def test_more_info(self):
def test_more_info(self) -> None:
more_info = self.auth.more_info()
assert isinstance(more_info, str)
assert self.path in more_info
def test_add_parser_arguments(self):
def test_add_parser_arguments(self) -> None:
add = mock.MagicMock()
self.auth.add_parser_arguments(add)
assert 2 == add.call_count
def test_prepare(self):
def test_prepare(self) -> None:
self.auth.prepare() # shouldn't raise any exceptions
@test_util.patch_display_util()
def test_webroot_from_list(self, mock_get_utility):
def test_webroot_from_list(self, mock_get_utility: FreezableMock) -> None:
self.config.webroot_path = []
self.config.webroot_map = {"otherthing.com": self.path}
mock_display = mock_get_utility()
@@ -88,7 +90,7 @@ class AuthenticatorTest(unittest.TestCase):
@unittest.skipIf(filesystem.POSIX_MODE, reason='Test specific to Windows')
@test_util.patch_display_util()
def test_webconfig_file_generate_and_cleanup(self, mock_get_utility):
def test_webconfig_file_generate_and_cleanup(self, mock_get_utility: FreezableMock) -> None:
mock_display = mock_get_utility()
mock_display.menu.return_value = (display_util.OK, 1,)
@@ -99,7 +101,7 @@ class AuthenticatorTest(unittest.TestCase):
@unittest.skipIf(filesystem.POSIX_MODE, reason='Test specific to Windows')
@test_util.patch_display_util()
def test_foreign_webconfig_file_handling(self, mock_get_utility):
def test_foreign_webconfig_file_handling(self, mock_get_utility: FreezableMock) -> None:
mock_display = mock_get_utility()
mock_display.menu.return_value = (display_util.OK, 1,)
@@ -116,7 +118,7 @@ class AuthenticatorTest(unittest.TestCase):
assert webconfig_hash not in _WEB_CONFIG_SHA256SUMS
@unittest.skipIf(filesystem.POSIX_MODE, reason='Test specific to Windows')
def test_foreign_webconfig_multiple_domains(self):
def test_foreign_webconfig_multiple_domains(self) -> None:
# Covers bug https://github.com/certbot/certbot/issues/9091
achall_2 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(challenges.HTTP01(token=b"bingo"), "pending"),
@@ -132,7 +134,7 @@ class AuthenticatorTest(unittest.TestCase):
self.auth.perform([self.achall, achall_2])
@test_util.patch_display_util()
def test_webroot_from_list_help_and_cancel(self, mock_get_utility):
def test_webroot_from_list_help_and_cancel(self, mock_get_utility: FreezableMock) -> None:
self.config.webroot_path = []
self.config.webroot_map = {"otherthing.com": self.path}
@@ -148,7 +150,7 @@ class AuthenticatorTest(unittest.TestCase):
for webroot in self.config.webroot_map.values())
@test_util.patch_display_util()
def test_new_webroot(self, mock_get_utility):
def test_new_webroot(self, mock_get_utility: FreezableMock) -> None:
self.config.webroot_path = []
self.config.webroot_map = {"something.com": self.path}
@@ -163,7 +165,7 @@ class AuthenticatorTest(unittest.TestCase):
assert self.config.webroot_map[self.achall.domain] == self.path
@test_util.patch_display_util()
def test_new_webroot_empty_map_cancel(self, mock_get_utility):
def test_new_webroot_empty_map_cancel(self, mock_get_utility: FreezableMock) -> None:
self.config.webroot_path = []
self.config.webroot_map = {}
@@ -174,13 +176,13 @@ class AuthenticatorTest(unittest.TestCase):
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
def test_perform_missing_root(self):
def test_perform_missing_root(self) -> None:
self.config.webroot_path = None
self.config.webroot_map = {}
with pytest.raises(errors.PluginError):
self.auth.perform([])
def test_perform_reraises_other_errors(self):
def test_perform_reraises_other_errors(self) -> None:
self.auth.full_path = os.path.join(self.path, "null")
permission_canary = os.path.join(self.path, "rnd")
with open(permission_canary, "w") as f:
@@ -197,12 +199,12 @@ class AuthenticatorTest(unittest.TestCase):
filesystem.chmod(self.path, 0o700)
@mock.patch("certbot._internal.plugins.webroot.filesystem.copy_ownership_and_apply_mode")
def test_failed_chown(self, mock_ownership):
def test_failed_chown(self, mock_ownership: MagicMock) -> None:
mock_ownership.side_effect = OSError(errno.EACCES, "msg")
self.auth.perform([self.achall]) # exception caught and logged
@test_util.patch_display_util()
def test_perform_new_webroot_not_in_map(self, mock_get_utility):
def test_perform_new_webroot_not_in_map(self, mock_get_utility: FreezableMock) -> None:
new_webroot = tempfile.mkdtemp()
self.config.webroot_path = []
self.config.webroot_map = {"whatever.com": self.path}
@@ -216,7 +218,7 @@ class AuthenticatorTest(unittest.TestCase):
self.auth.perform([achall])
assert self.config.webroot_map[achall.domain] == new_webroot
def test_perform_permissions(self):
def test_perform_permissions(self) -> None:
self.auth.prepare()
# Remove exec bit from permission check, so that it
@@ -232,7 +234,7 @@ class AuthenticatorTest(unittest.TestCase):
assert filesystem.has_same_ownership(self.validation_path, self.path)
def test_perform_cleanup(self):
def test_perform_cleanup(self) -> None:
self.auth.prepare()
responses = self.auth.perform([self.achall])
assert 1 == len(responses)
@@ -248,7 +250,7 @@ class AuthenticatorTest(unittest.TestCase):
assert not os.path.exists(self.root_challenge_path)
assert not os.path.exists(self.partial_root_challenge_path)
def test_perform_cleanup_existing_dirs(self):
def test_perform_cleanup_existing_dirs(self) -> None:
filesystem.mkdir(self.partial_root_challenge_path)
self.auth.prepare()
self.auth.perform([self.achall])
@@ -258,7 +260,7 @@ class AuthenticatorTest(unittest.TestCase):
assert not os.path.exists(self.validation_path)
assert not os.path.exists(self.root_challenge_path)
def test_perform_cleanup_multiple_challenges(self):
def test_perform_cleanup_multiple_challenges(self) -> None:
bingo_achall = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.HTTP01(token=b"bingo"), "pending"),
@@ -276,7 +278,7 @@ class AuthenticatorTest(unittest.TestCase):
assert not os.path.exists(self.validation_path)
assert not os.path.exists(self.root_challenge_path)
def test_cleanup_leftovers(self):
def test_cleanup_leftovers(self) -> None:
self.auth.prepare()
self.auth.perform([self.achall])
@@ -290,7 +292,7 @@ class AuthenticatorTest(unittest.TestCase):
os.rmdir(leftover_path)
@mock.patch('certbot.compat.os.rmdir')
def test_cleanup_failure(self, mock_rmdir):
def test_cleanup_failure(self, mock_rmdir: MagicMock) -> None:
self.auth.prepare()
self.auth.perform([self.achall])
@@ -309,7 +311,7 @@ class WebrootActionTest(unittest.TestCase):
achall = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY)
def setUp(self):
def setUp(self) -> None:
from certbot._internal.plugins.webroot import Authenticator
self.path = tempfile.mkdtemp()
self.parser = argparse.ArgumentParser()
@@ -317,31 +319,31 @@ class WebrootActionTest(unittest.TestCase):
action="append", default=[])
Authenticator.inject_parser_options(self.parser, "webroot")
def test_webroot_map_action(self):
def test_webroot_map_action(self) -> None:
args = self.parser.parse_args(
["--webroot-map", json.dumps({'thing.com': self.path})])
assert args.webroot_map["thing.com"] == self.path
def test_domain_before_webroot(self):
def test_domain_before_webroot(self) -> None:
args = self.parser.parse_args(
"-d {0} -w {1}".format(self.achall.domain, self.path).split())
config = self._get_config_after_perform(args)
assert config.webroot_map[self.achall.domain] == self.path
def test_domain_before_webroot_error(self):
def test_domain_before_webroot_error(self) -> None:
with pytest.raises(errors.PluginError):
self.parser.parse_args("-d foo -w bar -w baz".split())
with pytest.raises(errors.PluginError):
self.parser.parse_args("-d foo -w bar -d baz -w qux".split())
def test_multiwebroot(self):
def test_multiwebroot(self) -> None:
args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format(
self.path, self.achall.domain, tempfile.mkdtemp()).split())
assert args.webroot_map[self.achall.domain] == self.path
config = self._get_config_after_perform(args)
assert config.webroot_map[self.achall.domain] == self.path
def test_webroot_map_partial_without_perform(self):
def test_webroot_map_partial_without_perform(self) -> None:
# This test acknowledges the fact that webroot_map content will be partial if webroot
# plugin perform method is not invoked (corner case when all auths are already valid).
# To not be a problem, the webroot_path must always been conserved during renew.
@@ -354,7 +356,7 @@ class WebrootActionTest(unittest.TestCase):
assert args.webroot_map == {self.achall.domain: self.path}
assert args.webroot_path == [self.path, other_webroot_path]
def _get_config_after_perform(self, config):
def _get_config_after_perform(self, config: argparse.Namespace) -> argparse.Namespace:
from certbot._internal.plugins.webroot import Authenticator
auth = Authenticator(config, "webroot")
auth.perform([self.achall])

View File

@@ -11,11 +11,14 @@ from certbot import configuration
from certbot import errors
from certbot._internal import storage
import certbot.tests.util as test_util
from certbot.tests.util import FreezableMock
from typing import List
from unittest.mock import MagicMock
class RenewalTest(test_util.ConfigTestCase):
@mock.patch('certbot._internal.cli.set_by_cli')
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
rc_path = test_util.make_lineage(
self.config.config_dir, 'sample-renewal-ancient.conf')
@@ -31,7 +34,7 @@ class RenewalTest(test_util.ConfigTestCase):
assert config.webroot_path == ['/var/www/']
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_webroot_params_conservation(self, mock_set_by_cli):
def test_webroot_params_conservation(self, mock_set_by_cli: MagicMock) -> None:
# For more details about why this test is important, see:
# certbot._internal.plugins.webroot_test::
# WebrootActionTest::test_webroot_map_partial_without_perform
@@ -55,7 +58,7 @@ class RenewalTest(test_util.ConfigTestCase):
assert self.config.webroot_path == ['/var/www/test']
@mock.patch('certbot._internal.renewal._avoid_reuse_key_conflicts')
def test_reuse_key_renewal_params(self, unused_mock_avoid_reuse_conflicts):
def test_reuse_key_renewal_params(self, unused_mock_avoid_reuse_conflicts: MagicMock) -> None:
self.config.elliptic_curve = 'INVALID_VALUE'
self.config.reuse_key = True
self.config.dry_run = True
@@ -76,7 +79,7 @@ class RenewalTest(test_util.ConfigTestCase):
assert self.config.elliptic_curve == 'secp256r1'
@mock.patch('certbot._internal.renewal._avoid_reuse_key_conflicts')
def test_reuse_ec_key_renewal_params(self, unused_mock_avoid_reuse_conflicts):
def test_reuse_ec_key_renewal_params(self, unused_mock_avoid_reuse_conflicts: MagicMock) -> None:
self.config.elliptic_curve = 'INVALID_CURVE'
self.config.reuse_key = True
self.config.dry_run = True
@@ -101,7 +104,7 @@ class RenewalTest(test_util.ConfigTestCase):
assert self.config.elliptic_curve == 'secp256r1'
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_new_key(self, mock_set_by_cli):
def test_new_key(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
# When renewing with both reuse_key and new_key, the key should be regenerated,
# the key type, key parameters and reuse_key should be kept.
@@ -130,7 +133,7 @@ class RenewalTest(test_util.ConfigTestCase):
@mock.patch('certbot._internal.renewal.hooks.renew_hook')
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_reuse_key_conflicts(self, mock_set_by_cli, unused_mock_renew_hook):
def test_reuse_key_conflicts(self, mock_set_by_cli: MagicMock, unused_mock_renew_hook: MagicMock) -> None:
mock_set_by_cli.return_value = False
# When renewing with reuse_key and a conflicting key parameter (size, curve)
@@ -162,7 +165,7 @@ class RenewalTest(test_util.ConfigTestCase):
@test_util.patch_display_util()
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_remove_deprecated_config_elements(self, mock_set_by_cli, unused_mock_get_utility):
def test_remove_deprecated_config_elements(self, mock_set_by_cli: MagicMock, unused_mock_get_utility: FreezableMock) -> None:
mock_set_by_cli.return_value = False
config = configuration.NamespaceConfig(self.config)
config.certname = "sample-renewal-deprecated-option"
@@ -181,25 +184,25 @@ class RenewalTest(test_util.ConfigTestCase):
class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
"""Tests for certbot._internal.renewal.restore_required_config_elements."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.renewal import restore_required_config_elements
return restore_required_config_elements(*args, **kwargs)
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_allow_subset_of_names_success(self, mock_set_by_cli):
def test_allow_subset_of_names_success(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
self._call(self.config, {'allow_subset_of_names': 'True'})
assert self.config.allow_subset_of_names is True
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_allow_subset_of_names_failure(self, mock_set_by_cli):
def test_allow_subset_of_names_failure(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
renewalparams = {'allow_subset_of_names': 'maybe'}
with pytest.raises(errors.Error):
self._call(self.config, renewalparams)
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_pref_challs_list(self, mock_set_by_cli):
def test_pref_challs_list(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
renewalparams = {'pref_challs': 'http-01, dns'.split(',')}
self._call(self.config, renewalparams)
@@ -207,7 +210,7 @@ class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
assert self.config.pref_challs == expected
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_pref_challs_str(self, mock_set_by_cli):
def test_pref_challs_str(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
renewalparams = {'pref_challs': 'dns'}
self._call(self.config, renewalparams)
@@ -215,27 +218,27 @@ class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
assert self.config.pref_challs == expected
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_pref_challs_failure(self, mock_set_by_cli):
def test_pref_challs_failure(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
renewalparams = {'pref_challs': 'finding-a-shrubbery'}
with pytest.raises(errors.Error):
self._call(self.config, renewalparams)
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_must_staple_success(self, mock_set_by_cli):
def test_must_staple_success(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
self._call(self.config, {'must_staple': 'True'})
assert self.config.must_staple is True
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_must_staple_failure(self, mock_set_by_cli):
def test_must_staple_failure(self, mock_set_by_cli: MagicMock) -> None:
mock_set_by_cli.return_value = False
renewalparams = {'must_staple': 'maybe'}
with pytest.raises(errors.Error):
self._call(self.config, renewalparams)
@mock.patch('certbot._internal.renewal.cli.set_by_cli')
def test_ancient_server_renewal_conf(self, mock_set_by_cli):
def test_ancient_server_renewal_conf(self, mock_set_by_cli: MagicMock) -> None:
from certbot._internal import constants
self.config.server = None
mock_set_by_cli.return_value = False
@@ -245,30 +248,30 @@ class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
class DescribeResultsTest(unittest.TestCase):
"""Tests for certbot._internal.renewal._renew_describe_results."""
def setUp(self):
def setUp(self) -> None:
self.patchers = {
'log_error': mock.patch('certbot._internal.renewal.logger.error'),
'notify': mock.patch('certbot._internal.renewal.display_util.notify')}
self.mock_notify = self.patchers['notify'].start()
self.mock_error = self.patchers['log_error'].start()
def tearDown(self):
def tearDown(self) -> None:
for patch in self.patchers.values():
patch.stop()
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot._internal.renewal import _renew_describe_results
_renew_describe_results(*args, **kwargs)
def _assert_success_output(self, lines):
def _assert_success_output(self, lines: List[str]) -> None:
self.mock_notify.assert_has_calls([mock.call(l) for l in lines])
def test_no_renewal_attempts(self):
def test_no_renewal_attempts(self) -> None:
self._call(mock.MagicMock(dry_run=True), [], [], [], [])
self._assert_success_output(['No simulated renewals were attempted.'])
def test_successful_renewal(self):
def test_successful_renewal(self) -> None:
self._call(mock.MagicMock(dry_run=False), ['good.pem'], None, None, None)
self._assert_success_output([
'\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -',
@@ -277,7 +280,7 @@ class DescribeResultsTest(unittest.TestCase):
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -',
])
def test_failed_renewal(self):
def test_failed_renewal(self) -> None:
self._call(mock.MagicMock(dry_run=False), [], ['bad.pem'], [], [])
self._assert_success_output([
'\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -',
@@ -288,7 +291,7 @@ class DescribeResultsTest(unittest.TestCase):
mock.call(' bad.pem (failure)'),
])
def test_all_renewal(self):
def test_all_renewal(self) -> None:
self._call(mock.MagicMock(dry_run=True),
['good.pem', 'good2.pem'], ['bad.pem', 'bad2.pem'],
['foo.pem expires on 123'], ['errored.conf'])

View File

@@ -10,12 +10,14 @@ from certbot._internal import main
from certbot._internal import updater
from certbot.plugins import enhancements
import certbot.tests.util as test_util
from certbot.tests.util import FreezableMock
from unittest.mock import MagicMock
class RenewUpdaterTest(test_util.ConfigTestCase):
"""Tests for interfaces.RenewDeployer and interfaces.GenericUpdater"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.generic_updater = mock.MagicMock(spec=interfaces.GenericUpdater)
self.generic_updater.restart = mock.MagicMock()
@@ -26,7 +28,7 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
@mock.patch('certbot._internal.plugins.selection.choose_configurator_plugins')
@mock.patch('certbot._internal.plugins.selection.get_unprepared_installer')
@test_util.patch_display_util()
def test_server_updates(self, _, mock_geti, mock_select, mock_getsave):
def test_server_updates(self, _: FreezableMock, mock_geti: MagicMock, mock_select: MagicMock, mock_getsave: MagicMock) -> None:
mock_getsave.return_value = mock.MagicMock()
mock_generic_updater = self.generic_updater
@@ -43,14 +45,14 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
assert mock_generic_updater.generic_updates.call_count == 1
assert mock_generic_updater.restart.called is False
def test_renew_deployer(self):
def test_renew_deployer(self) -> None:
lineage = mock.MagicMock()
mock_deployer = self.renew_deployer
updater.run_renewal_deployer(self.config, lineage, mock_deployer)
assert mock_deployer.renew_deploy.called_with(lineage)
@mock.patch("certbot._internal.updater.logger.debug")
def test_updater_skip_dry_run(self, mock_log):
def test_updater_skip_dry_run(self, mock_log: MagicMock) -> None:
self.config.dry_run = True
updater.run_generic_updaters(self.config, None, None)
assert mock_log.called
@@ -58,7 +60,7 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
"Skipping updaters in dry-run mode."
@mock.patch("certbot._internal.updater.logger.debug")
def test_deployer_skip_dry_run(self, mock_log):
def test_deployer_skip_dry_run(self, mock_log: MagicMock) -> None:
self.config.dry_run = True
updater.run_renewal_deployer(self.config, None, None)
assert mock_log.called
@@ -66,32 +68,32 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
"Skipping renewal deployer in dry-run mode."
@mock.patch('certbot._internal.plugins.selection.get_unprepared_installer')
def test_enhancement_updates(self, mock_geti):
def test_enhancement_updates(self, mock_geti: MagicMock) -> None:
mock_geti.return_value = self.mockinstaller
updater.run_generic_updaters(self.config, mock.MagicMock(), None)
assert self.mockinstaller.update_autohsts.called
assert self.mockinstaller.update_autohsts.call_count == 1
def test_enhancement_deployer(self):
def test_enhancement_deployer(self) -> None:
updater.run_renewal_deployer(self.config, mock.MagicMock(),
self.mockinstaller)
assert self.mockinstaller.deploy_autohsts.called
@mock.patch('certbot._internal.plugins.selection.get_unprepared_installer')
def test_enhancement_updates_not_called(self, mock_geti):
def test_enhancement_updates_not_called(self, mock_geti: MagicMock) -> None:
self.config.disable_renew_updates = True
mock_geti.return_value = self.mockinstaller
updater.run_generic_updaters(self.config, mock.MagicMock(), None)
assert self.mockinstaller.update_autohsts.called is False
def test_enhancement_deployer_not_called(self):
def test_enhancement_deployer_not_called(self) -> None:
self.config.disable_renew_updates = True
updater.run_renewal_deployer(self.config, mock.MagicMock(),
self.mockinstaller)
assert self.mockinstaller.deploy_autohsts.called is False
@mock.patch('certbot._internal.plugins.selection.get_unprepared_installer')
def test_enhancement_no_updater(self, mock_geti):
def test_enhancement_no_updater(self, mock_geti: MagicMock) -> None:
FAKEINDEX = [
{
"name": "Test",
@@ -106,7 +108,7 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
updater.run_generic_updaters(self.config, mock.MagicMock(), None)
assert self.mockinstaller.update_autohsts.called is False
def test_enhancement_no_deployer(self):
def test_enhancement_no_deployer(self) -> None:
FAKEINDEX = [
{
"name": "Test",

View File

@@ -12,11 +12,13 @@ import pytest
from certbot import errors
from certbot.compat import os
from certbot.tests import util as test_util
from typing import List, Set, Tuple
from unittest.mock import MagicMock
class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
"""Test the Reverter Class."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
from certbot.reverter import Reverter
@@ -28,7 +30,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
tup = setup_test_files()
self.config1, self.config2, self.dir1, self.dir2, self.sets = tup
def tearDown(self):
def tearDown(self) -> None:
shutil.rmtree(self.config.work_dir)
shutil.rmtree(self.dir1)
shutil.rmtree(self.dir2)
@@ -36,7 +38,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
logging.disable(logging.NOTSET)
@mock.patch("certbot.reverter.Reverter._read_and_append")
def test_no_change(self, mock_read):
def test_no_change(self, mock_read: MagicMock) -> None:
mock_read.side_effect = OSError("cannot even")
try:
self.reverter.add_to_checkpoint(self.sets[0], "save1")
@@ -49,7 +51,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
x = f.read()
assert "No changes" in x
def test_basic_add_to_temp_checkpoint(self):
def test_basic_add_to_temp_checkpoint(self) -> None:
# These shouldn't conflict even though they are both named config.txt
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
self.reverter.add_to_temp_checkpoint(self.sets[1], "save2")
@@ -63,13 +65,13 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
assert get_filepaths(self.config.temp_checkpoint_dir) == \
"{0}\n{1}\n".format(self.config1, self.config2)
def test_add_to_checkpoint_copy_failure(self):
def test_add_to_checkpoint_copy_failure(self) -> None:
with mock.patch("certbot.reverter.shutil.copy2") as mock_copy2:
mock_copy2.side_effect = IOError("bad copy")
with pytest.raises(errors.ReverterError):
self.reverter.add_to_checkpoint(self.sets[0], "save1")
def test_checkpoint_conflict(self):
def test_checkpoint_conflict(self) -> None:
"""Make sure that checkpoint errors are thrown appropriately."""
config3 = os.path.join(self.dir1, "config3.txt")
self.reverter.register_file_creation(True, config3)
@@ -88,7 +90,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
with pytest.raises(errors.ReverterError):
self.reverter.add_to_checkpoint({config3}, "invalid save")
def test_multiple_saves_and_temp_revert(self):
def test_multiple_saves_and_temp_revert(self) -> None:
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
update_file(self.config1, "updated-directive")
self.reverter.add_to_temp_checkpoint(self.sets[0], "save2-updated dir")
@@ -97,7 +99,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
self.reverter.revert_temporary_config()
assert read_in(self.config1) == "directive-dir1"
def test_multiple_registration_fail_and_revert(self):
def test_multiple_registration_fail_and_revert(self) -> None:
config3 = os.path.join(self.dir1, "config3.txt")
update_file(config3, "Config3")
@@ -117,7 +119,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
assert not os.path.isfile(config3)
assert not os.path.isfile(config4)
def test_multiple_registration_same_file(self):
def test_multiple_registration_same_file(self) -> None:
self.reverter.register_file_creation(True, self.config1)
self.reverter.register_file_creation(True, self.config1)
self.reverter.register_file_creation(True, self.config1)
@@ -127,19 +129,19 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
assert len(files) == 1
def test_register_file_creation_write_error(self):
def test_register_file_creation_write_error(self) -> None:
m_open = mock.mock_open()
with mock.patch("certbot.reverter.open", m_open, create=True):
m_open.side_effect = OSError("bad open")
with pytest.raises(errors.ReverterError):
self.reverter.register_file_creation(True, self.config1)
def test_bad_registration(self):
def test_bad_registration(self) -> None:
# Made this mistake and want to make sure it doesn't happen again...
with pytest.raises(errors.ReverterError):
self.reverter.register_file_creation("filepath")
def test_register_undo_command(self):
def test_register_undo_command(self) -> None:
coms = [
["a2dismod", "ssl"],
["a2dismod", "rewrite"],
@@ -153,7 +155,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
for a_com, com in zip(act_coms, coms):
assert a_com == com
def test_bad_register_undo_command(self):
def test_bad_register_undo_command(self) -> None:
m_open = mock.mock_open()
with mock.patch("certbot.reverter.open", m_open, create=True):
m_open.side_effect = OSError("bad open")
@@ -161,7 +163,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
self.reverter.register_undo_command(True, ["command"])
@mock.patch("certbot.util.run_script")
def test_run_undo_commands(self, mock_run):
def test_run_undo_commands(self, mock_run: MagicMock) -> None:
mock_run.side_effect = ["", errors.SubprocessError]
coms = [
["invalid_command"],
@@ -174,7 +176,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
assert mock_run.call_count == 2
def test_recovery_routine_in_progress_failure(self):
def test_recovery_routine_in_progress_failure(self) -> None:
self.reverter.add_to_checkpoint(self.sets[0], "perm save")
# pylint: disable=protected-access
@@ -183,7 +185,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
with pytest.raises(errors.ReverterError):
self.reverter.recovery_routine()
def test_recover_checkpoint_revert_temp_failures(self):
def test_recover_checkpoint_revert_temp_failures(self) -> None:
mock_recover = mock.MagicMock(
side_effect=errors.ReverterError("e"))
@@ -196,7 +198,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
with pytest.raises(errors.ReverterError):
self.reverter.revert_temporary_config()
def test_recover_checkpoint_rollback_failure(self):
def test_recover_checkpoint_rollback_failure(self) -> None:
mock_recover = mock.MagicMock(
side_effect=errors.ReverterError("e"))
# pylint: disable=protected-access
@@ -208,7 +210,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
with pytest.raises(errors.ReverterError):
self.reverter.rollback_checkpoints(1)
def test_recover_checkpoint_copy_failure(self):
def test_recover_checkpoint_copy_failure(self) -> None:
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
with mock.patch("certbot.reverter.shutil.copy2") as mock_copy2:
@@ -216,7 +218,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
with pytest.raises(errors.ReverterError):
self.reverter.revert_temporary_config()
def test_recover_checkpoint_rm_failure(self):
def test_recover_checkpoint_rm_failure(self) -> None:
self.reverter.add_to_temp_checkpoint(self.sets[0], "temp save")
with mock.patch("certbot.reverter.shutil.rmtree") as mock_rmtree:
@@ -225,20 +227,20 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
self.reverter.revert_temporary_config()
@mock.patch("certbot.reverter.logger.warning")
def test_recover_checkpoint_missing_new_files(self, mock_warn):
def test_recover_checkpoint_missing_new_files(self, mock_warn: MagicMock) -> None:
self.reverter.register_file_creation(
True, os.path.join(self.dir1, "missing_file.txt"))
self.reverter.revert_temporary_config()
assert mock_warn.call_count == 1
@mock.patch("certbot.reverter.os.remove")
def test_recover_checkpoint_remove_failure(self, mock_remove):
def test_recover_checkpoint_remove_failure(self, mock_remove: MagicMock) -> None:
self.reverter.register_file_creation(True, self.config1)
mock_remove.side_effect = OSError("Can't remove")
with pytest.raises(errors.ReverterError):
self.reverter.revert_temporary_config()
def test_recovery_routine_temp_and_perm(self):
def test_recovery_routine_temp_and_perm(self) -> None:
# Register a new perm checkpoint file
config3 = os.path.join(self.dir1, "config3.txt")
self.reverter.register_file_creation(False, config3)
@@ -274,7 +276,7 @@ class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
class TestFullCheckpointsReverter(test_util.ConfigTestCase):
"""Tests functions having to deal with full checkpoints."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
from certbot.reverter import Reverter
@@ -286,14 +288,14 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
tup = setup_test_files()
self.config1, self.config2, self.dir1, self.dir2, self.sets = tup
def tearDown(self):
def tearDown(self) -> None:
shutil.rmtree(self.config.work_dir)
shutil.rmtree(self.dir1)
shutil.rmtree(self.dir2)
logging.disable(logging.NOTSET)
def test_rollback_improper_inputs(self):
def test_rollback_improper_inputs(self) -> None:
with pytest.raises(errors.ReverterError):
self.reverter.rollback_checkpoints("-1")
with pytest.raises(errors.ReverterError):
@@ -301,7 +303,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
with pytest.raises(errors.ReverterError):
self.reverter.rollback_checkpoints("one")
def test_rollback_finalize_checkpoint_valid_inputs(self):
def test_rollback_finalize_checkpoint_valid_inputs(self) -> None:
config3 = self._setup_three_checkpoints()
@@ -330,12 +332,12 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
self.reverter.rollback_checkpoints(1)
assert read_in(self.config1) == "directive-dir1"
def test_finalize_checkpoint_no_in_progress(self):
def test_finalize_checkpoint_no_in_progress(self) -> None:
# No need to warn for this... just make sure there are no errors.
self.reverter.finalize_checkpoint("No checkpoint...")
@mock.patch("certbot.reverter.shutil.move")
def test_finalize_checkpoint_cannot_title(self, mock_move):
def test_finalize_checkpoint_cannot_title(self, mock_move: MagicMock) -> None:
self.reverter.add_to_checkpoint(self.sets[0], "perm save")
mock_move.side_effect = OSError("cannot move")
@@ -343,7 +345,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
self.reverter.finalize_checkpoint("Title")
@mock.patch("certbot.reverter.filesystem.replace")
def test_finalize_checkpoint_no_rename_directory(self, mock_replace):
def test_finalize_checkpoint_no_rename_directory(self, mock_replace: MagicMock) -> None:
self.reverter.add_to_checkpoint(self.sets[0], "perm save")
mock_replace.side_effect = OSError
@@ -352,7 +354,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
self.reverter.finalize_checkpoint("Title")
@mock.patch("certbot.reverter.logger")
def test_rollback_too_many(self, mock_logger):
def test_rollback_too_many(self, mock_logger: MagicMock) -> None:
# Test no exist warning...
self.reverter.rollback_checkpoints(1)
assert mock_logger.warning.call_count == 1
@@ -363,7 +365,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
self.reverter.rollback_checkpoints(4)
assert mock_logger.warning.call_count == 1
def test_multi_rollback(self):
def test_multi_rollback(self) -> None:
config3 = self._setup_three_checkpoints()
self.reverter.rollback_checkpoints(3)
@@ -371,7 +373,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
assert read_in(self.config2) == "directive-dir2"
assert not os.path.isfile(config3)
def _setup_three_checkpoints(self):
def _setup_three_checkpoints(self) -> str:
"""Generate some finalized checkpoints."""
# Checkpoint1 - config1
self.reverter.add_to_checkpoint(self.sets[0], "first save")
@@ -400,7 +402,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
return config3
def setup_test_files():
def setup_test_files() -> Tuple[str, str, str, str, List[Set[str]]]:
"""Setup sample configuration files."""
dir1 = tempfile.mkdtemp("dir1")
dir2 = tempfile.mkdtemp("dir2")
@@ -418,34 +420,34 @@ def setup_test_files():
return config1, config2, dir1, dir2, sets
def get_save_notes(dire):
def get_save_notes(dire: str) -> str:
"""Read save notes"""
return read_in(os.path.join(dire, "CHANGES_SINCE"))
def get_filepaths(dire):
def get_filepaths(dire: str) -> str:
"""Get Filepaths"""
return read_in(os.path.join(dire, "FILEPATHS"))
def get_new_files(dire):
def get_new_files(dire: str) -> List[str]:
"""Get new files."""
return read_in(os.path.join(dire, "NEW_FILES")).splitlines()
def get_undo_commands(dire):
def get_undo_commands(dire: str) -> List[List[str]]:
"""Get new files."""
with open(os.path.join(dire, "COMMANDS")) as csvfile:
return list(csv.reader(csvfile))
def read_in(path):
def read_in(path: str) -> str:
"""Read in a file, return the str"""
with open(path, "r") as file_fd:
return file_fd.read()
def update_file(filename, string):
def update_file(filename: str, string: str) -> None:
"""Update a file with a new value."""
with open(filename, "w") as file_fd:
file_fd.write(string)

View File

@@ -13,21 +13,24 @@ import pytz
import certbot
from certbot import errors
from certbot._internal.storage import ALL_FOUR
from certbot._internal.storage import RenewableCert, ALL_FOUR
from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
from certbot.configuration import NamespaceConfig
from typing import Dict, Optional, Union
from unittest.mock import MagicMock
CERT = test_util.load_cert('cert_512.pem')
def unlink_all(rc_object):
def unlink_all(rc_object: RenewableCert) -> None:
"""Unlink all four items associated with this RenewableCert."""
for kind in ALL_FOUR:
os.unlink(getattr(rc_object, kind))
def fill_with_sample_data(rc_object):
def fill_with_sample_data(rc_object: RenewableCert) -> None:
"""Put dummy data into all four files of this RenewableCert."""
for kind in ALL_FOUR:
with open(getattr(rc_object, kind), "w") as f:
@@ -37,16 +40,16 @@ def fill_with_sample_data(rc_object):
class RelevantValuesTest(unittest.TestCase):
"""Tests for certbot._internal.storage.relevant_values."""
def setUp(self):
def setUp(self) -> None:
self.values = {"server": "example.org", "key_type": "rsa"}
def _call(self, *args, **kwargs):
def _call(self, *args, **kwargs) -> Dict[str, Union[str, bool, int]]:
from certbot._internal.storage import relevant_values
return relevant_values(*args, **kwargs)
@mock.patch("certbot._internal.cli.option_was_set")
@mock.patch("certbot._internal.plugins.disco.PluginsRegistry.find_all")
def test_namespace(self, mock_find_all, mock_option_was_set):
def test_namespace(self, mock_find_all: MagicMock, mock_option_was_set: MagicMock) -> None:
mock_find_all.return_value = ["certbot-foo:bar"]
mock_option_was_set.return_value = True
@@ -54,7 +57,7 @@ class RelevantValuesTest(unittest.TestCase):
assert self._call(self.values.copy()) == self.values
@mock.patch("certbot._internal.cli.option_was_set")
def test_option_set(self, mock_option_was_set):
def test_option_set(self, mock_option_was_set: MagicMock) -> None:
mock_option_was_set.return_value = True
self.values["allow_subset_of_names"] = True
@@ -66,7 +69,7 @@ class RelevantValuesTest(unittest.TestCase):
assert self._call(self.values) == expected_relevant_values
@mock.patch("certbot._internal.cli.option_was_set")
def test_option_unset(self, mock_option_was_set):
def test_option_unset(self, mock_option_was_set: MagicMock) -> None:
mock_option_was_set.return_value = False
expected_relevant_values = self.values.copy()
@@ -75,7 +78,7 @@ class RelevantValuesTest(unittest.TestCase):
assert self._call(self.values) == expected_relevant_values
@mock.patch("certbot._internal.cli.set_by_cli")
def test_deprecated_item(self, unused_mock_set_by_cli):
def test_deprecated_item(self, unused_mock_set_by_cli: MagicMock) -> None:
# deprecated items should never be relevant to store
expected_relevant_values = self.values.copy()
self.values["manual_public_ip_logging_ok"] = None
@@ -94,7 +97,7 @@ class BaseRenewableCertTest(test_util.ConfigTestCase):
"""
def setUp(self):
def setUp(self) -> None:
from certbot._internal import storage
super().setUp()
@@ -132,7 +135,7 @@ class BaseRenewableCertTest(test_util.ConfigTestCase):
check.return_value = True
self.test_rc = storage.RenewableCert(config_file.filename, self.config)
def _write_out_kind(self, kind, ver, value=None):
def _write_out_kind(self, kind: str, ver: int, value: Optional[bytes]=None) -> None:
link = getattr(self.test_rc, kind)
if os.path.lexists(link):
os.unlink(link)
@@ -144,7 +147,7 @@ class BaseRenewableCertTest(test_util.ConfigTestCase):
if kind == "privkey":
filesystem.chmod(link, 0o600)
def _write_out_ex_kinds(self):
def _write_out_ex_kinds(self) -> None:
for kind in ALL_FOUR:
self._write_out_kind(kind, 12)
self._write_out_kind(kind, 11)
@@ -153,13 +156,13 @@ class BaseRenewableCertTest(test_util.ConfigTestCase):
class RenewableCertTests(BaseRenewableCertTest):
"""Tests for certbot._internal.storage."""
def test_initialization(self):
def test_initialization(self) -> None:
assert self.test_rc.lineagename == "example.org"
for kind in ALL_FOUR:
assert getattr(self.test_rc, kind) == os.path.join(
self.config.config_dir, "live", "example.org", kind + ".pem")
def test_renewal_bad_config(self):
def test_renewal_bad_config(self) -> None:
"""Test that the RenewableCert constructor will complain if
the renewal configuration file doesn't end in ".conf"
@@ -174,7 +177,7 @@ class RenewableCertTests(BaseRenewableCertTest):
with pytest.raises(errors.CertStorageError):
storage.RenewableCert("fun", self.config)
def test_renewal_incomplete_config(self):
def test_renewal_incomplete_config(self) -> None:
"""Test that the RenewableCert constructor will complain if
the renewal configuration file is missing a required file element."""
from certbot._internal import storage
@@ -188,7 +191,7 @@ class RenewableCertTests(BaseRenewableCertTest):
with pytest.raises(errors.CertStorageError):
storage.RenewableCert(config.filename, self.config)
def test_no_renewal_version(self):
def test_no_renewal_version(self) -> None:
from certbot._internal import storage
self._write_out_ex_kinds()
@@ -198,7 +201,7 @@ class RenewableCertTests(BaseRenewableCertTest):
storage.RenewableCert(self.config_file.filename, self.config)
assert mock_logger.warning.called is False
def test_renewal_newer_version(self):
def test_renewal_newer_version(self) -> None:
from certbot._internal import storage
self._write_out_ex_kinds()
@@ -210,7 +213,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert mock_logger.info.called
assert "version" in mock_logger.info.call_args[0][0]
def test_consistent(self):
def test_consistent(self) -> None:
# pylint: disable=protected-access
oldcert = self.test_rc.cert
self.test_rc.cert = "relative/path"
@@ -252,7 +255,7 @@ class RenewableCertTests(BaseRenewableCertTest):
f.write("wrongly-named fullchain")
assert not self.test_rc._consistent()
def test_current_target(self):
def test_current_target(self) -> None:
# Relative path logic
self._write_out_kind("cert", 17)
assert os.path.samefile(self.test_rc.current_target("cert"),
@@ -270,7 +273,7 @@ class RenewableCertTests(BaseRenewableCertTest):
"example.org",
"cert17.pem"))
def test_current_version(self):
def test_current_version(self) -> None:
for ver in (1, 5, 10, 20):
self._write_out_kind("cert", ver)
os.unlink(self.test_rc.cert)
@@ -278,10 +281,10 @@ class RenewableCertTests(BaseRenewableCertTest):
"cert10.pem"), self.test_rc.cert)
assert self.test_rc.current_version("cert") == 10
def test_no_current_version(self):
def test_no_current_version(self) -> None:
assert self.test_rc.current_version("cert") is None
def test_latest_and_next_versions(self):
def test_latest_and_next_versions(self) -> None:
for ver in range(1, 6):
for kind in ALL_FOUR:
self._write_out_kind(kind, ver)
@@ -305,7 +308,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert self.test_rc.next_free_version() == 18
@mock.patch("certbot._internal.storage.logger")
def test_ensure_deployed(self, mock_logger):
def test_ensure_deployed(self, mock_logger: MagicMock) -> None:
mock_update = self.test_rc.update_all_links_to = mock.Mock()
mock_has_pending = self.test_rc.has_pending_deployment = mock.Mock()
self.test_rc.latest_common_version = mock.Mock()
@@ -321,7 +324,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert mock_logger.warning.call_count == 1
def test_update_link_to(self):
def test_update_link_to(self) -> None:
for ver in range(1, 6):
for kind in ALL_FOUR:
self._write_out_kind(kind, ver)
@@ -340,7 +343,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert os.path.basename(filesystem.readlink(self.test_rc.chain)) == \
"chain3000.pem"
def test_version(self):
def test_version(self) -> None:
self._write_out_kind("cert", 12)
# TODO: We should probably test that the directory is still the
# same, but it's tricky because we can get an absolute
@@ -348,7 +351,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert "cert8.pem" == \
os.path.basename(self.test_rc.version("cert", 8))
def test_update_all_links_to_success(self):
def test_update_all_links_to_success(self) -> None:
for ver in range(1, 6):
for kind in ALL_FOUR:
self._write_out_kind(kind, ver)
@@ -360,7 +363,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert ver == self.test_rc.current_version(kind)
assert self.test_rc.latest_common_version() == 5
def test_update_all_links_to_partial_failure(self):
def test_update_all_links_to_partial_failure(self) -> None:
def unlink_or_raise(path, real_unlink=os.unlink):
# pylint: disable=missing-docstring
basename = os.path.basename(path)
@@ -377,7 +380,7 @@ class RenewableCertTests(BaseRenewableCertTest):
for kind in ALL_FOUR:
assert self.test_rc.current_version(kind) == 12
def test_update_all_links_to_full_failure(self):
def test_update_all_links_to_full_failure(self) -> None:
def unlink_or_raise(path, real_unlink=os.unlink):
# pylint: disable=missing-docstring
if "fullchain" in os.path.basename(path):
@@ -393,7 +396,7 @@ class RenewableCertTests(BaseRenewableCertTest):
for kind in ALL_FOUR:
assert self.test_rc.current_version(kind) == 11
def test_has_pending_deployment(self):
def test_has_pending_deployment(self) -> None:
for ver in range(1, 6):
for kind in ALL_FOUR:
self._write_out_kind(kind, ver)
@@ -407,7 +410,7 @@ class RenewableCertTests(BaseRenewableCertTest):
else:
assert not self.test_rc.has_pending_deployment()
def test_names(self):
def test_names(self) -> None:
# Trying the current version
self._write_out_kind("cert", 12, test_util.load_vector("cert-san_512.pem"))
@@ -421,7 +424,7 @@ class RenewableCertTests(BaseRenewableCertTest):
@mock.patch("certbot._internal.storage.cli")
@mock.patch("certbot._internal.storage.datetime")
def test_time_interval_judgments(self, mock_datetime, mock_cli):
def test_time_interval_judgments(self, mock_datetime: MagicMock, mock_cli: MagicMock) -> None:
"""Test should_autorenew() on the basis of expiry time windows."""
test_cert = test_util.load_vector("cert_512.pem")
@@ -464,7 +467,7 @@ class RenewableCertTests(BaseRenewableCertTest):
self.test_rc.configuration["renew_before_expiry"] = interval
assert self.test_rc.should_autorenew() == result
def test_autorenewal_is_enabled(self):
def test_autorenewal_is_enabled(self) -> None:
self.test_rc.configuration["renewalparams"] = {}
assert self.test_rc.autorenewal_is_enabled()
self.test_rc.configuration["renewalparams"]["autorenew"] = "True"
@@ -475,7 +478,7 @@ class RenewableCertTests(BaseRenewableCertTest):
@mock.patch("certbot._internal.storage.cli")
@mock.patch("certbot._internal.storage.RenewableCert.ocsp_revoked")
def test_should_autorenew(self, mock_ocsp, mock_cli):
def test_should_autorenew(self, mock_ocsp: MagicMock, mock_cli: MagicMock) -> None:
"""Test should_autorenew on the basis of reasons other than
expiry time window."""
mock_cli.set_by_cli.return_value = False
@@ -491,7 +494,7 @@ class RenewableCertTests(BaseRenewableCertTest):
mock_ocsp.return_value = False
@mock.patch("certbot._internal.storage.relevant_values")
def test_save_successor(self, mock_rv):
def test_save_successor(self, mock_rv: MagicMock) -> None:
# Mock relevant_values() to claim that all values are relevant here
# (to avoid instantiating parser)
mock_rv.side_effect = lambda x: x
@@ -572,7 +575,7 @@ class RenewableCertTests(BaseRenewableCertTest):
@mock.patch("certbot._internal.storage.relevant_values")
@mock.patch("certbot._internal.storage.filesystem.copy_ownership_and_apply_mode")
def test_save_successor_maintains_gid(self, mock_ownership, mock_rv):
def test_save_successor_maintains_gid(self, mock_ownership: MagicMock, mock_rv: MagicMock) -> None:
# Mock relevant_values() to claim that all values are relevant here
# (to avoid instantiating parser)
mock_rv.side_effect = lambda x: x
@@ -585,7 +588,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert mock_ownership.called
@mock.patch("certbot._internal.storage.relevant_values")
def test_new_lineage(self, mock_rv):
def test_new_lineage(self, mock_rv: MagicMock) -> None:
"""Test for new_lineage() class method."""
# Mock relevant_values to say everything is relevant here (so we
# don't have to mock the parser to help it decide!)
@@ -639,7 +642,7 @@ class RenewableCertTests(BaseRenewableCertTest):
# got saved
@mock.patch("certbot._internal.storage.relevant_values")
def test_new_lineage_nonexistent_dirs(self, mock_rv):
def test_new_lineage_nonexistent_dirs(self, mock_rv: MagicMock) -> None:
"""Test that directories can be created if they don't exist."""
# Mock relevant_values to say everything is relevant here (so we
# don't have to mock the parser to help it decide!)
@@ -661,14 +664,14 @@ class RenewableCertTests(BaseRenewableCertTest):
self.config.default_archive_dir, "the-lineage.com", "privkey1.pem"))
@mock.patch("certbot._internal.storage.util.unique_lineage_name")
def test_invalid_config_filename(self, mock_uln):
def test_invalid_config_filename(self, mock_uln: MagicMock) -> None:
from certbot._internal import storage
mock_uln.return_value = "this_does_not_end_with_dot_conf", "yikes"
with pytest.raises(errors.CertStorageError):
storage.RenewableCert.new_lineage("example.com",
"cert", "privkey", "chain", self.config)
def test_bad_kind(self):
def test_bad_kind(self) -> None:
with pytest.raises(errors.CertStorageError):
self.test_rc.current_target("elephant")
with pytest.raises(errors.CertStorageError):
@@ -684,7 +687,7 @@ class RenewableCertTests(BaseRenewableCertTest):
self.test_rc._update_link_to("elephant", 17)
@mock.patch("certbot.ocsp.RevocationChecker.ocsp_revoked_by_paths")
def test_ocsp_revoked(self, mock_checker):
def test_ocsp_revoked(self, mock_checker: MagicMock) -> None:
# Write out test files
for kind in ALL_FOUR:
self._write_out_kind(kind, 1)
@@ -713,7 +716,7 @@ class RenewableCertTests(BaseRenewableCertTest):
log_msg = logger.call_args[0][0]
assert "An error occurred determining the OCSP status" in log_msg
def test_add_time_interval(self):
def test_add_time_interval(self) -> None:
from certbot._internal import storage
# this month has 30 days, and the next year is a leap year
@@ -755,14 +758,14 @@ class RenewableCertTests(BaseRenewableCertTest):
assert storage.add_time_interval(base_time, interval) == \
excepted
def test_server(self):
def test_server(self) -> None:
self.test_rc.configuration["renewalparams"] = {}
assert self.test_rc.server is None
rp = self.test_rc.configuration["renewalparams"]
rp["server"] = "https://acme.example/dir"
assert self.test_rc.server == "https://acme.example/dir"
def test_is_test_cert(self):
def test_is_test_cert(self) -> None:
self.test_rc.configuration["renewalparams"] = {}
rp = self.test_rc.configuration["renewalparams"]
assert self.test_rc.is_test_cert is False
@@ -775,7 +778,7 @@ class RenewableCertTests(BaseRenewableCertTest):
rp["server"] = "https://acme-v02.api.letsencrypt.org/directory"
assert self.test_rc.is_test_cert is False
def test_missing_cert(self):
def test_missing_cert(self) -> None:
from certbot._internal import storage
with pytest.raises(errors.CertStorageError):
storage.RenewableCert(self.config_file.filename, self.config)
@@ -783,7 +786,7 @@ class RenewableCertTests(BaseRenewableCertTest):
with pytest.raises(errors.CertStorageError):
storage.RenewableCert(self.config_file.filename, self.config)
def test_write_renewal_config(self):
def test_write_renewal_config(self) -> None:
# Mostly tested by the process of creating and updating lineages,
# but we can test that this successfully creates files, removes
# unneeded items, and preserves comments.
@@ -816,7 +819,7 @@ class RenewableCertTests(BaseRenewableCertTest):
assert stat.S_IMODE(os.lstat(temp).st_mode) == \
stat.S_IMODE(os.lstat(temp2).st_mode)
def test_update_symlinks(self):
def test_update_symlinks(self) -> None:
from certbot._internal import storage
archive_dir_path = os.path.join(self.config.config_dir, "archive", "example.org")
for kind in ALL_FOUR:
@@ -831,7 +834,7 @@ class RenewableCertTests(BaseRenewableCertTest):
storage.RenewableCert(self.config_file.filename, self.config,
update_symlinks=True)
def test_truncate(self):
def test_truncate(self) -> None:
# It should not do anything when there's less than 5 cert history
for kind in ALL_FOUR:
self._write_out_kind(kind, 1)
@@ -850,7 +853,7 @@ class RenewableCertTests(BaseRenewableCertTest):
class DeleteFilesTest(BaseRenewableCertTest):
"""Tests for certbot._internal.storage.delete_files"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
for kind in ALL_FOUR:
@@ -866,12 +869,12 @@ class DeleteFilesTest(BaseRenewableCertTest):
assert os.path.exists(os.path.join(
self.config.config_dir, "archive", "example.org"))
def _call(self):
def _call(self) -> None:
from certbot._internal import storage
with mock.patch("certbot._internal.storage.logger"):
storage.delete_files(self.config, "example.org")
def test_delete_all_files(self):
def test_delete_all_files(self) -> None:
self._call()
assert not os.path.exists(os.path.join(
@@ -881,7 +884,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
assert not os.path.exists(os.path.join(
self.config.config_dir, "archive", "example.org"))
def test_bad_renewal_config(self):
def test_bad_renewal_config(self) -> None:
with open(self.config_file.filename, 'a') as config_file:
config_file.write("asdfasfasdfasdf")
@@ -892,7 +895,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
assert not os.path.exists(os.path.join(
self.config.renewal_configs_dir, "example.org.conf"))
def test_no_renewal_config(self):
def test_no_renewal_config(self) -> None:
os.remove(self.config_file.filename)
with pytest.raises(errors.CertStorageError):
self._call()
@@ -900,7 +903,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
self.config.live_dir, "example.org"))
assert not os.path.exists(self.config_file.filename)
def test_no_cert_file(self):
def test_no_cert_file(self) -> None:
os.remove(os.path.join(
self.config.live_dir, "example.org", "cert.pem"))
self._call()
@@ -910,7 +913,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
assert not os.path.exists(os.path.join(
self.config.config_dir, "archive", "example.org"))
def test_no_readme_file(self):
def test_no_readme_file(self) -> None:
os.remove(os.path.join(
self.config.live_dir, "example.org", "README"))
self._call()
@@ -920,7 +923,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
assert not os.path.exists(os.path.join(
self.config.config_dir, "archive", "example.org"))
def test_livedir_not_empty(self):
def test_livedir_not_empty(self) -> None:
with open(os.path.join(
self.config.live_dir, "example.org", "other_file"), 'a'):
pass
@@ -931,7 +934,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
assert not os.path.exists(os.path.join(
self.config.config_dir, "archive", "example.org"))
def test_no_archive(self):
def test_no_archive(self) -> None:
archive_dir = os.path.join(self.config.config_dir, "archive", "example.org")
os.rmdir(archive_dir)
self._call()
@@ -942,7 +945,7 @@ class DeleteFilesTest(BaseRenewableCertTest):
class CertPathForCertNameTest(BaseRenewableCertTest):
"""Test for certbot._internal.storage.cert_path_for_cert_name"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.config_file.write()
self._write_out_ex_kinds()
@@ -950,14 +953,14 @@ class CertPathForCertNameTest(BaseRenewableCertTest):
'fullchain.pem')
self.config.cert_path = self.fullchain
def _call(self, cli_config, certname):
def _call(self, cli_config: NamespaceConfig, certname: str) -> str:
from certbot._internal.storage import cert_path_for_cert_name
return cert_path_for_cert_name(cli_config, certname)
def test_simple_cert_name(self):
def test_simple_cert_name(self) -> None:
assert self._call(self.config, 'example.org') == self.fullchain
def test_no_such_cert_name(self):
def test_no_such_cert_name(self) -> None:
with pytest.raises(errors.CertStorageError):
self._call(self.config, 'fake-example.org')

View File

@@ -13,16 +13,18 @@ from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
from typing import Dict, List, Tuple, Type, Union
from unittest.mock import MagicMock
class EnvNoSnapForExternalCallsTest(unittest.TestCase):
"""Tests for certbot.util.env_no_snap_for_external_calls."""
@classmethod
def _call(cls):
def _call(cls) -> Dict[str, str]:
from certbot.util import env_no_snap_for_external_calls
return env_no_snap_for_external_calls()
def test_removed(self):
def test_removed(self) -> None:
original_path = os.environ['PATH']
env_copy_dict = os.environ.copy()
env_copy_dict['PATH'] = 'RANDOM_NONSENSE_GARBAGE/blah/blah:' + original_path
@@ -31,7 +33,7 @@ class EnvNoSnapForExternalCallsTest(unittest.TestCase):
with mock.patch('certbot.compat.os.environ.copy', return_value=env_copy_dict):
assert self._call()['PATH'] == original_path
def test_noop(self):
def test_noop(self) -> None:
env_copy_dict_unmodified = os.environ.copy()
env_copy_dict_unmodified['PATH'] = 'RANDOM_NONSENSE_GARBAGE/blah/blah:' \
+ env_copy_dict_unmodified['PATH']
@@ -52,12 +54,12 @@ class EnvNoSnapForExternalCallsTest(unittest.TestCase):
class RunScriptTest(unittest.TestCase):
"""Tests for certbot.util.run_script."""
@classmethod
def _call(cls, params):
def _call(cls, params: List[str]) -> Tuple[str, str]:
from certbot.util import run_script
return run_script(params)
@mock.patch("certbot.util.subprocess.run")
def test_default(self, mock_run):
def test_default(self, mock_run: MagicMock) -> None:
"""These will be changed soon enough with reload."""
mock_run().returncode = 0
mock_run().stdout = "stdout"
@@ -68,14 +70,14 @@ class RunScriptTest(unittest.TestCase):
assert err == "stderr"
@mock.patch("certbot.util.subprocess.run")
def test_bad_process(self, mock_run):
def test_bad_process(self, mock_run: MagicMock) -> None:
mock_run.side_effect = OSError
with pytest.raises(errors.SubprocessError):
self._call(["test"])
@mock.patch("certbot.util.subprocess.run")
def test_failure(self, mock_run):
def test_failure(self, mock_run: MagicMock) -> None:
mock_run().returncode = 1
with pytest.raises(errors.SubprocessError):
@@ -86,15 +88,15 @@ class ExeExistsTest(unittest.TestCase):
"""Tests for certbot.util.exe_exists."""
@classmethod
def _call(cls, exe):
def _call(cls, exe: str) -> bool:
from certbot.util import exe_exists
return exe_exists(exe)
def test_exe_exists(self):
def test_exe_exists(self) -> None:
with mock.patch("certbot.util.filesystem.is_executable", return_value=True):
assert self._call("/path/to/exe")
def test_exe_not_exists(self):
def test_exe_not_exists(self) -> None:
with mock.patch("certbot.util.filesystem.is_executable", return_value=False):
assert not self._call("/path/to/exe")
@@ -102,11 +104,11 @@ class ExeExistsTest(unittest.TestCase):
class LockDirUntilExit(test_util.TempDirTestCase):
"""Tests for certbot.util.lock_dir_until_exit."""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot.util import lock_dir_until_exit
return lock_dir_until_exit(*args, **kwargs)
def setUp(self):
def setUp(self) -> None:
super().setUp()
# reset global state from other tests
import certbot.util
@@ -114,7 +116,7 @@ class LockDirUntilExit(test_util.TempDirTestCase):
@mock.patch('certbot.util.logger')
@mock.patch('certbot.util.atexit_register')
def test_it(self, mock_register, mock_logger):
def test_it(self, mock_register: MagicMock, mock_logger: MagicMock) -> None:
subdir = os.path.join(self.tempdir, 'subdir')
filesystem.mkdir(subdir)
self._call(self.tempdir)
@@ -138,19 +140,19 @@ class LockDirUntilExit(test_util.TempDirTestCase):
class SetUpCoreDirTest(test_util.TempDirTestCase):
"""Tests for certbot.util.make_or_verify_core_dir."""
def _call(self, *args, **kwargs):
def _call(self, *args, **kwargs) -> None:
from certbot.util import set_up_core_dir
return set_up_core_dir(*args, **kwargs)
@mock.patch('certbot.util.lock_dir_until_exit')
def test_success(self, mock_lock):
def test_success(self, mock_lock: MagicMock) -> None:
new_dir = os.path.join(self.tempdir, 'new')
self._call(new_dir, 0o700, False)
assert os.path.exists(new_dir)
assert mock_lock.call_count == 1
@mock.patch('certbot.util.make_or_verify_dir')
def test_failure(self, mock_make_or_verify):
def test_failure(self, mock_make_or_verify: MagicMock) -> None:
mock_make_or_verify.side_effect = OSError
with pytest.raises(errors.Error):
self._call(self.tempdir, 0o700, False)
@@ -164,31 +166,31 @@ class MakeOrVerifyDirTest(test_util.TempDirTestCase):
"""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.path = os.path.join(self.tempdir, "foo")
filesystem.mkdir(self.path, 0o600)
def _call(self, directory, mode):
def _call(self, directory: str, mode: int) -> None:
from certbot.util import make_or_verify_dir
return make_or_verify_dir(directory, mode, strict=True)
def test_creates_dir_when_missing(self):
def test_creates_dir_when_missing(self) -> None:
path = os.path.join(self.tempdir, "bar")
self._call(path, 0o650)
assert os.path.isdir(path)
assert filesystem.check_mode(path, 0o650)
def test_existing_correct_mode_does_not_fail(self):
def test_existing_correct_mode_does_not_fail(self) -> None:
self._call(self.path, 0o600)
assert filesystem.check_mode(self.path, 0o600)
def test_existing_wrong_mode_fails(self):
def test_existing_wrong_mode_fails(self) -> None:
with pytest.raises(errors.Error):
self._call(self.path, 0o400)
def test_reraises_os_error(self):
def test_reraises_os_error(self) -> None:
with mock.patch.object(filesystem, "makedirs") as makedirs:
makedirs.side_effect = OSError()
with pytest.raises(OSError):
@@ -198,23 +200,23 @@ class MakeOrVerifyDirTest(test_util.TempDirTestCase):
class UniqueFileTest(test_util.TempDirTestCase):
"""Tests for certbot.util.unique_file."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.default_name = os.path.join(self.tempdir, "foo.txt")
def _call(self, mode=0o600):
def _call(self, mode: int=0o600) -> Tuple[ io.TextIOWrapper, str]:
from certbot.util import unique_file
return unique_file(self.default_name, mode)
def test_returns_fd_for_writing(self):
def test_returns_fd_for_writing(self) -> None:
fd, name = self._call()
fd.write("bar")
fd.close()
with open(name) as f:
assert f.read() == "bar"
def test_right_mode(self):
def test_right_mode(self) -> None:
fd1, name1 = self._call(0o700)
fd2, name2 = self._call(0o600)
assert filesystem.check_mode(name1, 0o700)
@@ -222,7 +224,7 @@ class UniqueFileTest(test_util.TempDirTestCase):
fd1.close()
fd2.close()
def test_default_exists(self):
def test_default_exists(self) -> None:
fd1, name1 = self._call() # create 0000_foo.txt
fd2, name2 = self._call()
fd3, name3 = self._call()
@@ -257,17 +259,17 @@ except NameError:
class UniqueLineageNameTest(test_util.TempDirTestCase):
"""Tests for certbot.util.unique_lineage_name."""
def _call(self, filename, mode=0o777):
def _call(self, filename: str, mode: int=0o777) -> Tuple[ io.TextIOWrapper, str]:
from certbot.util import unique_lineage_name
return unique_lineage_name(self.tempdir, filename, mode)
def test_basic(self):
def test_basic(self) -> None:
f, path = self._call("wow")
assert isinstance(f, file_type)
assert os.path.join(self.tempdir, "wow.conf") == path
f.close()
def test_multiple(self):
def test_multiple(self) -> None:
items = []
for _ in range(10):
items.append(self._call("wow"))
@@ -278,7 +280,7 @@ class UniqueLineageNameTest(test_util.TempDirTestCase):
for f, _ in items:
f.close()
def test_failure(self):
def test_failure(self) -> None:
with mock.patch("certbot.compat.filesystem.open", side_effect=OSError(errno.EIO)):
with pytest.raises(OSError):
self._call("wow")
@@ -287,27 +289,27 @@ class UniqueLineageNameTest(test_util.TempDirTestCase):
class SafelyRemoveTest(test_util.TempDirTestCase):
"""Tests for certbot.util.safely_remove."""
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.path = os.path.join(self.tempdir, "foo")
def _call(self):
def _call(self) -> None:
from certbot.util import safely_remove
return safely_remove(self.path)
def test_exists(self):
def test_exists(self) -> None:
with open(self.path, "w"):
pass # just create the file
self._call()
assert not os.path.exists(self.path)
def test_missing(self):
def test_missing(self) -> None:
self._call()
# no error, yay!
assert not os.path.exists(self.path)
def test_other_error_passthrough(self):
def test_other_error_passthrough(self) -> None:
with mock.patch("certbot.util.os.remove") as mock_remove:
mock_remove.side_effect = OSError
with pytest.raises(OSError):
@@ -317,11 +319,11 @@ class SafelyRemoveTest(test_util.TempDirTestCase):
class SafeEmailTest(unittest.TestCase):
"""Test safe_email."""
@classmethod
def _call(cls, addr):
def _call(cls, addr: str) -> bool:
from certbot.util import safe_email
return safe_email(addr)
def test_valid_emails(self):
def test_valid_emails(self) -> None:
addrs = [
"certbot@certbot.org",
"tbd.ade@gmail.com",
@@ -330,7 +332,7 @@ class SafeEmailTest(unittest.TestCase):
for addr in addrs:
assert self._call(addr), "%s failed." % addr
def test_invalid_emails(self):
def test_invalid_emails(self) -> None:
addrs = [
"certbot@certbot..org",
".tbd.ade@gmail.com",
@@ -342,14 +344,14 @@ class SafeEmailTest(unittest.TestCase):
class AddDeprecatedArgumentTest(unittest.TestCase):
"""Test add_deprecated_argument."""
def setUp(self):
def setUp(self) -> None:
self.parser = argparse.ArgumentParser()
def _call(self, argument_name, nargs):
def _call(self, argument_name: str, nargs: int) -> None:
from certbot.util import add_deprecated_argument
add_deprecated_argument(self.parser.add_argument, argument_name, nargs)
def test_warning_no_arg(self):
def test_warning_no_arg(self) -> None:
self._call("--old-option", 0)
with mock.patch("warnings.warn") as mock_warn:
self.parser.parse_args(["--old-option"])
@@ -357,7 +359,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase):
assert "is deprecated" in mock_warn.call_args[0][0]
assert "--old-option" in mock_warn.call_args[0][0]
def test_warning_with_arg(self):
def test_warning_with_arg(self) -> None:
self._call("--old-option", 1)
with mock.patch("warnings.warn") as mock_warn:
self.parser.parse_args(["--old-option", "42"])
@@ -365,7 +367,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase):
assert "is deprecated" in mock_warn.call_args[0][0]
assert "--old-option" in mock_warn.call_args[0][0]
def test_help(self):
def test_help(self) -> None:
self._call("--old-option", 2)
stdout = io.StringIO()
with mock.patch("sys.stdout", new=stdout):
@@ -375,7 +377,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase):
pass
assert "--old-option" not in stdout.getvalue()
def test_set_constant(self):
def test_set_constant(self) -> None:
"""Test when ACTION_TYPES_THAT_DONT_NEED_A_VALUE is a set.
This variable is a set in configargparse versions < 0.12.0.
@@ -383,7 +385,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase):
"""
self._test_constant_common(set)
def test_tuple_constant(self):
def test_tuple_constant(self) -> None:
"""Test when ACTION_TYPES_THAT_DONT_NEED_A_VALUE is a tuple.
This variable is a tuple in configargparse versions >= 0.12.0.
@@ -391,7 +393,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase):
"""
self._test_constant_common(tuple)
def _test_constant_common(self, typ):
def _test_constant_common(self, typ: Union[Type[set], Type[tuple]]) -> None:
with mock.patch("certbot.util.configargparse") as mock_configargparse:
mock_configargparse.ACTION_TYPES_THAT_DONT_NEED_A_VALUE = typ()
self._call("--old-option", 1)
@@ -401,97 +403,97 @@ class AddDeprecatedArgumentTest(unittest.TestCase):
class EnforceLeValidity(unittest.TestCase):
"""Test enforce_le_validity."""
def _call(self, domain):
def _call(self, domain: str) -> str:
from certbot.util import enforce_le_validity
return enforce_le_validity(domain)
def test_sanity(self):
def test_sanity(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"..")
def test_invalid_chars(self):
def test_invalid_chars(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"hello_world.example.com")
def test_leading_hyphen(self):
def test_leading_hyphen(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"-a.example.com")
def test_trailing_hyphen(self):
def test_trailing_hyphen(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"a-.example.com")
def test_one_label(self):
def test_one_label(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"com")
def test_valid_domain(self):
def test_valid_domain(self) -> None:
assert self._call(u"example.com") == u"example.com"
def test_input_with_scheme(self):
def test_input_with_scheme(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"http://example.com")
with pytest.raises(errors.ConfigurationError):
self._call(u"https://example.com")
def test_valid_input_with_scheme_name(self):
def test_valid_input_with_scheme_name(self) -> None:
assert self._call(u"http.example.com") == u"http.example.com"
class EnforceDomainSanityTest(unittest.TestCase):
"""Test enforce_domain_sanity."""
def _call(self, domain):
def _call(self, domain: Union[bytes, str]) -> str:
from certbot.util import enforce_domain_sanity
return enforce_domain_sanity(domain)
def test_nonascii_str(self):
def test_nonascii_str(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"eichh\u00f6rnchen.example.com".encode("utf-8"))
def test_nonascii_unicode(self):
def test_nonascii_unicode(self) -> None:
with pytest.raises(errors.ConfigurationError):
self._call(u"eichh\u00f6rnchen.example.com")
def test_too_long(self):
def test_too_long(self) -> None:
long_domain = u"a"*256
with pytest.raises(errors.ConfigurationError):
self._call(long_domain)
def test_not_too_long(self):
def test_not_too_long(self) -> None:
not_too_long_domain = u"{0}.{1}.{2}.{3}".format("a"*63, "b"*63, "c"*63, "d"*63)
self._call(not_too_long_domain)
def test_empty_label(self):
def test_empty_label(self) -> None:
empty_label_domain = u"fizz..example.com"
with pytest.raises(errors.ConfigurationError):
self._call(empty_label_domain)
def test_empty_trailing_label(self):
def test_empty_trailing_label(self) -> None:
empty_trailing_label_domain = u"example.com.."
with pytest.raises(errors.ConfigurationError):
self._call(empty_trailing_label_domain)
def test_long_label_1(self):
def test_long_label_1(self) -> None:
long_label_domain = u"a"*64
with pytest.raises(errors.ConfigurationError):
self._call(long_label_domain)
def test_long_label_2(self):
def test_long_label_2(self) -> None:
long_label_domain = u"{0}.{1}.com".format(u"a"*64, u"b"*63)
with pytest.raises(errors.ConfigurationError):
self._call(long_label_domain)
def test_not_long_label(self):
def test_not_long_label(self) -> None:
not_too_long_label_domain = u"{0}.{1}.com".format(u"a"*63, u"b"*63)
self._call(not_too_long_label_domain)
def test_empty_domain(self):
def test_empty_domain(self) -> None:
empty_domain = u""
with pytest.raises(errors.ConfigurationError):
self._call(empty_domain)
def test_punycode_ok(self):
def test_punycode_ok(self) -> None:
# Punycode is now legal, so no longer an error; instead check
# that it's _not_ an error (at the initial sanity check stage)
self._call('this.is.xn--ls8h.tld')
@@ -500,19 +502,19 @@ class EnforceDomainSanityTest(unittest.TestCase):
class IsWildcardDomainTest(unittest.TestCase):
"""Tests for is_wildcard_domain."""
def setUp(self):
def setUp(self) -> None:
self.wildcard = u"*.example.org"
self.no_wildcard = u"example.org"
def _call(self, domain):
def _call(self, domain: Union[bytes, str]) -> bool:
from certbot.util import is_wildcard_domain
return is_wildcard_domain(domain)
def test_no_wildcard(self):
def test_no_wildcard(self) -> None:
assert not self._call(self.no_wildcard)
assert not self._call(self.no_wildcard.encode())
def test_wildcard(self):
def test_wildcard(self) -> None:
assert self._call(self.wildcard)
assert self._call(self.wildcard.encode())
@@ -558,7 +560,7 @@ class OsInfoTest(unittest.TestCase):
m_distro.version.return_value = "else"
assert cbutil.get_os_info() == ("something", "else")
def test_non_systemd_os_info(self):
def test_non_systemd_os_info(self) -> None:
import certbot.util as cbutil
with mock.patch('certbot.util._USE_DISTRO', False):
with mock.patch('platform.system_alias',
@@ -602,25 +604,25 @@ class OsInfoTest(unittest.TestCase):
class AtexitRegisterTest(unittest.TestCase):
"""Tests for certbot.util.atexit_register."""
def setUp(self):
def setUp(self) -> None:
self.func = mock.MagicMock()
self.args = ('hi',)
self.kwargs = {'answer': 42}
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> None:
from certbot.util import atexit_register
return atexit_register(*args, **kwargs)
def test_called(self):
def test_called(self) -> None:
self._test_common(os.getpid())
self.func.assert_called_with(*self.args, **self.kwargs)
def test_not_called(self):
def test_not_called(self) -> None:
self._test_common(initial_pid=-1)
assert self.func.called is False
def _test_common(self, initial_pid):
def _test_common(self, initial_pid: int) -> None:
with mock.patch('certbot.util._INITIAL_PID', initial_pid):
with mock.patch('certbot.util.atexit') as mock_atexit:
self._call(self.func, *self.args, **self.kwargs)
@@ -642,11 +644,11 @@ class ParseLooseVersionTest(unittest.TestCase):
"""
@classmethod
def _call(cls, *args, **kwargs):
def _call(cls, *args, **kwargs) -> List[Union[int, str]]:
from certbot.util import parse_loose_version
return parse_loose_version(*args, **kwargs)
def test_less_than(self):
def test_less_than(self) -> None:
comparisons = (('1.5.1', '1.5.2b2'),
('3.4j', '1996.07.12'),
('2g6', '11g'),
@@ -655,10 +657,10 @@ class ParseLooseVersionTest(unittest.TestCase):
for v1, v2 in comparisons:
assert self._call(v1) < self._call(v2)
def test_equal(self):
def test_equal(self) -> None:
assert self._call('8.02') == self._call('8.02')
def test_greater_than(self):
def test_greater_than(self) -> None:
comparisons = (('161', '3.10a'),
('3.2.pl0', '3.1.1.6'))
for v1, v2 in comparisons: