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

1042 lines
36 KiB
C#

using System.Reflection;
using Omika.Core.Contracts.Common;
using Omika.Core.Contracts.Planning;
using Omika.Core.Domain;
using Omika.Core.Domain.Commands;
using Omika.Core.Domain.Entities.Planning;
using Omika.Core.Domain.ValueObjects;
using Omika.Core.Interfaces;
using Omika.Core.Services;
namespace Omika.Tests.Services;
/// <summary>
/// Tests unitaires de <see cref="RendezVousService"/> avec un fake
/// <see cref="IRendezVousRepository"/> en mémoire. Aucune base réelle.
/// </summary>
public class RendezVousServiceTests
{
// =========================================================================
// SEARCH
// =========================================================================
[Fact]
public async Task SearchAsync_CallsRepositoryOnce()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest());
Assert.Equal(1, repo.SearchCallCount);
}
[Fact]
public async Task SearchAsync_TransmitsPage()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { Page = 3 });
Assert.Equal(3, repo.LastSearchCriteria?.Page);
}
[Fact]
public async Task SearchAsync_TransmitsPageSize()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { PageSize = 50 });
Assert.Equal(50, repo.LastSearchCriteria?.PageSize);
}
[Fact]
public async Task SearchAsync_TransmitsSearch()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { Search = "test" });
Assert.Equal("test", repo.LastSearchCriteria?.Search);
}
[Fact]
public async Task SearchAsync_TransmitsTypeActivite()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { TypeActivite = "Réunion" });
Assert.Equal("Réunion", repo.LastSearchCriteria?.TypeActivite);
}
[Fact]
public async Task SearchAsync_TransmitsStatut()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { Statut = "Confirmé" });
Assert.Equal("Confirmé", repo.LastSearchCriteria?.Statut);
}
[Fact]
public async Task SearchAsync_TransmitsDebutMin()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
var debutMin = new DateTime(2026, 1, 1);
await service.SearchAsync(new SearchRendezVousRequest { DebutMin = debutMin });
Assert.Equal(debutMin, repo.LastSearchCriteria?.DebutMin);
}
[Fact]
public async Task SearchAsync_TransmitsEstArchive()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { EstArchive = true });
Assert.True(repo.LastSearchCriteria?.EstArchive);
}
[Fact]
public async Task SearchAsync_TransmitsSortBy()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { SortBy = "Date" });
Assert.Equal("Date", repo.LastSearchCriteria?.SortBy);
}
[Fact]
public async Task SearchAsync_TransmitsSortDescending()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.SearchAsync(new SearchRendezVousRequest { SortDescending = true });
Assert.True(repo.LastSearchCriteria?.SortDescending);
}
[Fact]
public async Task SearchAsync_TransmitsCancellationToken()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
using var cts = new CancellationTokenSource();
await service.SearchAsync(new SearchRendezVousRequest(), cts.Token);
Assert.Equal(cts.Token, repo.LastCancellationToken);
}
[Fact]
public async Task SearchAsync_EmptyResult_ReturnsEmptyPage()
{
var repo = new FakeRendezVousRepository
{
SearchResult = new PagedResult<RendezVous>([], 1, 20, 0, 0)
};
var service = new RendezVousService(repo);
PagedResult<RendezVousSummaryDto> result = await service.SearchAsync(
new SearchRendezVousRequest());
Assert.Empty(result.Items);
Assert.Equal(0, result.TotalCount);
Assert.Equal(0, result.TotalPages);
Assert.Equal(1, result.Page);
Assert.Equal(20, result.PageSize);
}
[Fact]
public async Task SearchAsync_SingleRendezVous_MapsToSummaryDto()
{
var rendezVous = new RendezVous
{
Titre = "Réunion",
TypeActivite = new ReferenceValue("Réunion") { Key = "REUNION" },
Statut = new ReferenceValue("Confirmé") { Key = "CONFIRME" },
Periode = new DateTimeRange(new DateTime(2026, 1, 15, 9, 0, 0), new DateTime(2026, 1, 15, 10, 0, 0))
};
var repo = new FakeRendezVousRepository
{
SearchResult = new PagedResult<RendezVous>([rendezVous], 1, 20, 1, 1)
};
var service = new RendezVousService(repo);
PagedResult<RendezVousSummaryDto> result = await service.SearchAsync(
new SearchRendezVousRequest());
RendezVousSummaryDto dto = Assert.Single(result.Items);
Assert.Equal("Réunion", dto.Titre);
Assert.Equal("Réunion", dto.TypeActivite);
Assert.Equal("Confirmé", dto.Statut);
Assert.Equal(new DateTime(2026, 1, 15, 9, 0, 0), dto.Debut);
}
[Fact]
public async Task SearchAsync_MultipleRendezVous_MapsAllToDtos()
{
var rendezVousList = new[]
{
new RendezVous { Titre = "Alpha" },
new RendezVous { Titre = "Beta" },
new RendezVous { Titre = "Gamma" }
};
var repo = new FakeRendezVousRepository
{
SearchResult = new PagedResult<RendezVous>(rendezVousList, 1, 20, 3, 1)
};
var service = new RendezVousService(repo);
PagedResult<RendezVousSummaryDto> result = await service.SearchAsync(
new SearchRendezVousRequest());
Assert.Equal(3, result.Items.Count);
Assert.Equal(3, result.TotalCount);
Assert.Equal("Alpha", result.Items[0].Titre);
Assert.Equal("Beta", result.Items[1].Titre);
Assert.Equal("Gamma", result.Items[2].Titre);
}
[Fact]
public async Task SearchAsync_PreservesTotalCount()
{
var repo = new FakeRendezVousRepository
{
SearchResult = new PagedResult<RendezVous>([], 2, 10, 42, 5)
};
var service = new RendezVousService(repo);
PagedResult<RendezVousSummaryDto> result = await service.SearchAsync(
new SearchRendezVousRequest());
Assert.Equal(42, result.TotalCount);
Assert.Equal(2, result.Page);
Assert.Equal(10, result.PageSize);
Assert.Equal(5, result.TotalPages);
}
[Fact]
public async Task SearchAsync_IdComesFromRendezVousContractMapper()
{
var rendezVous = new RendezVous { Titre = "Test" };
var expectedId = rendezVous.Id.ToString();
var repo = new FakeRendezVousRepository
{
SearchResult = new PagedResult<RendezVous>([rendezVous], 1, 20, 1, 1)
};
var service = new RendezVousService(repo);
PagedResult<RendezVousSummaryDto> result = await service.SearchAsync(
new SearchRendezVousRequest());
Assert.Equal(expectedId, result.Items[0].Id);
}
[Fact]
public async Task SearchAsync_ServiceDoesNotRePaginate()
{
var repo = new FakeRendezVousRepository
{
SearchResult = new PagedResult<RendezVous>(
[new RendezVous { Titre = "A" }, new RendezVous { Titre = "B" }], 3, 15, 100, 7)
};
var service = new RendezVousService(repo);
PagedResult<RendezVousSummaryDto> result = await service.SearchAsync(
new SearchRendezVousRequest { Page = 1, PageSize = 5 });
Assert.Equal(2, result.Items.Count);
Assert.Equal(100, result.TotalCount);
Assert.Equal(7, result.TotalPages);
}
// =========================================================================
// DETAIL
// =========================================================================
[Fact]
public async Task GetByIdAsync_TransmitsExactId()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.GetByIdAsync("test-id-123");
Assert.Equal("test-id-123", repo.LastDetailsId);
}
[Fact]
public async Task GetByIdAsync_TransmitsCancellationToken()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
using var cts = new CancellationTokenSource();
await service.GetByIdAsync("42", cts.Token);
Assert.Equal(cts.Token, repo.LastCancellationToken);
}
[Fact]
public async Task GetByIdAsync_RepositoryReturnsNull_ReturnsNull()
{
var repo = new FakeRendezVousRepository { DetailsResult = null };
var service = new RendezVousService(repo);
RendezVousDetailsDto? result = await service.GetByIdAsync("999");
Assert.Null(result);
}
[Fact]
public async Task GetByIdAsync_MapsToDetailsDto()
{
var rendezVous = new RendezVous
{
Titre = "Réunion",
Description = "Description",
TypeActivite = new ReferenceValue("Réunion") { Key = "REUNION" },
Statut = new ReferenceValue("Confirmé") { Key = "CONFIRME" }
};
var repo = new FakeRendezVousRepository { DetailsResult = rendezVous };
var service = new RendezVousService(repo);
RendezVousDetailsDto? result = await service.GetByIdAsync("42");
Assert.NotNull(result);
Assert.Equal("Réunion", result.Titre);
Assert.Equal("Description", result.Description);
Assert.Equal("Réunion", result.TypeActivite?.Label);
Assert.Equal("Confirmé", result.Statut?.Label);
}
[Fact]
public async Task GetByIdAsync_CollaborateursMapped()
{
var rendezVous = new RendezVous { Titre = "Test" };
rendezVous.AddCollaborateur(new AffectationCollaborateur { NomAffiche = "Jean Dupont" });
var repo = new FakeRendezVousRepository { DetailsResult = rendezVous };
var service = new RendezVousService(repo);
RendezVousDetailsDto? result = await service.GetByIdAsync("42");
Assert.NotNull(result);
Assert.Single(result.Collaborateurs);
Assert.Equal("Jean Dupont", result.Collaborateurs[0].NomAffiche);
}
[Fact]
public async Task GetByIdAsync_ContactsMapped()
{
var rendezVous = new RendezVous { Titre = "Test" };
rendezVous.AddContact(new ParticipantRendezVous { NomAffiche = "Marie Curie" });
var repo = new FakeRendezVousRepository { DetailsResult = rendezVous };
var service = new RendezVousService(repo);
RendezVousDetailsDto? result = await service.GetByIdAsync("42");
Assert.NotNull(result);
Assert.Single(result.Contacts);
Assert.Equal("Marie Curie", result.Contacts[0].NomAffiche);
}
[Fact]
public async Task GetByIdAsync_IdComesFromMapper()
{
var rendezVous = new RendezVous { Titre = "Test" };
var expectedId = rendezVous.Id.ToString();
var repo = new FakeRendezVousRepository { DetailsResult = rendezVous };
var service = new RendezVousService(repo);
RendezVousDetailsDto? result = await service.GetByIdAsync("42");
Assert.NotNull(result);
Assert.Equal(expectedId, result.Id);
}
[Fact]
public async Task GetByIdAsync_NoGuidParsingInService()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
await service.GetByIdAsync("not-a-guid-nor-int");
Assert.Equal("not-a-guid-nor-int", repo.LastDetailsId);
}
[Fact]
public async Task GetByIdAsync_RepositoryException_Propagated()
{
var repo = new FakeRendezVousRepository
{
ThrowOnDetails = new InvalidOperationException("Infrastructure error")
};
var service = new RendezVousService(repo);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.GetByIdAsync("42"));
Assert.Equal("Infrastructure error", ex.Message);
}
// =========================================================================
// CREATE
// =========================================================================
[Fact]
public async Task CreateAsync_CallsRepositoryOnce()
{
var repo = new FakeRendezVousRepository
{
CreateResult = new RendezVous { Titre = "Nouveau" }
};
var service = new RendezVousService(repo);
await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" });
Assert.Equal(1, repo.CreateCallCount);
}
[Fact]
public async Task CreateAsync_MapsAllProperties()
{
var repo = new FakeRendezVousRepository
{
CreateResult = new RendezVous { Titre = "Nouveau" }
};
var service = new RendezVousService(repo);
await service.CreateAsync(new CreateRendezVousRequest
{
Titre = "Nouveau",
Description = "Description",
Debut = new DateTime(2026, 1, 15, 9, 0, 0),
Fin = new DateTime(2026, 1, 15, 10, 0, 0),
TypeActivite = new ReferenceValueDto { Key = "REUNION" },
DossierId = "42",
InterventionId = "43",
SiteId = "44"
});
Assert.Equal("Nouveau", repo.LastCreateCommand!.Titre);
Assert.Equal("Description", repo.LastCreateCommand!.Description);
Assert.Equal(new DateTime(2026, 1, 15, 9, 0, 0), repo.LastCreateCommand!.Debut);
Assert.Equal(new DateTime(2026, 1, 15, 10, 0, 0), repo.LastCreateCommand!.Fin);
Assert.Equal("REUNION", repo.LastCreateCommand!.TypeActiviteKey);
Assert.Equal("42", repo.LastCreateCommand!.DossierId);
Assert.Equal("43", repo.LastCreateCommand!.InterventionId);
Assert.Equal("44", repo.LastCreateCommand!.SiteId);
}
[Fact]
public async Task CreateAsync_ResultMappedToDetails()
{
var repo = new FakeRendezVousRepository
{
CreateResult = new RendezVous { Titre = "Nouveau" }
};
var service = new RendezVousService(repo);
RendezVousDetailsDto result = await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" });
Assert.Equal("Nouveau", result.Titre);
}
[Fact]
public async Task CreateAsync_CancellationTokenTransmitted()
{
var repo = new FakeRendezVousRepository
{
CreateResult = new RendezVous { Titre = "Nouveau" }
};
var service = new RendezVousService(repo);
using var cts = new CancellationTokenSource();
await service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" }, cts.Token);
Assert.Equal(cts.Token, repo.LastCreateCancellationToken);
}
[Fact]
public async Task CreateAsync_ExceptionPropagated()
{
var repo = new FakeRendezVousRepository
{
ThrowOnCreate = new InvalidOperationException("Panne")
};
var service = new RendezVousService(repo);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.CreateAsync(new CreateRendezVousRequest { Titre = "Nouveau" }));
Assert.Equal("Panne", ex.Message);
}
// =========================================================================
// UPDATE
// =========================================================================
[Fact]
public async Task UpdateAsync_MapsAllProperties()
{
var repo = new FakeRendezVousRepository
{
UpdateResult = new RendezVous { Titre = "Updated" }
};
var service = new RendezVousService(repo);
await service.UpdateAsync("42", new UpdateRendezVousRequest
{
Titre = "Updated",
TypeActivite = new ReferenceValueDto { Key = "REUNION" },
Statut = new ReferenceValueDto { Key = "CONFIRME" }
});
Assert.Equal(1, repo.UpdateCallCount);
Assert.Equal("42", repo.LastUpdateId);
Assert.Equal("Updated", repo.LastUpdateCommand!.Titre);
Assert.Equal("REUNION", repo.LastUpdateCommand!.TypeActiviteKey);
Assert.Equal("CONFIRME", repo.LastUpdateCommand!.StatutKey);
}
[Fact]
public async Task UpdateAsync_NullsPreserved()
{
var repo = new FakeRendezVousRepository { UpdateResult = new RendezVous { Titre = "X" } };
var service = new RendezVousService(repo);
await service.UpdateAsync("42", new UpdateRendezVousRequest());
Assert.Null(repo.LastUpdateCommand!.TypeActiviteKey);
Assert.Null(repo.LastUpdateCommand!.StatutKey);
}
[Fact]
public async Task UpdateAsync_RepoReturnsNull_ThrowsKeyNotFound()
{
var repo = new FakeRendezVousRepository { UpdateResult = null };
var service = new RendezVousService(repo);
await Assert.ThrowsAsync<KeyNotFoundException>(
() => service.UpdateAsync("42", new UpdateRendezVousRequest()));
}
[Fact]
public async Task UpdateAsync_IdTransmittedExactly()
{
var repo = new FakeRendezVousRepository
{
UpdateResult = new RendezVous { Titre = "XY" }
};
var service = new RendezVousService(repo);
await service.UpdateAsync("my-guid-123", new UpdateRendezVousRequest());
Assert.Equal("my-guid-123", repo.LastUpdateId);
}
[Fact]
public async Task UpdateAsync_CancellationTokenTransmitted()
{
var repo = new FakeRendezVousRepository { UpdateResult = new RendezVous { Titre = "X" } };
var service = new RendezVousService(repo);
using var cts = new CancellationTokenSource();
await service.UpdateAsync("42", new UpdateRendezVousRequest(), cts.Token);
Assert.Equal(cts.Token, repo.LastUpdateCancellationToken);
}
[Fact]
public async Task UpdateAsync_ExceptionPropagated()
{
var repo = new FakeRendezVousRepository
{
ThrowOnUpdate = new InvalidOperationException("Panne")
};
var service = new RendezVousService(repo);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.UpdateAsync("42", new UpdateRendezVousRequest()));
Assert.Equal("Panne", ex.Message);
}
// =========================================================================
// CANCEL
// =========================================================================
[Fact]
public async Task CancelAsync_TruePropagated()
{
var repo = new FakeRendezVousRepository { CancelResult = true };
var service = new RendezVousService(repo);
await service.CancelAsync("42", new AnnulationRendezVousDto());
Assert.Equal(1, repo.CancelCallCount);
}
[Fact]
public async Task CancelAsync_False_ThrowsKeyNotFound()
{
var repo = new FakeRendezVousRepository { CancelResult = false };
var service = new RendezVousService(repo);
await Assert.ThrowsAsync<KeyNotFoundException>(
() => service.CancelAsync("42", new AnnulationRendezVousDto()));
}
[Fact]
public async Task CancelAsync_IdTransmittedExactly()
{
var repo = new FakeRendezVousRepository { CancelResult = true };
var service = new RendezVousService(repo);
await service.CancelAsync("my-guid-123", new AnnulationRendezVousDto());
Assert.Equal("my-guid-123", repo.LastCancelId);
}
[Fact]
public async Task CancelAsync_MapsAnnulation()
{
var repo = new FakeRendezVousRepository { CancelResult = true };
var service = new RendezVousService(repo);
await service.CancelAsync("42", new AnnulationRendezVousDto
{
Origine = new ReferenceValueDto { Key = "CLIENT" },
Motif = new ReferenceValueDto { Key = "DISPONIBILITE" },
Commentaire = "Reporté",
CollaborateurId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
});
Assert.Equal("CLIENT", repo.LastCancelCommand!.OrigineKey);
Assert.Equal("DISPONIBILITE", repo.LastCancelCommand!.MotifKey);
Assert.Equal("Reporté", repo.LastCancelCommand!.Commentaire);
Assert.Equal("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", repo.LastCancelCommand!.CollaborateurId);
}
[Fact]
public async Task CancelAsync_TransmetLAuteurAuRepository()
{
var repo = new FakeRendezVousRepository { CancelResult = true };
var service = new RendezVousService(repo);
var auteur = new AuditActor("7", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
// L'annulation modifie le rendez-vous : l'auteur doit atteindre le
// repository pour estampiller ModifDate/ModifUser du noyau.
await service.CancelAsync("42", new AnnulationRendezVousDto(), auteur: auteur);
Assert.Same(auteur, repo.LastCancelAuteur);
}
[Fact]
public async Task CancelAsync_SansAuteur_TransmetUnAuteurVide()
{
var repo = new FakeRendezVousRepository { CancelResult = true };
var service = new RendezVousService(repo);
await service.CancelAsync("42", new AnnulationRendezVousDto());
Assert.True(repo.LastCancelAuteur is { UserId: null, CollaborateurId: null });
}
// =========================================================================
// ARCHIVE
// =========================================================================
[Fact]
public async Task ArchiveAsync_TruePropagated()
{
var repo = new FakeRendezVousRepository { ArchiveResult = true };
var service = new RendezVousService(repo);
await service.ArchiveAsync("42");
Assert.Equal(1, repo.ArchiveCallCount);
}
[Fact]
public async Task ArchiveAsync_False_ThrowsKeyNotFound()
{
var repo = new FakeRendezVousRepository { ArchiveResult = false };
var service = new RendezVousService(repo);
await Assert.ThrowsAsync<KeyNotFoundException>(
() => service.ArchiveAsync("42"));
}
[Fact]
public async Task ArchiveAsync_IdTransmittedExactly()
{
var repo = new FakeRendezVousRepository { ArchiveResult = true };
var service = new RendezVousService(repo);
await service.ArchiveAsync("my-guid-123");
Assert.Equal("my-guid-123", repo.LastArchiveId);
}
[Fact]
public async Task ArchiveAsync_CancellationTokenTransmitted()
{
var repo = new FakeRendezVousRepository { ArchiveResult = true };
var service = new RendezVousService(repo);
using var cts = new CancellationTokenSource();
await service.ArchiveAsync("42", cts.Token);
Assert.Equal(cts.Token, repo.LastArchiveCancellationToken);
}
[Fact]
public async Task ArchiveAsync_ExceptionPropagated()
{
var repo = new FakeRendezVousRepository
{
ThrowOnArchive = new InvalidOperationException("Panne")
};
var service = new RendezVousService(repo);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.ArchiveAsync("42"));
Assert.Equal("Panne", ex.Message);
}
[Fact]
public async Task ArchiveAsync_TransmetLAuteurAuRepository()
{
var repo = new FakeRendezVousRepository { ArchiveResult = true };
var service = new RendezVousService(repo);
var auteur = new AuditActor("7", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
await service.ArchiveAsync("42", auteur: auteur);
Assert.Same(auteur, repo.LastArchiveAuteur);
}
[Fact]
public async Task SetTempsValideAsync_TransmetLAuteurAuRepository()
{
var repo = new FakeRendezVousRepository();
var service = new RendezVousService(repo);
var auteur = new AuditActor("7", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
// Le passage au statut « Fait » modifie le noyau du rendez-vous : l'auteur
// doit atteindre le repository.
await service.SetTempsValideAsync("42", "collab-1", tempsValide: true, auteur: auteur);
Assert.Same(auteur, repo.LastSetTempsValideAuteur);
}
// =========================================================================
// ARCHITECTURE
// =========================================================================
[Fact]
public void Service_ImplementsIRendezVousService()
{
Assert.True(typeof(IRendezVousService).IsAssignableFrom(typeof(RendezVousService)));
}
[Fact]
public void Service_DependsOnIRendezVousRepository()
{
var type = typeof(RendezVousService);
bool hasRepoParam = type.GetConstructors()
.Any(c => c.GetParameters()
.Any(p => p.ParameterType == typeof(IRendezVousRepository)));
Assert.True(hasRepoParam, "RendezVousService doit dépendre de IRendezVousRepository.");
}
[Fact]
public void Service_HasNoOmikaDataReferences()
{
var type = typeof(RendezVousService);
var refs = GetReferencedTypes(type);
Assert.DoesNotContain(refs, t =>
t.Namespace is not null && t.Namespace.StartsWith("Omika.Data"));
}
[Fact]
public void Service_HasNoBatikReferences()
{
var type = typeof(RendezVousService);
var refs = GetReferencedTypes(type);
Assert.DoesNotContain(refs, t =>
t.Name.Contains("Batik", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Service_HasNoHfsqlReferences()
{
var type = typeof(RendezVousService);
var refs = GetReferencedTypes(type);
Assert.DoesNotContain(refs, t =>
t.Name.Contains("Row", StringComparison.OrdinalIgnoreCase)
&& t.Namespace is not null
&& (t.Namespace.Contains("HfSql", StringComparison.OrdinalIgnoreCase)
|| t.Namespace.Contains("Models.Omika", StringComparison.OrdinalIgnoreCase)));
}
[Fact]
public void Service_HasNoSqlStatements()
{
string source = ReadSourceFile(typeof(RendezVousService));
Assert.DoesNotContain("SELECT ", source, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("FROM ", source, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Service_HasNoApplicationRuntimeContext()
{
var type = typeof(RendezVousService);
var refs = GetReferencedTypes(type);
Assert.DoesNotContain(refs, t =>
t.Name.Contains("ApplicationRuntimeContext", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Service_HasNoDatabaseSchemaVersion()
{
var type = typeof(RendezVousService);
var refs = GetReferencedTypes(type);
Assert.DoesNotContain(refs, t =>
t.Name.Contains("DatabaseSchemaVersion", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Service_HasNoOdbcReferences()
{
var type = typeof(RendezVousService);
var refs = GetReferencedTypes(type);
Assert.DoesNotContain(refs, t =>
t.Namespace is not null && t.Namespace.StartsWith("System.Data.Odbc", StringComparison.Ordinal));
}
// =========================================================================
// HELPERS
// =========================================================================
private static IEnumerable<Type> GetReferencedTypes(Type type)
{
var members = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
.Select(m => m.DeclaringType)
.Where(t => t is not null)
.Cast<Type>();
var parameters = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.SelectMany(m => m.GetParameters())
.Select(p => p.ParameterType);
var returnTypes = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(m => m.ReturnType);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => p.PropertyType);
var ctorParams = type.GetConstructors()
.SelectMany(c => c.GetParameters())
.Select(p => p.ParameterType);
return members.Concat(parameters).Concat(returnTypes).Concat(properties).Concat(ctorParams);
}
private static string ReadSourceFile(Type type)
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
var dir = new DirectoryInfo(baseDir);
while (dir != null && !File.Exists(Path.Combine(dir.FullName, "Omika.slnx")))
dir = dir.Parent;
string root = dir?.FullName ?? baseDir;
return File.ReadAllText(Path.Combine(root, "src", "Omika.Core", "Services", type.Name + ".cs"));
}
// =========================================================================
// FAKE REPOSITORY
// =========================================================================
private sealed class FakeRendezVousRepository : IRendezVousRepository
{
public int SearchCallCount { get; private set; }
public int DetailsCallCount { get; private set; }
public int UpdateCallCount { get; private set; }
public int ArchiveCallCount { get; private set; }
public int CreateCallCount { get; private set; }
public int CancelCallCount { get; private set; }
public RendezVousSearchCriteria? LastSearchCriteria { get; private set; }
public string? LastDetailsId { get; private set; }
public string? LastUpdateId { get; private set; }
public string? LastArchiveId { get; private set; }
public string? LastCancelId { get; private set; }
public RendezVousUpdateCommand? LastUpdateCommand { get; private set; }
public RendezVousCreateCommand? LastCreateCommand { get; private set; }
public AnnulationRendezVousCommand? LastCancelCommand { get; private set; }
public AuditActor? LastCancelAuteur { get; private set; }
public AuditActor? LastArchiveAuteur { get; private set; }
public AuditActor? LastSetTempsValideAuteur { get; private set; }
public CancellationToken LastUpdateCancellationToken { get; private set; }
public CancellationToken LastArchiveCancellationToken { get; private set; }
public CancellationToken LastCreateCancellationToken { get; private set; }
public CancellationToken LastCancelCancellationToken { get; private set; }
public CancellationToken LastCancellationToken { get; private set; }
public PagedResult<RendezVous>? SearchResult { get; set; }
public RendezVous? DetailsResult { get; set; }
public RendezVous? UpdateResult { get; set; }
public RendezVous? CreateResult { get; set; }
public bool ArchiveResult { get; set; }
public bool CancelResult { get; set; }
public Exception? ThrowOnSearch { get; set; }
public Exception? ThrowOnDetails { get; set; }
public Exception? ThrowOnUpdate { get; set; }
public Exception? ThrowOnArchive { get; set; }
public Exception? ThrowOnCreate { get; set; }
public Exception? ThrowOnCancel { get; set; }
public Task<RendezVous> CreateAsync(
RendezVousCreateCommand command,
CancellationToken cancellationToken = default)
{
CreateCallCount++;
LastCreateCommand = command;
LastCreateCancellationToken = cancellationToken;
if (ThrowOnCreate is not null)
throw ThrowOnCreate;
return Task.FromResult(CreateResult ?? new RendezVous { Titre = command.Titre });
}
public Task<PagedResult<RendezVous>> SearchAsync(
RendezVousSearchCriteria criteria,
CancellationToken cancellationToken = default)
{
SearchCallCount++;
LastSearchCriteria = criteria;
LastCancellationToken = cancellationToken;
if (ThrowOnSearch is not null)
throw ThrowOnSearch;
return Task.FromResult(SearchResult
?? new PagedResult<RendezVous>([], 1, 20, 0, 0));
}
public Task<PagedResult<RendezVous>> SearchDetailsAsync(
RendezVousSearchCriteria criteria,
CancellationToken cancellationToken = default)
{
DetailsCallCount++;
LastSearchCriteria = criteria;
LastCancellationToken = cancellationToken;
if (ThrowOnSearch is not null)
throw ThrowOnSearch;
return Task.FromResult(SearchResult
?? new PagedResult<RendezVous>([], 1, 20, 0, 0));
}
public Task<RendezVous?> GetDetailsAsync(
string id,
CancellationToken cancellationToken = default)
{
DetailsCallCount++;
LastDetailsId = id;
LastCancellationToken = cancellationToken;
if (ThrowOnDetails is not null)
throw ThrowOnDetails;
return Task.FromResult(DetailsResult);
}
public Task<RendezVous?> UpdateAsync(
string id, RendezVousUpdateCommand command,
CancellationToken cancellationToken = default)
{
UpdateCallCount++;
LastUpdateId = id;
LastUpdateCommand = command;
LastUpdateCancellationToken = cancellationToken;
if (ThrowOnUpdate is not null)
throw ThrowOnUpdate;
return Task.FromResult(UpdateResult);
}
public Task<bool> CancelAsync(
string id, AnnulationRendezVousCommand annulation,
CancellationToken cancellationToken = default,
AuditActor? auteur = null)
{
CancelCallCount++;
LastCancelId = id;
LastCancelCommand = annulation;
LastCancelCancellationToken = cancellationToken;
LastCancelAuteur = auteur;
if (ThrowOnCancel is not null)
throw ThrowOnCancel;
return Task.FromResult(CancelResult);
}
public Task<bool> ArchiveAsync(
string id,
CancellationToken cancellationToken = default,
AuditActor? auteur = null)
{
ArchiveCallCount++;
LastArchiveId = id;
LastArchiveCancellationToken = cancellationToken;
LastArchiveAuteur = auteur;
if (ThrowOnArchive is not null)
throw ThrowOnArchive;
return Task.FromResult(ArchiveResult);
}
public Task<bool> SetTempsValideAsync(
string rendezVousId, string collaborateurId, bool tempsValide,
CancellationToken cancellationToken = default,
AuditActor? auteur = null)
{
LastSetTempsValideAuteur = auteur;
return Task.FromResult(true);
}
}
}