Files
Omika/tests/Omika.Tests/Services/EmailRoundtripDiagnosticTests.cs
T
2026-10-23 18:46:50 +02:00

113 lines
5.1 KiB
C#

using System.Security.Cryptography;
using MimeKit;
using Omika.Core.Interfaces;
using Omika.Core.Services;
namespace Omika.Tests.Services;
/// <summary>
/// Diagnostic roundtrip : suit la même image (par SHA256) à travers
/// APERÇU (data URI) → REQUÊTE mobile (écho) → SMTP (ressource CID), pour
/// identifier précisément l'étape où une image disparaîtrait. Aucun contenu
/// n'est journalisé — uniquement des empreintes.
/// </summary>
public class EmailRoundtripDiagnosticTests
{
private static byte[] JpegA() => [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46];
private static byte[] JpegB() => [0xFF, 0xD8, 0xFF, 0xE1, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
private static string DataUri(byte[] bytes) => $"data:image/jpeg;base64,{Convert.ToBase64String(bytes)}";
private static byte[] Contenu(MimePart part)
{
using var ms = new MemoryStream();
part.Content.DecodeTo(ms);
return ms.ToArray();
}
private static string Sha256(byte[] data)
=> Convert.ToHexString(SHA256.HashData(data));
// ------------------------------------------------------------------
// L'analyseur produit des empreintes déterministes (sans contenu)
// ------------------------------------------------------------------
[Fact]
public void Analyser_CompteEtEmpreintes_Deterministes()
{
string html = $"<img src=\"{DataUri(JpegA())}\"><img src=\"{DataUri(JpegB())}\">";
(int imgCount, int dataUriCount, IReadOnlyList<EmailInlineDiagnostic.ImageTrace> images) =
EmailInlineDiagnostic.Analyser(html);
Assert.Equal(2, imgCount);
Assert.Equal(2, dataUriCount);
Assert.Equal(2, images.Count);
Assert.All(images, i => Assert.Equal("image/jpeg", i.Mime));
Assert.Equal(Sha256(JpegA()), images[0].Sha256);
Assert.Equal(Sha256(JpegB()), images[1].Sha256);
Assert.DoesNotContain("base64", images[0].Sha256);
Assert.DoesNotContain(Convert.ToBase64String(JpegA()), images[0].Sha256);
}
// ------------------------------------------------------------------
// Roundtrip : APERÇU → REQUÊTE → SMTP (mêmes empreintes)
// ------------------------------------------------------------------
[Fact]
public void Roundtrip_HashApercu_Request_Smtp_Identiques()
{
string preview = $"<table><tr><td><img src=\"{DataUri(JpegA())}\"></td><td>30/01/2026</td></tr>" +
$"<tr><td><img src=\"{DataUri(JpegB())}\"></td><td>31/01/2026</td></tr></table>";
// Étape 1 : APERÇU (serveur → mobile).
(_, _, IReadOnlyList<EmailInlineDiagnostic.ImageTrace> apercu) = EmailInlineDiagnostic.Analyser(preview);
// Étape 2 : REQUÊTE (le mobile renvoie le HTML inchangé).
string request = preview;
(_, _, IReadOnlyList<EmailInlineDiagnostic.ImageTrace> requete) = EmailInlineDiagnostic.Analyser(request);
// Étape 3 : SMTP (data URI → ressource CID).
(string htmlSmtp, IReadOnlyList<EmailSmtpInline.Ressource> ressources) = EmailSmtpInline.Convertir(request);
Assert.Equal(2, ressources.Count);
Assert.DoesNotContain("data:", htmlSmtp);
// Étape 4 : MimeMessage réel (LinkedResources).
MimeMessage mime = MailKitEmailSender.ConstruireMime(
new EmailMessage { Sujet = "S", CorpsHtml = request, Destinataires = ["d@x.fr"] },
"exp@x.fr");
List<MimePart> imagesMime = mime.BodyParts.OfType<MimePart>()
.Where(p => p.ContentType.MediaType == "image").ToList();
Assert.Equal(2, imagesMime.Count);
// Correspondance par SHA256, photo par photo.
Assert.Equal(apercu[0].Sha256, requete[0].Sha256);
Assert.Equal(apercu[1].Sha256, requete[1].Sha256);
Assert.Equal(apercu[0].Sha256, Sha256(ressources[0].Data));
Assert.Equal(apercu[1].Sha256, Sha256(ressources[1].Data));
Assert.Equal(apercu[0].Sha256, Sha256(Contenu(imagesMime[0])));
Assert.Equal(apercu[1].Sha256, Sha256(Contenu(imagesMime[1])));
}
// ------------------------------------------------------------------
// Perte simulée : une image absente de la requête → détectée par hash
// ------------------------------------------------------------------
[Fact]
public void Roundtrip_ImageManquante_DetecteeParHash()
{
string preview = $"<img src=\"{DataUri(JpegA())}\"><img src=\"{DataUri(JpegB())}\">";
// Le mobile ne renvoie qu'UNE image (ex. troncature) → la seconde disparaît.
string request = $"<img src=\"{DataUri(JpegA())}\">";
(_, _, IReadOnlyList<EmailInlineDiagnostic.ImageTrace> apercu) = EmailInlineDiagnostic.Analyser(preview);
(_, _, IReadOnlyList<EmailInlineDiagnostic.ImageTrace> requete) = EmailInlineDiagnostic.Analyser(request);
Assert.Equal(2, apercu.Count);
Assert.Single(requete);
Assert.Equal(apercu[0].Sha256, requete[0].Sha256);
// La photo B est perdue au roundtrip : détectable par absence d'empreinte.
Assert.DoesNotContain(requete, i => i.Sha256 == apercu[1].Sha256);
}
}