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

205 lines
9.7 KiB
C#

using Omika.Core.Compatibility;
using Omika.Core.Contracts.Common;
using Omika.Core.Contracts.Mobile.Sync;
using Omika.Core.Contracts.Organisation;
using Omika.Core.Contracts.Planning;
using Omika.Core.Contracts.Sites;
using Omika.Core.Domain;
using Omika.Core.Domain.Commands;
using Omika.Core.Domain.Entities.Clients;
using Omika.Core.Interfaces;
using Omika.Core.Interfaces.Mobile;
using Omika.Core.Services;
using Omika.Core.Services.Mobile;
namespace Omika.Tests.Services;
/// <summary>
/// Tests du SiteService : les relations du site (contacts, clients, rendez-vous)
/// doivent être résolues par LI_PKID numérique, jamais par le GUID public Omika.
/// </summary>
public class SiteServiceTests
{
private sealed class FakeSiteRepository : ISiteRepository
{
public long SiteNumericId { get; set; } = 42;
public string? LastContactsId { get; private set; }
public string? LastClientsId { get; private set; }
public Task<Site> CreateAsync(SiteCreateCommand command, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<PagedResult<Site>> SearchAsync(SiteSearchCriteria criteria, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<PagedResult<Site>> SearchDetailsAsync(SiteSearchCriteria criteria, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<Site?> GetDetailsAsync(string id, CancellationToken cancellationToken = default)
{
var site = new Site();
site.SourceReference = new LegacyEntityReference(DatabaseSchemaVersion.Omika, NumericId: SiteNumericId);
return Task.FromResult<Site?>(site);
}
public Task<IReadOnlyList<Contact>> GetContactsAsync(string id, CancellationToken cancellationToken = default)
{
LastContactsId = id;
return Task.FromResult<IReadOnlyList<Contact>>([]);
}
public Task<IReadOnlyList<Client>> GetClientsAsync(string id, CancellationToken cancellationToken = default)
{
LastClientsId = id;
return Task.FromResult<IReadOnlyList<Client>>([]);
}
public Task<Site?> UpdateAsync(string id, SiteUpdateCommand command, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<bool> ArchiveAsync(string id, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
}
private sealed class FakeRendezVousService : IRendezVousService
{
public IReadOnlyList<RendezVousDetailsDto> DetailsResults { get; set; } = [];
public Task<PagedResult<RendezVousSummaryDto>> SearchAsync(SearchRendezVousRequest request, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<PagedResult<RendezVousDetailsDto>> SearchDetailsAsync(SearchRendezVousRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(new PagedResult<RendezVousDetailsDto>(
DetailsResults, 1, DetailsResults.Count, DetailsResults.Count, DetailsResults.Count == 0 ? 0 : 1));
public Task<RendezVousDetailsDto?> GetByIdAsync(string id, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<RendezVousDetailsDto> CreateAsync(CreateRendezVousRequest request, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
public Task<RendezVousDetailsDto> UpdateAsync(string id, UpdateRendezVousRequest request, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
public Task CancelAsync(string id, AnnulationRendezVousDto annulation, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
public Task ArchiveAsync(string id, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
public Task<bool> SetTempsValideAsync(string rendezVousId, string collaborateurId, bool tempsValide, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
}
private sealed class FakeCollaborateurService : ICollaborateurService
{
public Task<PagedResult<CollaborateurSummaryDto>> SearchAsync(SearchCollaborateurRequest request, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<PagedResult<CollaborateurDetailsDto>> SearchDetailsAsync(SearchCollaborateurRequest request, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<CollaborateurDetailsDto?> GetByIdAsync(string id, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<CollaborateurDetailsDto> CreateAsync(CreateCollaborateurRequest request, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
public Task<CollaborateurDetailsDto> UpdateAsync(string id, UpdateCollaborateurRequest request, CancellationToken cancellationToken = default, AuditActor? auteur = null)
=> throw new NotSupportedException();
public Task<bool> ArchiveAsync(string id, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<IReadOnlyList<long>> GetSocieteIdsAsync(long collaborateurId, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<IReadOnlyList<long>> GetAllowedCollaborateurIdsAsync(long collaborateurId, bool includeSocietePrincipale, bool includeSocietes, bool includeEquipe, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<IReadOnlyList<RendezVousDetailsDto>> GetRendezVousAsync(string collaborateurId, DateTime debutMin, DateTime debutMax, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
public Task<IReadOnlyList<RendezVousDetailsDto>> GetRendezVousForCollaborateursAsync(IReadOnlyList<string> collaborateurIds, DateTime debutMin, DateTime debutMax, CancellationToken cancellationToken = default)
=> throw new NotSupportedException();
}
private sealed class FakeIdentityResolver : IMobileEntityIdentityResolver
{
public Guid CreateCanonicalIdentity(string entityType, PhysicalEntityIdentity physical)
=> physical.Guid ?? Guid.NewGuid();
public PhysicalEntityIdentity? ResolvePhysicalIdentity(string entityType, Guid publicGuid)
=> new PhysicalEntityIdentity { Guid = publicGuid };
public bool IsCanonical(string entityType, Guid publicGuid) => publicGuid != Guid.Empty;
}
[Fact]
public async Task GetContactsAsync_GuidPublic_ResoutLiPkidAvantLaRequete()
{
// Arrange : schéma Omika — le resolver retourne un GUID SANS NumericId.
var repo = new FakeSiteRepository { SiteNumericId = 42 };
var service = new SiteService(
repo, new FakeRendezVousService(), new FakeCollaborateurService(), new FakeIdentityResolver());
// Act : l'identité publique du site est un GUID (comme côté mobile Omika).
await service.GetContactsAsync(Guid.NewGuid().ToString("D"));
// Assert : le repository a reçu le PK numérique (42), jamais le GUID.
Assert.Equal("42", repo.LastContactsId);
}
[Fact]
public async Task GetClientsAsync_GuidPublic_ResoutLiPkidAvantLaRequete()
{
var repo = new FakeSiteRepository { SiteNumericId = 7 };
var service = new SiteService(
repo, new FakeRendezVousService(), new FakeCollaborateurService(), new FakeIdentityResolver());
await service.GetClientsAsync(Guid.NewGuid().ToString("D"));
Assert.Equal("7", repo.LastClientsId);
}
[Fact]
public async Task GetRendezVousAsync_RdvDateSurAffectation_RestitueLeRdvRabattu()
{
// Omika : RV_Date (racine) vide → Debut = default. La fiche lieu doit
// rabattre la date de l'affectation (RVC_DateDeb) AVANT l'éligibilité,
// comme la fiche intervention et le snapshot. Sans cela, un rendez-vous
// réellement daté est écarté et l'onglet « Rendez-vous » reste vide.
DateTime debutAffectation = new DateTime(2026, 8, 19, 10, 0, 0);
var rdvService = new FakeRendezVousService
{
DetailsResults =
[
new RendezVousDetailsDto
{
Id = Guid.NewGuid().ToString(),
Debut = default,
Collaborateurs =
[
new AffectationCollaborateurDto
{
PeriodeDebut = debutAffectation,
PeriodeFin = debutAffectation.AddHours(1.5)
}
]
}
]
};
var service = new SiteService(
new FakeSiteRepository { SiteNumericId = 42 },
rdvService,
new FakeCollaborateurService(),
new FakeIdentityResolver());
IReadOnlyList<RendezVousDetailsDto> result =
await service.GetRendezVousAsync(Guid.NewGuid().ToString("D"));
RendezVousDetailsDto item = Assert.Single(result);
Assert.Equal(debutAffectation, item.Debut);
}
}