Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed aiosmtpd/tests/certs/__init__.py
Empty file.
23 changes: 0 additions & 23 deletions aiosmtpd/tests/certs/server.crt

This file was deleted.

28 changes: 0 additions & 28 deletions aiosmtpd/tests/certs/server.key

This file was deleted.

23 changes: 0 additions & 23 deletions aiosmtpd/tests/certs/server_alt.crt

This file was deleted.

28 changes: 0 additions & 28 deletions aiosmtpd/tests/certs/server_alt.key

This file was deleted.

55 changes: 37 additions & 18 deletions aiosmtpd/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from contextlib import suppress
from functools import wraps
from smtplib import SMTP as SMTPClient
from typing import Any, Callable, Generator, NamedTuple, Optional, Type, TypeVar
from typing import Any, Callable, Generator, Iterator, NamedTuple, Optional, Type, TypeVar

import pytest
from pkg_resources import resource_filename
import trustme
from pytest_mock import MockFixture

from aiosmtpd.controller import Controller
Expand All @@ -32,8 +32,6 @@
"handler_data",
"Global",
"AUTOSTOP_DELAY",
"SERVER_CRT",
"SERVER_KEY",
]


Expand Down Expand Up @@ -73,8 +71,6 @@ def set_addr_from(cls, contr: Controller):
# If less than 1.0, might cause intermittent error if test system
# is too busy/overloaded.
AUTOSTOP_DELAY = 1.5
SERVER_CRT = resource_filename("aiosmtpd.tests.certs", "server.crt")
SERVER_KEY = resource_filename("aiosmtpd.tests.certs", "server.key")

# endregion

Expand Down Expand Up @@ -315,27 +311,50 @@ def client(request: pytest.FixtureRequest) -> Generator[SMTPClient, None, None]:


@pytest.fixture
def ssl_context_server() -> ssl.SSLContext:
def tls_certificate_authority() -> trustme.CA:
return trustme.CA()


@pytest.fixture
def tls_certificate(tls_certificate_authority: trustme.CA) -> trustme.LeafCert:
return tls_certificate_authority.issue_cert(
"localhost",
"xn--prklad-4va.localhost", # príklad.localhost
"127.0.0.1",
"::1",
)


@pytest.fixture
def ssl_context_server(tls_certificate: trustme.LeafCert) -> ssl.SSLContext:
"""
Provides a server-side SSL Context
"""
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.check_hostname = False
context.load_cert_chain(SERVER_CRT, SERVER_KEY)
#
return context
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
tls_certificate.configure_cert(ssl_ctx)
return ssl_ctx


@pytest.fixture
def ssl_context_client() -> ssl.SSLContext:
def ssl_context_client(tls_certificate_authority: trustme.CA) -> ssl.SSLContext:
"""
Provides a client-side SSL Context
"""
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.check_hostname = False
context.load_verify_locations(SERVER_CRT)
#
return context
ssl_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
tls_certificate_authority.configure_trust(ssl_ctx)
return ssl_ctx


@pytest.fixture
def tls_cert_pem_path(tls_certificate: trustme.LeafCert) -> Iterator[str]:
with tls_certificate.cert_chain_pems[0].tempfile() as cert_pem:
yield cert_pem


@pytest.fixture
def tls_key_pem_path(tls_certificate: trustme.LeafCert) -> Iterator[str]:
with tls_certificate.private_key_pem.tempfile() as key_pem:
yield key_pem


# Please keep the scope as "module"; setting it as "function" (the default) somehow
Expand Down
28 changes: 15 additions & 13 deletions aiosmtpd/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from aiosmtpd.main import main, parseargs
from aiosmtpd.testing.helpers import catchup_delay
from aiosmtpd.testing.statuscodes import SMTP_STATUS_CODES as S
from aiosmtpd.tests.conftest import AUTOSTOP_DELAY, SERVER_CRT, SERVER_KEY
from aiosmtpd.tests.conftest import AUTOSTOP_DELAY

try:
import pwd
Expand Down Expand Up @@ -199,24 +199,24 @@ def test_debug_3(self):

@pytest.mark.skipif(sys.platform == "darwin", reason="No idea why these are failing")
class TestMainByWatcher:
def test_tls(self, temp_event_loop):
def test_tls(self, temp_event_loop, tls_cert_pem_path, tls_key_pem_path):
with watcher_process(watch_for_tls) as retq:
temp_event_loop.call_later(AUTOSTOP_DELAY, temp_event_loop.stop)
main_n("--tlscert", str(SERVER_CRT), "--tlskey", str(SERVER_KEY))
main_n("--tlscert", tls_cert_pem_path, "--tlskey", tls_key_pem_path)
catchup_delay()
has_starttls = retq.get()
assert has_starttls is True
require_tls = retq.get()
assert require_tls is True

def test_tls_noreq(self, temp_event_loop):
def test_tls_noreq(self, temp_event_loop, tls_cert_pem_path, tls_key_pem_path):
with watcher_process(watch_for_tls) as retq:
temp_event_loop.call_later(AUTOSTOP_DELAY, temp_event_loop.stop)
main_n(
"--tlscert",
str(SERVER_CRT),
tls_cert_pem_path,
"--tlskey",
str(SERVER_KEY),
tls_key_pem_path,
"--no-requiretls",
)
catchup_delay()
Expand All @@ -225,10 +225,10 @@ def test_tls_noreq(self, temp_event_loop):
require_tls = retq.get()
assert require_tls is False

def test_smtps(self, temp_event_loop):
def test_smtps(self, temp_event_loop, tls_cert_pem_path, tls_key_pem_path):
with watcher_process(watch_for_smtps) as retq:
temp_event_loop.call_later(AUTOSTOP_DELAY, temp_event_loop.stop)
main_n("--smtpscert", str(SERVER_CRT), "--smtpskey", str(SERVER_KEY))
main_n("--smtpscert", tls_cert_pem_path, "--smtpskey", tls_key_pem_path)
catchup_delay()
has_smtps = retq.get()
assert has_smtps is True
Expand Down Expand Up @@ -335,16 +335,18 @@ def test_norequiretls(self, capsys, mocker):
assert args.requiretls is False

@pytest.mark.parametrize(
("certfile", "keyfile", "expect"),
("certfile_present", "keyfile_present", "expect"),
[
("x", "x", "Cert file x not found"),
(SERVER_CRT, "x", "Key file x not found"),
("x", SERVER_KEY, "Cert file x not found"),
(False, False, "Cert file x not found"),
(True, False, "Key file x not found"),
(False, True, "Cert file x not found"),
],
ids=["x-x", "cert-x", "x-key"],
)
@pytest.mark.parametrize("meth", ["smtps", "tls"])
def test_ssl_files_err(self, capsys, mocker, meth, certfile, keyfile, expect):
def test_ssl_files_err(self, capsys, mocker, meth, certfile_present, keyfile_present, expect, request):
certfile = request.getfixturevalue("tls_cert_pem_path") if certfile_present else "x"
keyfile = request.getfixturevalue("tls_key_pem_path") if keyfile_present else "x"
mocker.patch("aiosmtpd.main.PROGRAM", "smtpd")
with pytest.raises(SystemExit) as exc:
parseargs((f"--{meth}cert", certfile, f"--{meth}key", keyfile))
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Python 3.8 is EOL, so latest versions of some packages do not support it, use newest compatible versions
atpublic==5.0
attrs==24.2.0
coverage==7.6.1
cryptography==41.0.7; python_version == '3.8' and platform_python_implementation == "PyPy"
pytest==8.3.4
pytest-cov==5.0.0
pytest-mock==3.14.0
trustme==1.1.0; python_version == '3.8'
trustme==1.2.1; python_version > '3.8'
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ deps =
pytest-profiling
pytest-sugar
py # needed for pytest-sugar as it doesn't declare dependency on it.
trustme
!nocov: coverage>=7.0.1
!nocov: coverage[toml]
!nocov: coverage-conditional-plugin
Expand Down
Loading