using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using Omika.Core.Contracts.Common; using Omika.Core.Contracts.Documents; using Omika.Core.Contracts.Interventions; using Omika.Core.Domain; using Omika.Core.Domain.Entities.Documents; using Omika.Core.Interfaces; using Omika.Core.Services; namespace Omika.Tests.Services; /// /// Vérifie que la sortie du repository Omika (champ simple + champ « Tableau » /// avec ListeMembre et données JSON) est correctement substituée par /// : champ simple remplacé, tableau formaté, /// aucune chaîne JSON brute injectée. /// public class EmailCreationServiceTests { [Fact] public async Task ApercuAsync_TableauEtSimple_SubstitutionSansJsonBrut() { var modeleMailRepository = new FakeModeleMailRepository { Modele = new ModeleMail { Sujet = "$REFINTERNE$ - $TABRENDEZVOUS$" }, CorpsDocx = CreerDocx("Réf: $REFINTERNE$\nTableau: $TABRENDEZVOUS$") }; var rapportChampService = new FakeRapportChampService { Champs = [ new RapportChampDto { Rubrique = "$REFINTERNE$", ListeDonnesBD = "ABC-123" }, new RapportChampDto { Rubrique = "$TABRENDEZVOUS$", Type = "Tableau", ListeDonnesBD = "[{\"objet\":\"Objet A\"}]", ListeMembre = [ new RapportChampSousElementDto { Rubrique = "$TABRDV.OBJET$", MembreMob = "saObjet" } ] } ] }; var service = new EmailCreationService( modeleMailRepository, rapportChampService, new FakeInterventionService()); EmailModeleApercuDto? apercu = await service.ApercuAsync( new CreateEmailModeleRequest { ModeleId = "1", InterventionId = "guid" }); Assert.NotNull(apercu); Assert.Equal("ABC-123 - Objet A", apercu!.Sujet); Assert.Contains("ABC-123", apercu.Corps); Assert.Contains("Objet A", apercu.Corps); Assert.DoesNotContain("[{", apercu.Corps); Assert.DoesNotContain("$REFINTERNE$", apercu.Corps); Assert.DoesNotContain("$TABRENDEZVOUS$", apercu.Corps); } private static byte[] CreerDocx(string texte) { using var stream = new MemoryStream(); using (WordprocessingDocument doc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document)) { MainDocumentPart mainPart = doc.AddMainDocumentPart(); mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document(); Body body = new Body(); body.Append(new Paragraph(new Run(new Text(texte)))); mainPart.Document.Append(body); mainPart.Document.Save(); } return stream.ToArray(); } private sealed class FakeModeleMailRepository : IModeleMailRepository { public ModeleMail? Modele { get; set; } public byte[]? CorpsDocx { get; set; } public Task> GetAllAsync(CancellationToken cancellationToken = default) => Task.FromResult>(Modele is null ? [] : [Modele]); public Task GetByIdAsync(string id, CancellationToken cancellationToken = default) => Task.FromResult(Modele); public Task GetCorpsDocxAsync(long modeleMailId, CancellationToken cancellationToken = default) => Task.FromResult(CorpsDocx); } private sealed class FakeRapportChampService : IRapportChampService { public IReadOnlyList Champs { get; set; } = []; public Task> GetChampsAsync( GetRapportChampsRequest request, bool toutesRub = false, CancellationToken cancellationToken = default) => Task.FromResult(Champs); public Task> GetChampsMailAsync( GetRapportChampsRequest request, CancellationToken cancellationToken = default) => Task.FromResult(Champs); } private sealed class FakeInterventionService : IInterventionService { public Task> SearchAsync( SearchInterventionsRequest request, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task> SearchDetailsAsync( SearchInterventionsRequest request, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task GetByIdAsync( string id, CancellationToken cancellationToken = default) => Task.FromResult(null); public Task CreateAsync( CreateInterventionRequest request, CancellationToken cancellationToken = default, AuditActor? auteur = null) => throw new NotSupportedException(); public Task UpdateAsync( string id, UpdateInterventionRequest request, CancellationToken cancellationToken = default, AuditActor? auteur = null) => throw new NotSupportedException(); public Task ArchiveAsync(string id, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task> GetNotesAsync( string interventionId, CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task> GetNotesAdministrativesAsync( string interventionId, CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task AddNoteAsync( string interventionId, string contenu, AuditActor? auteur, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task> GetEmailsAsync( string interventionId, CancellationToken cancellationToken = default) => Task.FromResult>([]); public Task AddEmailAsync( string interventionId, CreateEmailInterventionRequest request, AuditActor? auteur, CancellationToken cancellationToken = default) => throw new NotSupportedException(); } }