using Omika.Core.Compatibility;
using Omika.Core.Contracts.Common;
using Omika.Core.Contracts.Documents;
using Omika.Core.Contracts.Interventions;
using Omika.Core.Contracts.Mobile.Sync;
using Omika.Core.Domain;
using Omika.Core.Domain.Entities.Documents;
using Omika.Core.Interfaces;
using Omika.Core.Services;
using Omika.Data.Repositories;
namespace Omika.Tests.Services;
///
/// Prouve que trois rapports de même nom produisent trois fichiers distincts via
/// le mécanisme standard des documents (),
/// SANS GUID dans le nom physique, avec nom en base == nom physique.
///
public class RapportCreationServiceTests : IDisposable
{
private readonly string _dir;
public RapportCreationServiceTests()
{
_dir = Path.Combine(Path.GetTempPath(), "omika-rapport-collision", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_dir);
}
public void Dispose()
{
if (Directory.Exists(_dir))
{
Directory.Delete(_dir, recursive: true);
}
}
private sealed class FakeModeleRapportRepository : IModeleRapportRepository
{
public Task> GetAllAsync(CancellationToken ct = default)
=> Task.FromResult>([]);
public Task GetByIdAsync(string id, CancellationToken ct = default)
=> Task.FromResult(null);
public Task GetModeleDocxAsync(long modeleId, CancellationToken ct = default)
=> Task.FromResult([1, 2, 3]);
}
private sealed class FakePdfService : IRapportPdfService
{
private int _calls;
public Task GenererPdfAsync(byte[] modeleDocx, IReadOnlyList champsRemplis, CancellationToken ct = default)
{
// Contenu distinct à chaque génération pour prouver l'absence d'écrasement.
_calls++;
return Task.FromResult(new byte[] { (byte)_calls, 9, 9, 9 });
}
}
private sealed class FakeRapportChampService : IRapportChampService
{
public Task> GetChampsAsync(
GetRapportChampsRequest request, bool toutesRub = false, CancellationToken ct = default)
=> Task.FromResult>([]);
public Task> GetChampsMailAsync(
GetRapportChampsRequest request, CancellationToken ct = default)
=> Task.FromResult>([]);
}
private sealed class FakeInterventionService : IInterventionService
{
public Task GetByIdAsync(string id, CancellationToken ct = default)
=> Task.FromResult(new InterventionDetailsDto
{
Id = id,
SourceReference = LegacyEntityReference.FromLegacyId(7)
});
public Task> SearchAsync(SearchInterventionsRequest request, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> SearchDetailsAsync(SearchInterventionsRequest request, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task CreateAsync(CreateInterventionRequest request, CancellationToken ct = default, AuditActor? auteur = null)
=> throw new NotImplementedException();
public Task UpdateAsync(string id, UpdateInterventionRequest request, CancellationToken ct = default, AuditActor? auteur = null)
=> throw new NotImplementedException();
public Task ArchiveAsync(string id, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> GetNotesAsync(string interventionId, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> GetNotesAdministrativesAsync(string interventionId, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task AddNoteAsync(string interventionId, string contenu, AuditActor? auteur, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> GetEmailsAsync(string interventionId, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task AddEmailAsync(string interventionId, CreateEmailInterventionRequest request, AuditActor? auteur, CancellationToken ct = default)
=> throw new NotImplementedException();
}
private sealed class FakeDocumentRepository : IDocumentRepository
{
private readonly string _directory;
public FakeDocumentRepository(string directory) => _directory = directory;
public List Documents { get; } = [];
public Task WriteDocumentFileAsync(
long interventionId, long categorieId, string fileName, byte[] content, CancellationToken ct = default)
{
// Réutilise EXACTEMENT le mécanisme standard de collision des documents.
string? finalName = DocumentPathResolver.WriteUniqueFile(_directory, fileName, content);
return Task.FromResult(
finalName is null ? null : new DocumentWriteResult(_directory, finalName));
}
public Task CreateAsync(Document document, CancellationToken ct = default)
{
Documents.Add(document);
return Task.FromResult(document);
}
public Task ResolveCreationUserAsync(AuditActor? auteur, CancellationToken ct = default)
=> Task.FromResult(1L);
public Task ResolveCategoryIdAsync(string categorieCle, CancellationToken ct = default)
=> Task.FromResult(2L);
public Task> SearchAsync(PagedRequest request, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> GetByInterventionAsync(PhysicalEntityIdentity interventionIdentity, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> GetByInterventionsAsync(IReadOnlyList numericInterventionIds, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task GetByIdAsync(string id, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task GetByPhysicalIdentityAsync(PhysicalEntityIdentity identity, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task ArchiveAsync(string id, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task ResolveDocumentFullPathAsync(Document document, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task> GetNotesAsync(long documentId, CancellationToken ct = default)
=> throw new NotImplementedException();
public Task AddNoteAsync(long documentId, string contenu, long creationUser, CancellationToken ct = default)
=> throw new NotImplementedException();
}
[Fact]
public async Task TroisRapportsMemeNom_TroisFichiersDistincts_SansGuid()
{
var documentRepository = new FakeDocumentRepository(_dir);
var service = new RapportCreationService(
new FakeModeleRapportRepository(),
new FakePdfService(),
documentRepository,
new FakeInterventionService(),
new FakeRapportChampService());
DocumentSummaryDto? premier = await service.CreerAsync(new CreateRapportRequest
{
ModeleId = "42",
InterventionId = Guid.NewGuid().ToString(),
Nom = "Rapport intervention",
RapportGuid = Guid.NewGuid()
}, auteur: null);
DocumentSummaryDto? second = await service.CreerAsync(new CreateRapportRequest
{
ModeleId = "42",
InterventionId = Guid.NewGuid().ToString(),
Nom = "Rapport intervention",
RapportGuid = Guid.NewGuid()
}, auteur: null);
DocumentSummaryDto? troisieme = await service.CreerAsync(new CreateRapportRequest
{
ModeleId = "42",
InterventionId = Guid.NewGuid().ToString(),
Nom = "Rapport intervention",
RapportGuid = Guid.NewGuid()
}, auteur: null);
Assert.NotNull(premier);
Assert.NotNull(second);
Assert.NotNull(troisieme);
// Noms physiques = noms en base, sans GUID.
Assert.Equal("Rapport intervention.pdf", premier!.Nom);
Assert.Equal("Rapport intervention_1.pdf", second!.Nom);
Assert.Equal("Rapport intervention_2.pdf", troisieme!.Nom);
// Les trois fichiers existent simultanément.
string[] files = Directory.GetFiles(_dir);
Assert.Equal(3, files.Length);
Assert.Contains(Path.Combine(_dir, "Rapport intervention.pdf"), files);
Assert.Contains(Path.Combine(_dir, "Rapport intervention_1.pdf"), files);
Assert.Contains(Path.Combine(_dir, "Rapport intervention_2.pdf"), files);
// Chaque fichier a son propre contenu (aucun écrasement).
byte[] content1 = await File.ReadAllBytesAsync(Path.Combine(_dir, "Rapport intervention.pdf"));
byte[] content2 = await File.ReadAllBytesAsync(Path.Combine(_dir, "Rapport intervention_1.pdf"));
byte[] content3 = await File.ReadAllBytesAsync(Path.Combine(_dir, "Rapport intervention_2.pdf"));
Assert.Equal((byte)1, content1[0]);
Assert.Equal((byte)2, content2[0]);
Assert.Equal((byte)3, content3[0]);
// DB : nom en base == nom physique (3 enregistrements distincts).
Assert.Equal(3, documentRepository.Documents.Count);
Assert.Equal("Rapport intervention.pdf", documentRepository.Documents[0].Nom);
Assert.Equal("Rapport intervention_1.pdf", documentRepository.Documents[1].Nom);
Assert.Equal("Rapport intervention_2.pdf", documentRepository.Documents[2].Nom);
}
}