using System.Security.Cryptography;
using MimeKit;
using Omika.Core.Interfaces;
using Omika.Core.Services;
namespace Omika.Tests.Services;
///
/// 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.
///
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 = $")}\")
";
(int imgCount, int dataUriCount, IReadOnlyList 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 = $")}\") | 30/01/2026 |
" +
$")}\") | 31/01/2026 |
";
// Étape 1 : APERÇU (serveur → mobile).
(_, _, IReadOnlyList apercu) = EmailInlineDiagnostic.Analyser(preview);
// Étape 2 : REQUÊTE (le mobile renvoie le HTML inchangé).
string request = preview;
(_, _, IReadOnlyList requete) = EmailInlineDiagnostic.Analyser(request);
// Étape 3 : SMTP (data URI → ressource CID).
(string htmlSmtp, IReadOnlyList 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 imagesMime = mime.BodyParts.OfType()
.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 = $")}\")
";
// Le mobile ne renvoie qu'UNE image (ex. troncature) → la seconde disparaît.
string request = $"
";
(_, _, IReadOnlyList apercu) = EmailInlineDiagnostic.Analyser(preview);
(_, _, IReadOnlyList 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);
}
}