Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user