mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 20:55:41 +02:00
fase 4
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
"""
|
||||
Test unitari per SmtpSender.
|
||||
|
||||
Verifica la costruzione del messaggio MIME senza connessioni SMTP reali.
|
||||
Il test del send() effettivo verso server reali è un test di integrazione
|
||||
(eseguito separatamente con flag --real-smtp).
|
||||
"""
|
||||
|
||||
import email as email_lib
|
||||
import email.policy
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
import pytest
|
||||
|
||||
# ─── Chiave test fissa – deve coincidere con ENCRYPTION_KEY ──────────────────
|
||||
_TEST_KEY_HEX = "b" * 64
|
||||
|
||||
# Imposta la variabile d'ambiente e invalida la cache prima di qualsiasi import
|
||||
import os as _os
|
||||
_os.environ["ENCRYPTION_KEY"] = _TEST_KEY_HEX
|
||||
_os.environ.setdefault("SECRET_KEY", "test-secret-worker")
|
||||
|
||||
# Invalida la cache di get_settings se già caricata
|
||||
try:
|
||||
from app.config import get_settings as _gs
|
||||
_gs.cache_clear()
|
||||
except Exception:
|
||||
pass
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
# ─── Fixtures helper ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _make_fake_mailbox():
|
||||
"""Crea un oggetto mailbox-like con attributi minimi per SmtpSender."""
|
||||
import base64
|
||||
import os
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
key = bytes.fromhex(_TEST_KEY_HEX)
|
||||
|
||||
def _enc(value: str) -> str:
|
||||
nonce = os.urandom(12)
|
||||
ct = AESGCM(key).encrypt(nonce, value.encode(), None)
|
||||
return base64.b64encode(nonce + ct).decode()
|
||||
|
||||
mailbox = MagicMock()
|
||||
mailbox.email_address = "test@pec.example.it"
|
||||
mailbox.smtp_host_enc = _enc("smtp.example.it")
|
||||
mailbox.smtp_port_enc = _enc("465")
|
||||
mailbox.smtp_user_enc = _enc("test@pec.example.it")
|
||||
mailbox.smtp_pass_enc = _enc("secret")
|
||||
mailbox.smtp_use_tls = True
|
||||
|
||||
return mailbox
|
||||
|
||||
|
||||
# ─── Test costruzione MIME ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestBuildMimeMessage:
|
||||
"""Verifica la costruzione del messaggio MIME con varie combinazioni."""
|
||||
|
||||
def _get_sender(self):
|
||||
"""Restituisce SmtpSender con mailbox mock."""
|
||||
# La chiave è già impostata a livello di modulo (_TEST_KEY_HEX)
|
||||
from app.config import get_settings
|
||||
get_settings.cache_clear() # forza rilettura env var
|
||||
|
||||
from app.smtp.sender import SmtpSender
|
||||
return SmtpSender(_make_fake_mailbox())
|
||||
|
||||
def test_build_plain_text_only(self):
|
||||
"""Verifica struttura MIME con solo testo semplice."""
|
||||
sender = self._get_sender()
|
||||
msg, msg_id = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Test oggetto",
|
||||
body_text="Testo del corpo",
|
||||
)
|
||||
|
||||
assert isinstance(msg, MIMEMultipart)
|
||||
assert msg["From"] == "test@pec.example.it"
|
||||
assert msg["To"] == "dest@pec.it"
|
||||
assert msg["Subject"] == "Test oggetto"
|
||||
assert msg_id.startswith("<")
|
||||
assert msg_id.endswith(">")
|
||||
assert "Cc" not in msg
|
||||
|
||||
def test_build_with_cc(self):
|
||||
"""Verifica che il campo Cc venga incluso correttamente."""
|
||||
sender = self._get_sender()
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest1@pec.it"],
|
||||
cc_addresses=["cc@pec.it", "cc2@pec.it"],
|
||||
subject="Test Cc",
|
||||
body_text="corpo",
|
||||
)
|
||||
|
||||
assert "Cc" in msg
|
||||
assert "cc@pec.it" in msg["Cc"]
|
||||
assert "cc2@pec.it" in msg["Cc"]
|
||||
|
||||
def test_build_multiple_to(self):
|
||||
"""Verifica destinatari multipli nel campo To."""
|
||||
sender = self._get_sender()
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest1@pec.it", "dest2@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Multi dest",
|
||||
body_text="corpo",
|
||||
)
|
||||
|
||||
assert "dest1@pec.it" in msg["To"]
|
||||
assert "dest2@pec.it" in msg["To"]
|
||||
|
||||
def test_build_with_html(self):
|
||||
"""Verifica che corpo HTML venga aggiunto come parte MIME."""
|
||||
sender = self._get_sender()
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Test HTML",
|
||||
body_text="Testo semplice",
|
||||
body_html="<p>Testo <b>HTML</b></p>",
|
||||
)
|
||||
|
||||
# Trova le parti del messaggio
|
||||
raw = msg.as_string()
|
||||
assert "text/plain" in raw
|
||||
assert "text/html" in raw
|
||||
|
||||
def test_build_with_attachment(self):
|
||||
"""Verifica che un allegato venga incluso nel messaggio."""
|
||||
sender = self._get_sender()
|
||||
attachments = [
|
||||
{
|
||||
"filename": "documento.pdf",
|
||||
"content": b"%PDF-1.4 fake content",
|
||||
"content_type": "application/pdf",
|
||||
}
|
||||
]
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Test allegato",
|
||||
body_text="Vedi allegato",
|
||||
attachments=attachments,
|
||||
)
|
||||
|
||||
raw = msg.as_string()
|
||||
assert "documento.pdf" in raw
|
||||
|
||||
def test_build_multiple_attachments(self):
|
||||
"""Verifica più allegati in un unico messaggio."""
|
||||
sender = self._get_sender()
|
||||
attachments = [
|
||||
{"filename": "file1.txt", "content": b"contenuto 1", "content_type": "text/plain"},
|
||||
{"filename": "file2.txt", "content": b"contenuto 2", "content_type": "text/plain"},
|
||||
]
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Multi allegati",
|
||||
body_text="Due allegati",
|
||||
attachments=attachments,
|
||||
)
|
||||
|
||||
raw = msg.as_string()
|
||||
assert "file1.txt" in raw
|
||||
assert "file2.txt" in raw
|
||||
|
||||
def test_message_id_unique(self):
|
||||
"""Verifica che ogni messaggio abbia un Message-ID unico."""
|
||||
sender = self._get_sender()
|
||||
_, id1 = sender.build_mime_message(
|
||||
to_addresses=["a@pec.it"], cc_addresses=[], subject="A", body_text="a"
|
||||
)
|
||||
_, id2 = sender.build_mime_message(
|
||||
to_addresses=["b@pec.it"], cc_addresses=[], subject="B", body_text="b"
|
||||
)
|
||||
assert id1 != id2
|
||||
|
||||
def test_required_headers_present(self):
|
||||
"""Verifica che tutti gli header obbligatori siano presenti."""
|
||||
sender = self._get_sender()
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Test headers",
|
||||
body_text="corpo",
|
||||
)
|
||||
|
||||
required_headers = ["From", "To", "Subject", "Date", "Message-ID", "MIME-Version"]
|
||||
for header in required_headers:
|
||||
assert header in msg, f"Header mancante: {header}"
|
||||
|
||||
def test_eml_bytes_serializable(self):
|
||||
"""Verifica che il messaggio sia serializzabile in bytes."""
|
||||
sender = self._get_sender()
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Serializzazione",
|
||||
body_text="corpo",
|
||||
)
|
||||
|
||||
raw = msg.as_bytes()
|
||||
assert len(raw) > 0
|
||||
assert isinstance(raw, bytes)
|
||||
|
||||
def test_empty_body_creates_valid_message(self):
|
||||
"""Verifica che un messaggio con corpo vuoto sia comunque valido."""
|
||||
sender = self._get_sender()
|
||||
msg, _ = sender.build_mime_message(
|
||||
to_addresses=["dest@pec.it"],
|
||||
cc_addresses=[],
|
||||
subject="Corpo vuoto",
|
||||
body_text="",
|
||||
)
|
||||
|
||||
raw = msg.as_bytes()
|
||||
assert len(raw) > 0
|
||||
|
||||
|
||||
# ─── Test decifrazione credenziali ───────────────────────────────────────────
|
||||
|
||||
|
||||
class TestDecryptSmtpCredentials:
|
||||
"""Verifica la decifrazione delle credenziali SMTP."""
|
||||
|
||||
def test_decrypt_returns_correct_values(self):
|
||||
"""Le credenziali decifrate devono corrispondere ai valori originali."""
|
||||
from app.config import get_settings
|
||||
get_settings.cache_clear()
|
||||
|
||||
from app.smtp.sender import decrypt_smtp_credentials
|
||||
|
||||
mailbox = _make_fake_mailbox()
|
||||
creds = decrypt_smtp_credentials(mailbox)
|
||||
|
||||
assert creds["host"] == "smtp.example.it"
|
||||
assert creds["port"] == 465
|
||||
assert creds["user"] == "test@pec.example.it"
|
||||
assert creds["password"] == "secret"
|
||||
assert creds["use_tls"] is True
|
||||
|
||||
def test_wrong_key_raises_error(self):
|
||||
"""Una chiave errata deve sollevare un'eccezione."""
|
||||
import os
|
||||
from app.config import get_settings
|
||||
|
||||
# Imposta chiave sbagliata
|
||||
os.environ["ENCRYPTION_KEY"] = "a" * 64
|
||||
get_settings.cache_clear()
|
||||
|
||||
from app.smtp.sender import decrypt_smtp_credentials
|
||||
mailbox = _make_fake_mailbox() # cifrato con chiave "b"*64
|
||||
|
||||
with pytest.raises(Exception):
|
||||
decrypt_smtp_credentials(mailbox)
|
||||
|
||||
# Ripristina chiave corretta per test successivi
|
||||
os.environ["ENCRYPTION_KEY"] = _TEST_KEY_HEX
|
||||
get_settings.cache_clear()
|
||||
Reference in New Issue
Block a user