tests.testing
Resources to assist in testing Certbot Deployer and its plugins
Functions here should be made available as pytest fixtures.
Other documented resources can be used by import from certbot_deployer.testing
, e.g.:
from certbot_deployer.testing import COMMON_NAME
1""" 2Resources to assist in testing Certbot Deployer and its plugins 3 4Functions here should be made available as pytest 5[fixtures](https://docs.pytest.org/en/latest/how-to/fixtures.html#how-to-fixtures). 6 7Other documented resources can be used by import from `certbot_deployer.testing`, e.g.: 8 9``` 10from certbot_deployer.testing import COMMON_NAME 11``` 12""" 13 14from datetime import datetime 15from pathlib import Path 16from typing import List, Optional, Tuple 17 18from cryptography import x509 19from cryptography.hazmat.primitives import hashes, serialization 20from cryptography.hazmat.primitives.asymmetric import rsa 21from cryptography.x509.oid import NameOID 22 23import pytest 24 25from certbot_deployer import ( 26 CERT_FILENAME, 27 FULLCHAIN_FILENAME, 28 INTERMEDIATES_FILENAME, 29 KEY_FILENAME, 30) 31from certbot_deployer.deployer import CertificateBundle 32 33 34COMMON_NAME: str = "test_common_name" 35""" A hard-coded default value used by testing fixtures """ 36NOT_VALID_BEFORE: datetime = datetime(2020, 1, 1) 37""" A hard-coded default value used by testing fixtures """ 38NOT_VALID_AFTER: datetime = datetime(2099, 1, 1) 39""" A hard-coded default value used by testing fixtures """ 40 41 42@pytest.fixture(name="certbot_deployer_self_signed_certificate_bundle") 43# pylint: disable=too-many-locals 44def fixture_self_signed_certificate_bundle( 45 request: pytest.FixtureRequest, 46 tmp_path: Path, 47) -> CertificateBundle: 48 """ 49 Pytest fixture which generates a self-signed certificate bundle for testing. 50 51 Example usage: 52 53 ``` 54 import certbot_deployer 55 from certbot_deployer.testing import COMMON_NAME 56 57 def test_your_plugin_function( 58 certbot_deployer_self_signed_certificate_bundle: certbot_deployer.CertificateBundle 59 ) -> None: 60 assert certbot_deployer_self_signed_certificate_bundle.common_name == COMMON_NAME 61 ``` 62 63 Args: 64 request (`pytest.FixtureRequest`): The pytest request object to access 65 indirect fixture parameters. 66 67 Parameters for Pytest indirect parameterization via `request`: 68 69 * common_name (str): The desired Common Name for the certificate, else COMMON_NAME 70 * not_valid_before (datetime): The certificate validity start time, else NOT_VALID_BEFORE 71 * not_valid_after (datetime): The certificate validity end time, else NOT_VALID_AFTER 72 * path (pathlib.Path): optional path in which to create the bundle files 73 74 Returns: 75 `certbot_deployer.deployer.CertificateBundle` corresponding to the created bundle. 76 """ 77 req_params: dict = getattr(request, "param", {}) 78 not_valid_before: datetime = NOT_VALID_BEFORE 79 if "not_valid_before" in req_params: 80 assert isinstance(req_params["not_valid_before"], datetime) 81 not_valid_before = req_params["not_valid_before"] 82 not_valid_after: datetime = NOT_VALID_AFTER 83 if "not_valid_after" in req_params: 84 assert isinstance(req_params["not_valid_after"], datetime) 85 not_valid_after = req_params["not_valid_after"] 86 path: Optional[Path] = None 87 if "path" in req_params: 88 assert isinstance(req_params["path"], (Path, type(None))) 89 path = req_params["path"] 90 subject_alternative_names: List[str] = [] 91 if "subject_alternative_names" in req_params: 92 assert isinstance(req_params["subject_alternative_names"], list) 93 subject_alternative_names = req_params["subject_alternative_names"] 94 common_name: Optional[str] = COMMON_NAME 95 if "common_name" in req_params: 96 assert isinstance(req_params["common_name"], (str, type(None))) 97 common_name = req_params["common_name"] 98 99 bundle_path: Path = path if path is not None else tmp_path 100 key: rsa.RSAPrivateKey = rsa.generate_private_key( 101 public_exponent=65537, key_size=2048 102 ) 103 subject: x509.Name 104 issuer: x509.Name 105 if common_name is None: 106 subject = issuer = x509.Name([]) 107 else: 108 subject = issuer = x509.Name( 109 [x509.NameAttribute(NameOID.COMMON_NAME, common_name)] 110 ) 111 cert_builder: x509.CertificateBuilder = ( 112 x509.CertificateBuilder() 113 .subject_name(subject) 114 .issuer_name(issuer) 115 .public_key(key.public_key()) 116 .serial_number(x509.random_serial_number()) 117 .not_valid_before(not_valid_before) 118 .not_valid_after(not_valid_after) 119 .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) 120 ) 121 if subject_alternative_names: 122 san_extension = x509.SubjectAlternativeName( 123 [x509.DNSName(fqdn) for fqdn in subject_alternative_names] 124 ) 125 cert_builder = cert_builder.add_extension(san_extension, critical=False) 126 cert: x509.Certificate = cert_builder.sign(key, hashes.SHA256()) 127 pem_bytes: bytes = cert.public_bytes(encoding=serialization.Encoding.PEM) 128 129 key_text: str = key.private_bytes( 130 encoding=serialization.Encoding.PEM, 131 format=serialization.PrivateFormat.PKCS8, 132 encryption_algorithm=serialization.NoEncryption(), 133 ).decode("utf-8") 134 cert_text: str = pem_bytes.decode("utf-8") 135 136 (bundle_path / CERT_FILENAME).write_text(cert_text, encoding="utf-8") 137 (bundle_path / KEY_FILENAME).write_text(key_text, encoding="utf-8") 138 (bundle_path / INTERMEDIATES_FILENAME).write_text(cert_text, encoding="utf-8") 139 (bundle_path / FULLCHAIN_FILENAME).write_text( 140 f"{cert_text}\n{cert_text}", encoding="utf-8" 141 ) 142 143 return CertificateBundle(path_obj=bundle_path) 144 145 146# pylint: enable=too-many-locals
COMMON_NAME: str =
'test_common_name'
A hard-coded default value used by testing fixtures
NOT_VALID_BEFORE: datetime.datetime =
datetime.datetime(2020, 1, 1, 0, 0)
A hard-coded default value used by testing fixtures
NOT_VALID_AFTER: datetime.datetime =
datetime.datetime(2099, 1, 1, 0, 0)
A hard-coded default value used by testing fixtures
@pytest.fixture(name='certbot_deployer_self_signed_certificate_bundle')
def
fixture_self_signed_certificate_bundle( request: _pytest.fixtures.FixtureRequest, tmp_path: pathlib.Path) -> certbot_deployer.deployer.CertificateBundle:
43@pytest.fixture(name="certbot_deployer_self_signed_certificate_bundle") 44# pylint: disable=too-many-locals 45def fixture_self_signed_certificate_bundle( 46 request: pytest.FixtureRequest, 47 tmp_path: Path, 48) -> CertificateBundle: 49 """ 50 Pytest fixture which generates a self-signed certificate bundle for testing. 51 52 Example usage: 53 54 ``` 55 import certbot_deployer 56 from certbot_deployer.testing import COMMON_NAME 57 58 def test_your_plugin_function( 59 certbot_deployer_self_signed_certificate_bundle: certbot_deployer.CertificateBundle 60 ) -> None: 61 assert certbot_deployer_self_signed_certificate_bundle.common_name == COMMON_NAME 62 ``` 63 64 Args: 65 request (`pytest.FixtureRequest`): The pytest request object to access 66 indirect fixture parameters. 67 68 Parameters for Pytest indirect parameterization via `request`: 69 70 * common_name (str): The desired Common Name for the certificate, else COMMON_NAME 71 * not_valid_before (datetime): The certificate validity start time, else NOT_VALID_BEFORE 72 * not_valid_after (datetime): The certificate validity end time, else NOT_VALID_AFTER 73 * path (pathlib.Path): optional path in which to create the bundle files 74 75 Returns: 76 `certbot_deployer.deployer.CertificateBundle` corresponding to the created bundle. 77 """ 78 req_params: dict = getattr(request, "param", {}) 79 not_valid_before: datetime = NOT_VALID_BEFORE 80 if "not_valid_before" in req_params: 81 assert isinstance(req_params["not_valid_before"], datetime) 82 not_valid_before = req_params["not_valid_before"] 83 not_valid_after: datetime = NOT_VALID_AFTER 84 if "not_valid_after" in req_params: 85 assert isinstance(req_params["not_valid_after"], datetime) 86 not_valid_after = req_params["not_valid_after"] 87 path: Optional[Path] = None 88 if "path" in req_params: 89 assert isinstance(req_params["path"], (Path, type(None))) 90 path = req_params["path"] 91 subject_alternative_names: List[str] = [] 92 if "subject_alternative_names" in req_params: 93 assert isinstance(req_params["subject_alternative_names"], list) 94 subject_alternative_names = req_params["subject_alternative_names"] 95 common_name: Optional[str] = COMMON_NAME 96 if "common_name" in req_params: 97 assert isinstance(req_params["common_name"], (str, type(None))) 98 common_name = req_params["common_name"] 99 100 bundle_path: Path = path if path is not None else tmp_path 101 key: rsa.RSAPrivateKey = rsa.generate_private_key( 102 public_exponent=65537, key_size=2048 103 ) 104 subject: x509.Name 105 issuer: x509.Name 106 if common_name is None: 107 subject = issuer = x509.Name([]) 108 else: 109 subject = issuer = x509.Name( 110 [x509.NameAttribute(NameOID.COMMON_NAME, common_name)] 111 ) 112 cert_builder: x509.CertificateBuilder = ( 113 x509.CertificateBuilder() 114 .subject_name(subject) 115 .issuer_name(issuer) 116 .public_key(key.public_key()) 117 .serial_number(x509.random_serial_number()) 118 .not_valid_before(not_valid_before) 119 .not_valid_after(not_valid_after) 120 .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) 121 ) 122 if subject_alternative_names: 123 san_extension = x509.SubjectAlternativeName( 124 [x509.DNSName(fqdn) for fqdn in subject_alternative_names] 125 ) 126 cert_builder = cert_builder.add_extension(san_extension, critical=False) 127 cert: x509.Certificate = cert_builder.sign(key, hashes.SHA256()) 128 pem_bytes: bytes = cert.public_bytes(encoding=serialization.Encoding.PEM) 129 130 key_text: str = key.private_bytes( 131 encoding=serialization.Encoding.PEM, 132 format=serialization.PrivateFormat.PKCS8, 133 encryption_algorithm=serialization.NoEncryption(), 134 ).decode("utf-8") 135 cert_text: str = pem_bytes.decode("utf-8") 136 137 (bundle_path / CERT_FILENAME).write_text(cert_text, encoding="utf-8") 138 (bundle_path / KEY_FILENAME).write_text(key_text, encoding="utf-8") 139 (bundle_path / INTERMEDIATES_FILENAME).write_text(cert_text, encoding="utf-8") 140 (bundle_path / FULLCHAIN_FILENAME).write_text( 141 f"{cert_text}\n{cert_text}", encoding="utf-8" 142 ) 143 144 return CertificateBundle(path_obj=bundle_path)
Pytest fixture which generates a self-signed certificate bundle for testing.
Example usage:
import certbot_deployer
from certbot_deployer.testing import COMMON_NAME
def test_your_plugin_function(
certbot_deployer_self_signed_certificate_bundle: certbot_deployer.CertificateBundle
) -> None:
assert certbot_deployer_self_signed_certificate_bundle.common_name == COMMON_NAME
Arguments:
- request (
pytest.FixtureRequest
): The pytest request object to access indirect fixture parameters.
Parameters for Pytest indirect parameterization via request
:
- common_name (str): The desired Common Name for the certificate, else COMMON_NAME
- not_valid_before (datetime): The certificate validity start time, else NOT_VALID_BEFORE
- not_valid_after (datetime): The certificate validity end time, else NOT_VALID_AFTER
- path (pathlib.Path): optional path in which to create the bundle files
Returns:
certbot_deployer.deployer.CertificateBundle
corresponding to the created bundle.