"""Unit tests for certificate rotation functionality.

Tests certificate management, expiry checking, hot-reload, and security validation.
"""

import logging
import os
import ssl
import tempfile
import threading
import time
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import MagicMock, Mock, patch

import pytest
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID

from mca_sdk.security.certificates import CertificateManager
from mca_sdk.utils.exceptions import (
    CertificateError,
    CertificateExpiredError,
    CertificateLoadError,
    CertificateNotLoadedError,
    CertificateValidationError,
    ConfigurationError,
    SecurityError,
)


class TestCertificateGeneration:
    """Helper methods for generating test certificates."""

    @staticmethod
    def generate_private_key():
        """Generate RSA private key for testing."""
        return rsa.generate_private_key(
            public_exponent=65537, key_size=2048, backend=default_backend()
        )

    @staticmethod
    def generate_certificate(
        private_key,
        subject_name="Test Certificate",
        issuer_name=None,
        days_valid=365,
        not_valid_before=None,
    ):
        """Generate self-signed certificate for testing."""
        subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, subject_name)])

        if issuer_name:
            issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, issuer_name)])

        now = datetime.now(timezone.utc)
        not_before = not_valid_before or now
        not_after = not_before + timedelta(days=days_valid)

        cert = (
            x509.CertificateBuilder()
            .subject_name(subject)
            .issuer_name(issuer)
            .public_key(private_key.public_key())
            .serial_number(x509.random_serial_number())
            .not_valid_before(not_before)
            .not_valid_after(not_after)
            .sign(private_key, hashes.SHA256(), default_backend())
        )

        return cert

    @staticmethod
    def write_certificate(cert, path):
        """Write certificate to PEM file."""
        with open(path, "wb") as f:
            f.write(cert.public_bytes(serialization.Encoding.PEM))

    @staticmethod
    def write_private_key(key, path, permissions=0o600):
        """Write private key to PEM file with specified permissions."""
        with open(path, "wb") as f:
            f.write(
                key.private_bytes(
                    encoding=serialization.Encoding.PEM,
                    format=serialization.PrivateFormat.PKCS8,
                    encryption_algorithm=serialization.NoEncryption(),
                )
            )
        os.chmod(path, permissions)


@pytest.fixture
def temp_cert_dir():
    """Create temporary directory for certificate files."""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield Path(tmpdir)


@pytest.fixture
def valid_cert_files(temp_cert_dir):
    """Generate valid certificate and key files for testing."""
    key = TestCertificateGeneration.generate_private_key()
    cert = TestCertificateGeneration.generate_certificate(key, days_valid=365)

    cert_path = temp_cert_dir / "cert.pem"
    key_path = temp_cert_dir / "key.pem"

    TestCertificateGeneration.write_certificate(cert, cert_path)
    TestCertificateGeneration.write_private_key(key, key_path, permissions=0o600)

    return {
        "cert_path": str(cert_path),
        "key_path": str(key_path),
        "key": key,
        "cert": cert,
    }


@pytest.fixture
def expiring_cert_files(temp_cert_dir):
    """Generate certificate expiring in 15 days."""
    key = TestCertificateGeneration.generate_private_key()
    cert = TestCertificateGeneration.generate_certificate(key, days_valid=15)

    cert_path = temp_cert_dir / "cert.pem"
    key_path = temp_cert_dir / "key.pem"

    TestCertificateGeneration.write_certificate(cert, cert_path)
    TestCertificateGeneration.write_private_key(key, key_path)

    return {
        "cert_path": str(cert_path),
        "key_path": str(key_path),
    }


@pytest.fixture
def expired_cert_files(temp_cert_dir):
    """Generate expired certificate."""
    key = TestCertificateGeneration.generate_private_key()
    # Certificate that expired 1 day ago
    not_before = datetime.utcnow() - timedelta(days=31)
    cert = TestCertificateGeneration.generate_certificate(
        key, days_valid=30, not_valid_before=not_before
    )

    cert_path = temp_cert_dir / "cert.pem"
    key_path = temp_cert_dir / "key.pem"

    TestCertificateGeneration.write_certificate(cert, cert_path)
    TestCertificateGeneration.write_private_key(key, key_path)

    return {
        "cert_path": str(cert_path),
        "key_path": str(key_path),
    }


class TestCertificateManagerInitialization:
    """Test CertificateManager initialization and validation."""

    def test_successful_initialization(self, valid_cert_files):
        """Test successful certificate manager initialization."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        assert manager is not None
        assert manager.get_ssl_context() is not None

    def test_missing_certificate_file(self, temp_cert_dir):
        """Test initialization fails when certificate file missing."""
        # Create a dummy key file to pass permission check
        key_path = temp_cert_dir / "key.pem"
        key = TestCertificateGeneration.generate_private_key()
        TestCertificateGeneration.write_private_key(key, key_path)

        with pytest.raises(CertificateLoadError, match="Failed to load certificate"):
            CertificateManager(cert_path=str(temp_cert_dir / "missing.pem"), key_path=str(key_path))

    def test_missing_private_key_file(self, valid_cert_files, temp_cert_dir):
        """Test initialization fails when private key file missing."""
        with pytest.raises(
            (ConfigurationError, CertificateLoadError),
            match="Private key file not found",
        ):
            CertificateManager(
                cert_path=valid_cert_files["cert_path"],
                key_path=str(temp_cert_dir / "missing-key.pem"),
            )

    def test_malformed_certificate(self, temp_cert_dir):
        """Test initialization fails with malformed certificate."""
        cert_path = temp_cert_dir / "bad-cert.pem"
        key_path = temp_cert_dir / "key.pem"

        # Write invalid data
        with open(cert_path, "w") as f:
            f.write("This is not a valid certificate")

        # Create a valid key
        key = TestCertificateGeneration.generate_private_key()
        TestCertificateGeneration.write_private_key(key, key_path)

        with pytest.raises(CertificateLoadError):
            CertificateManager(cert_path=str(cert_path), key_path=str(key_path))


class TestFilePermissionValidation:
    """Test private key file permission validation (AC: #6)."""

    def test_secure_permissions_pass(self, valid_cert_files):
        """Test that secure permissions (0600) pass validation."""
        # File already has 0600 from fixture
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            enforce_key_permissions=True,
        )
        assert manager is not None

    def test_world_readable_permissions_hard_fail(self, valid_cert_files):
        """Test that world-readable permissions fail when enforcement enabled."""
        # Make key world-readable
        os.chmod(valid_cert_files["key_path"], 0o644)

        with pytest.raises(SecurityError, match="insecure permissions"):
            CertificateManager(
                cert_path=valid_cert_files["cert_path"],
                key_path=valid_cert_files["key_path"],
                enforce_key_permissions=True,
            )

    def test_world_readable_permissions_soft_fail(self, valid_cert_files, caplog):
        """Test that world-readable permissions warn when enforcement disabled."""
        # Make key world-readable
        os.chmod(valid_cert_files["key_path"], 0o644)

        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            enforce_key_permissions=False,
        )

        assert manager is not None
        assert "insecure permissions" in caplog.text.lower()

    def test_group_readable_permissions_fail(self, valid_cert_files):
        """Test that group-readable permissions fail validation."""
        os.chmod(valid_cert_files["key_path"], 0o640)

        with pytest.raises(SecurityError, match="insecure permissions"):
            CertificateManager(
                cert_path=valid_cert_files["cert_path"],
                key_path=valid_cert_files["key_path"],
                enforce_key_permissions=True,
            )


class TestCertificateExpiryChecking:
    """Test certificate expiry detection (AC: #1, #4)."""

    def test_valid_certificate_no_warning(self, valid_cert_files, caplog):
        """Test that valid certificate with >30 days doesn't warn."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        time_remaining = manager.check_expiry()

        assert time_remaining is not None
        assert time_remaining.days > 30
        assert "expiring" not in caplog.text.lower()

    def test_expiring_certificate_warning(self, expiring_cert_files, caplog):
        """Test that certificate expiring within 30 days logs warning (AC: #1)."""
        manager = CertificateManager(
            cert_path=expiring_cert_files["cert_path"],
            key_path=expiring_cert_files["key_path"],
            enforce_key_permissions=False,
        )

        time_remaining = manager.check_expiry()

        assert time_remaining is not None
        assert time_remaining.days <= 30
        assert "expiring" in caplog.text.lower() or "rotation recommended" in caplog.text.lower()

    def test_expired_certificate_error(self, temp_cert_dir):
        """Test that expired certificate raises error with rotation steps (AC: #4)."""
        # Create certificate that expired, but allow initialization
        key = TestCertificateGeneration.generate_private_key()
        not_before = datetime.utcnow() - timedelta(days=31)
        cert = TestCertificateGeneration.generate_certificate(
            key, days_valid=30, not_valid_before=not_before
        )

        cert_path = temp_cert_dir / "cert.pem"
        key_path = temp_cert_dir / "key.pem"

        TestCertificateGeneration.write_certificate(cert, cert_path)
        TestCertificateGeneration.write_private_key(key, key_path)

        # Should fail at initialization due to startup check
        with pytest.raises(CertificateLoadError) as exc_info:
            CertificateManager(
                cert_path=str(cert_path),
                key_path=str(key_path),
                enforce_key_permissions=False,
            )

        # Error should indicate expiry and rotation procedure
        assert "expired" in str(exc_info.value).lower()
        assert (
            "rotation" in str(exc_info.value).lower() or "procedure" in str(exc_info.value).lower()
        )

    def test_expired_at_startup_fails_fast(self, expired_cert_files):
        """Test startup check fails fast if certificates expired."""
        with pytest.raises(CertificateLoadError):
            CertificateManager(
                cert_path=expired_cert_files["cert_path"],
                key_path=expired_cert_files["key_path"],
                enforce_key_permissions=False,
            )

    def test_clock_skew_detection(self, temp_cert_dir, caplog):
        """Test detection of system clock skew."""
        key = TestCertificateGeneration.generate_private_key()
        # Certificate valid starting 1 day in the future
        not_before = datetime.utcnow() + timedelta(days=1)
        cert = TestCertificateGeneration.generate_certificate(
            key, days_valid=365, not_valid_before=not_before
        )

        cert_path = temp_cert_dir / "future-cert.pem"
        key_path = temp_cert_dir / "key.pem"

        TestCertificateGeneration.write_certificate(cert, cert_path)
        TestCertificateGeneration.write_private_key(key, key_path)

        manager = CertificateManager(
            cert_path=str(cert_path),
            key_path=str(key_path),
            enforce_key_permissions=False,
        )

        manager.check_expiry()

        # Should log warning about clock skew
        assert "not yet valid" in caplog.text.lower() or "clock" in caplog.text.lower()


class TestKeyCertificateMatching:
    """Test key/certificate matching validation."""

    def test_matching_key_cert_succeeds(self, valid_cert_files):
        """Test that matching key and certificate succeed."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )
        assert manager is not None

    def test_mismatched_key_cert_fails(self, valid_cert_files, temp_cert_dir):
        """Test that mismatched key and certificate fail validation."""
        # Generate different key
        different_key = TestCertificateGeneration.generate_private_key()
        different_key_path = temp_cert_dir / "different-key.pem"
        TestCertificateGeneration.write_private_key(different_key, different_key_path)

        with pytest.raises(CertificateLoadError, match="does not match"):
            CertificateManager(
                cert_path=valid_cert_files["cert_path"],
                key_path=str(different_key_path),
            )


class TestCertificateReload:
    """Test certificate reload mechanism (AC: #2, #5)."""

    def test_successful_reload(self, valid_cert_files, temp_cert_dir):
        """Test successful certificate reload (AC: #2)."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        old_expiry = manager._cert.not_valid_after_utc

        # Generate new certificate with different expiry
        new_key = TestCertificateGeneration.generate_private_key()
        new_cert = TestCertificateGeneration.generate_certificate(new_key, days_valid=400)

        TestCertificateGeneration.write_certificate(new_cert, valid_cert_files["cert_path"])
        TestCertificateGeneration.write_private_key(new_key, valid_cert_files["key_path"])

        # Reload
        success = manager.reload()

        assert success is True
        assert manager._cert.not_valid_after_utc != old_expiry

    def test_corrupt_reload_fallback(self, valid_cert_files, caplog):
        """Test fallback to previous certificate on corrupt reload (AC: #5)."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        old_expiry = manager._cert.not_valid_after_utc

        # Corrupt the certificate file
        with open(valid_cert_files["cert_path"], "w") as f:
            f.write("CORRUPTED DATA")

        # Attempt reload
        success = manager.reload()

        assert success is False
        assert manager._cert.not_valid_after_utc == old_expiry
        assert "failed to reload" in caplog.text.lower()
        assert "previous valid certificate" in caplog.text.lower()


class TestObserverPattern:
    """Test observer pattern for certificate updates."""

    def test_observer_registration(self, valid_cert_files):
        """Test observer callback registration."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        callback = Mock()
        manager.register_observer(callback)

        assert callback in manager._observers

    def test_observer_notification_on_reload(self, valid_cert_files):
        """Test observers notified on successful reload."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        callback = Mock()
        manager.register_observer(callback)

        # Generate new certificate
        new_key = TestCertificateGeneration.generate_private_key()
        new_cert = TestCertificateGeneration.generate_certificate(new_key, days_valid=400)

        TestCertificateGeneration.write_certificate(new_cert, valid_cert_files["cert_path"])
        TestCertificateGeneration.write_private_key(new_key, valid_cert_files["key_path"])

        # Reload
        manager.reload()

        # Observer should be called with new SSL context
        callback.assert_called_once()
        args = callback.call_args[0]
        assert isinstance(args[0], ssl.SSLContext)

    def test_observer_not_notified_on_failed_reload(self, valid_cert_files):
        """Test observers not notified on failed reload."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        callback = Mock()
        manager.register_observer(callback)

        # Corrupt certificate
        with open(valid_cert_files["cert_path"], "w") as f:
            f.write("CORRUPTED")

        manager.reload()

        # Observer should not be called
        callback.assert_not_called()


class TestBackgroundMonitoring:
    """Test background file monitoring for hot-reload."""

    def test_start_monitoring(self, valid_cert_files):
        """Test starting background monitoring thread."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            poll_interval=1,  # 1 second for testing
        )

        manager.start_monitoring()

        assert manager._running is True
        assert manager._monitor_thread is not None
        assert manager._monitor_thread.is_alive()

        manager.stop_monitoring()

    def test_stop_monitoring(self, valid_cert_files):
        """Test stopping background monitoring thread."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            poll_interval=1,
        )

        manager.start_monitoring()
        manager.stop_monitoring()

        assert manager._running is False

    def test_file_modification_triggers_reload(self, valid_cert_files):
        """Test that file modification triggers automatic reload."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            poll_interval=1,  # 1 second
        )

        old_expiry = manager._cert.not_valid_after_utc

        manager.start_monitoring()

        # Wait a bit for monitor thread to start
        time.sleep(0.5)

        # Modify certificate
        new_key = TestCertificateGeneration.generate_private_key()
        new_cert = TestCertificateGeneration.generate_certificate(new_key, days_valid=400)
        TestCertificateGeneration.write_certificate(new_cert, valid_cert_files["cert_path"])
        TestCertificateGeneration.write_private_key(new_key, valid_cert_files["key_path"])

        # Wait for poll interval + processing time
        time.sleep(2)

        manager.stop_monitoring()

        # Certificate should be reloaded
        assert manager._cert.not_valid_after_utc != old_expiry

    def test_background_monitoring_calls_check_expiry(self, valid_cert_files):
        """Test that background monitoring periodically calls check_expiry."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            poll_interval=1,  # 1 second
        )

        # Mock _emit_metric to track calls
        metric_calls = []
        original_emit = manager._emit_metric

        def track_emit(name, value):
            metric_calls.append((name, value))
            original_emit(name, value)

        manager._emit_metric = track_emit

        manager.start_monitoring()

        # Wait for 2 poll intervals
        time.sleep(2.5)

        manager.stop_monitoring()

        # check_expiry should have been called at least twice
        # (once at startup, at least once by background thread)
        days_remaining_calls = [c for c in metric_calls if "days_remaining" in c[0]]
        assert (
            len(days_remaining_calls) >= 2
        ), f"Expected at least 2 days_remaining metric calls, got {len(days_remaining_calls)}"


class TestSSLContextGeneration:
    """Test SSL context generation for OTLP exporters."""

    def test_ssl_context_creation(self, valid_cert_files):
        """Test SSL context is created correctly."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        ssl_context = manager.get_ssl_context()

        assert isinstance(ssl_context, ssl.SSLContext)

    def test_ssl_context_with_ca_bundle(self, valid_cert_files, temp_cert_dir):
        """Test SSL context creation with CA bundle."""
        # Create CA certificate
        ca_key = TestCertificateGeneration.generate_private_key()
        ca_cert = TestCertificateGeneration.generate_certificate(ca_key, subject_name="Test CA")
        ca_path = temp_cert_dir / "ca-bundle.pem"
        TestCertificateGeneration.write_certificate(ca_cert, ca_path)

        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
            ca_bundle_path=str(ca_path),
        )

        ssl_context = manager.get_ssl_context()
        assert isinstance(ssl_context, ssl.SSLContext)

    def test_get_ssl_context_before_load_fails(self, temp_cert_dir):
        """Test that get_ssl_context fails if certificate not loaded."""
        # Create a manager but force _ssl_context to None to simulate load failure
        key = TestCertificateGeneration.generate_private_key()
        cert = TestCertificateGeneration.generate_certificate(key)

        cert_path = temp_cert_dir / "cert.pem"
        key_path = temp_cert_dir / "key.pem"

        TestCertificateGeneration.write_certificate(cert, cert_path)
        TestCertificateGeneration.write_private_key(key, key_path)

        manager = CertificateManager(cert_path=str(cert_path), key_path=str(key_path))

        # Force ssl_context to None to simulate failure scenario
        manager._ssl_context = None

        with pytest.raises(CertificateNotLoadedError, match="No certificate loaded"):
            manager.get_ssl_context()


class TestThreadSafety:
    """Test thread safety of certificate operations."""

    def test_concurrent_reload(self, valid_cert_files):
        """Test that concurrent reload operations are thread-safe."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        def reload_thread():
            for _ in range(5):
                manager.reload()

        threads = [threading.Thread(target=reload_thread) for _ in range(3)]
        for t in threads:
            t.start()
        for t in threads:
            t.join()

        # Should complete without errors
        assert manager is not None


class TestLoadTesting:
    """Test certificate rotation under high load."""

    def test_rotation_during_high_throughput(self, valid_cert_files):
        """Test certificate rotation during simulated high throughput (1000 req/s)."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        # Simulate high throughput: 1000 SSL context retrievals per second
        iterations = 1000
        start_time = time.time()

        def get_context_repeatedly():
            for _ in range(iterations // 10):  # Each thread does 100 iterations
                ctx = manager.get_ssl_context()
                assert isinstance(ctx, ssl.SSLContext)

        # Start 10 worker threads
        threads = [threading.Thread(target=get_context_repeatedly) for _ in range(10)]
        for t in threads:
            t.start()

        # Simulate rotation during load
        time.sleep(0.1)
        manager.reload()

        # Wait for all threads
        for t in threads:
            t.join()

        elapsed = time.time() - start_time

        # Should complete quickly (under 2 seconds)
        assert elapsed < 2.0
        # No telemetry should be lost (all threads completed successfully)


class TestInternalTelemetry:
    """Test internal telemetry emission."""

    def test_metric_emission_on_expiry_check(self, valid_cert_files):
        """Test that days_remaining metric is emitted."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        # Mock _emit_metric to verify it's called
        manager._emit_metric = Mock()

        manager.check_expiry()

        # Should emit mca.sdk.certificate.days_remaining metric
        manager._emit_metric.assert_called_once()
        args = manager._emit_metric.call_args[0]
        assert "days_remaining" in args[0]

    def test_event_emission_on_reload_success(self, valid_cert_files):
        """Test that reload success event is emitted."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        manager._emit_event = Mock()

        # Generate new certificate
        new_key = TestCertificateGeneration.generate_private_key()
        new_cert = TestCertificateGeneration.generate_certificate(new_key, days_valid=400)
        TestCertificateGeneration.write_certificate(new_cert, valid_cert_files["cert_path"])
        TestCertificateGeneration.write_private_key(new_key, valid_cert_files["key_path"])

        manager.reload()

        # Should emit certificate_reload_success event
        manager._emit_event.assert_called_once_with("certificate_reload_success")

    def test_event_emission_on_reload_failure(self, valid_cert_files):
        """Test that reload failure event is emitted."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        manager._emit_event = Mock()

        # Corrupt certificate
        with open(valid_cert_files["cert_path"], "w") as f:
            f.write("CORRUPTED")

        manager.reload()

        # Should emit certificate_reload_failure event
        manager._emit_event.assert_called_once()
        args = manager._emit_event.call_args[0]
        assert "failure" in args[0]


class TestCertificateLogging:
    """Test that certificates are never logged (security requirement)."""

    def test_no_certificate_contents_in_logs(self, valid_cert_files, caplog):
        """Test that certificate contents never appear in logs."""
        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        manager.check_expiry()
        manager.reload()

        # Read actual certificate content
        with open(valid_cert_files["cert_path"], "r") as f:
            cert_content = f.read()

        # Certificate PEM content should NOT be in logs
        assert "BEGIN CERTIFICATE" not in caplog.text
        assert cert_content not in caplog.text

    def test_fingerprint_logged_not_cert(self, valid_cert_files, caplog):
        """Test that only fingerprint/serial is logged, not full certificate."""
        # Set log level to capture INFO messages
        caplog.set_level(logging.INFO, logger="mca_sdk.security.certificates")

        manager = CertificateManager(
            cert_path=valid_cert_files["cert_path"],
            key_path=valid_cert_files["key_path"],
        )

        # Logs should contain serial number or fingerprint
        assert "serial" in caplog.text.lower() or "fingerprint" in caplog.text.lower()

        # But NOT the actual certificate
        assert "BEGIN CERTIFICATE" not in caplog.text
