Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,762 @@
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Omika.Core.Compatibility;
|
||||
using Omika.Core.Contracts.Common;
|
||||
using Omika.Core.Contracts.Planning;
|
||||
using Omika.Core.Domain.Commands;
|
||||
using Omika.Core.Domain.Entities.Planning;
|
||||
using Omika.Core.Interfaces;
|
||||
using Omika.Core.Runtime;
|
||||
using Omika.Core.Services;
|
||||
using Omika.Data;
|
||||
using Omika.Data.Configuration;
|
||||
using Omika.Data.Connection;
|
||||
using Omika.Data.Repositories;
|
||||
using Omika.Data.Repositories.Batik;
|
||||
using Omika.Data.Repositories.Omika;
|
||||
|
||||
namespace Omika.Tests.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Tests unitaires de <see cref="AdaptiveSuiviTempsRepository"/> : délégation,
|
||||
/// comportement Unknown, mapping Batik/Omika, délégation du service, et DI.
|
||||
/// </summary>
|
||||
[Collection("ApplicationRuntimeContext")]
|
||||
public class AdaptiveSuiviTempsRepositoryTests : IDisposable
|
||||
{
|
||||
public AdaptiveSuiviTempsRepositoryTests()
|
||||
{
|
||||
ApplicationRuntimeContext.Reset();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ApplicationRuntimeContext.Reset();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DELEGATION SEARCH
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAsync_BatikDetected_DelegatesToBatikRepository()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Batik);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
batikConnection.SuiviTempsRows = [BatikSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
await adaptive.SearchAsync(new SuiviTempsSearchCriteria());
|
||||
|
||||
Assert.True(batikConnection.QueryCount > 0, "Batik aurait dû exécuter des requêtes.");
|
||||
Assert.Equal(0, omikaConnection.QueryCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAsync_OmikaDetected_DelegatesToOmikaRepository()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Omika);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
omikaConnection.SuiviTempsRows = [OmikaSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
await adaptive.SearchAsync(new SuiviTempsSearchCriteria());
|
||||
|
||||
Assert.Equal(0, batikConnection.QueryCount);
|
||||
Assert.True(omikaConnection.QueryCount > 0, "Omika aurait dû exécuter des requêtes.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAsync_UnknownDetected_ThrowsInvalidOperationException()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => adaptive.SearchAsync(new SuiviTempsSearchCriteria()));
|
||||
|
||||
Assert.Contains("not been detected", ex.Message);
|
||||
Assert.Equal(0, batikConnection.QueryCount);
|
||||
Assert.Equal(0, omikaConnection.QueryCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAsync_EmptyCriteria_DoesNotCrash()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Batik);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
batikConnection.SuiviTempsRows = [];
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
PagedResult<SuiviTemps> result = await adaptive.SearchAsync(new SuiviTempsSearchCriteria());
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result.Items);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// MAPPING BATIK
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAsync_BatikMapping_ProducesCanonicalSuiviTemps()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Batik);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
batikConnection.SuiviTempsRows = [BatikSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
PagedResult<SuiviTemps> result = await adaptive.SearchAsync(new SuiviTempsSearchCriteria());
|
||||
|
||||
Assert.Single(result.Items);
|
||||
Assert.Equal("Réunion", result.Items[0].Description);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// MAPPING OMIKA
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAsync_OmikaMapping_ProducesCanonicalSuiviTemps()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Omika);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
omikaConnection.SuiviTempsRows = [OmikaSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
PagedResult<SuiviTemps> result = await adaptive.SearchAsync(new SuiviTempsSearchCriteria());
|
||||
|
||||
Assert.Single(result.Items);
|
||||
Assert.Equal("Réunion", result.Items[0].Description);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DELEGATION GET BY ID
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task GetByIdAsync_BatikDetected_DelegatesToBatik()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Batik);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
batikConnection.SuiviTempsRows = [BatikSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
SuiviTemps? result = await adaptive.GetByIdAsync(Guid.NewGuid().ToString("D"));
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.True(batikConnection.QueryCount > 0, "Batik aurait dû exécuter des requêtes.");
|
||||
Assert.Equal(0, omikaConnection.QueryCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByIdAsync_OmikaDetected_DelegatesToOmika()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Omika);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
omikaConnection.SuiviTempsRows = [OmikaSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
SuiviTemps? result = await adaptive.GetByIdAsync(Guid.NewGuid().ToString("D"));
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(0, batikConnection.QueryCount);
|
||||
Assert.True(omikaConnection.QueryCount > 0, "Omika aurait dû exécuter des requêtes.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByIdAsync_UnknownDetected_ThrowsInvalidOperationException()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => adaptive.GetByIdAsync(Guid.NewGuid().ToString("D")));
|
||||
|
||||
Assert.Contains("not been detected", ex.Message);
|
||||
Assert.Equal(0, batikConnection.QueryCount);
|
||||
Assert.Equal(0, omikaConnection.QueryCount);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DELEGATION CREATE
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_BatikDetected_DelegatesToBatik()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Batik);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
batikConnection.SuiviTempsRows = [BatikSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
SuiviTemps result = await adaptive.CreateAsync(
|
||||
new SuiviTempsCreateCommand { Date = DateTime.Now, DureeMinutes = 60 },
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.True(batikConnection.QueryCount > 0, "Batik aurait dû exécuter des requêtes.");
|
||||
Assert.Equal(0, omikaConnection.QueryCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_OmikaDetected_DelegatesToOmika()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
ApplicationRuntimeContext.SetDetectedVersion(DatabaseSchemaVersion.Omika);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
omikaConnection.SuiviTempsRows = [OmikaSuiviTempsDict(1, "Réunion", "INT-001")];
|
||||
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
SuiviTemps result = await adaptive.CreateAsync(
|
||||
new SuiviTempsCreateCommand { Date = DateTime.Now, DureeMinutes = 60 },
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(0, batikConnection.QueryCount);
|
||||
Assert.True(omikaConnection.QueryCount > 0, "Omika aurait dû exécuter des requêtes.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_UnknownDetected_ThrowsInvalidOperationException()
|
||||
{
|
||||
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
||||
|
||||
var batikConnection = new SpyingHfsqlConnection();
|
||||
var omikaConnection = new SpyingHfsqlConnection();
|
||||
var batikRepo = new BatikSuiviTempsRepository(batikConnection);
|
||||
var omikaRepo = new OmikaSuiviTempsRepository(omikaConnection);
|
||||
var adaptive = new AdaptiveSuiviTempsRepository(batikRepo, omikaRepo);
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => adaptive.CreateAsync(
|
||||
new SuiviTempsCreateCommand { Date = DateTime.Now, DureeMinutes = 60 }));
|
||||
|
||||
Assert.Contains("not been detected", ex.Message);
|
||||
Assert.Equal(0, batikConnection.QueryCount);
|
||||
Assert.Equal(0, omikaConnection.QueryCount);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// SERVICE DELEGATION
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public async Task Service_SearchAsync_DelegatesToRepository()
|
||||
{
|
||||
var repo = new FakeSuiviTempsRepository();
|
||||
var service = new SuiviTempsService(repo);
|
||||
|
||||
PagedResult<SuiviTempsDto> result = await service.SearchAsync(new SuiviTempsSearchCriteria());
|
||||
|
||||
Assert.True(repo.SearchCalled);
|
||||
Assert.Empty(result.Items);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Service_GetByIdAsync_DelegatesToRepository()
|
||||
{
|
||||
var repo = new FakeSuiviTempsRepository();
|
||||
var service = new SuiviTempsService(repo);
|
||||
|
||||
SuiviTempsDto? result = await service.GetByIdAsync("abc");
|
||||
|
||||
Assert.True(repo.GetByIdCalled);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Service_CreateAsync_DelegatesToRepository()
|
||||
{
|
||||
var repo = new FakeSuiviTempsRepository();
|
||||
var service = new SuiviTempsService(repo);
|
||||
|
||||
SuiviTempsDto result = await service.CreateAsync(
|
||||
new CreateSuiviTempsRequest { Date = DateTime.Now, DureeMinutes = 60 });
|
||||
|
||||
Assert.True(repo.CreateCalled);
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DI TESTS
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_RegistersBatikSuiviTempsRepositoryAsScoped()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptor = services.Single(d => d.ServiceType == typeof(BatikSuiviTempsRepository));
|
||||
Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_RegistersOmikaSuiviTempsRepositoryAsScoped()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptor = services.Single(d => d.ServiceType == typeof(OmikaSuiviTempsRepository));
|
||||
Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_RegistersISuiviTempsRepositoryAsScoped()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptor = services.Single(d => d.ServiceType == typeof(ISuiviTempsRepository));
|
||||
Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_ISuiviTempsRepositoryPointsToAdaptiveSuiviTempsRepository()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptor = services.Single(d => d.ServiceType == typeof(ISuiviTempsRepository));
|
||||
Assert.Equal(typeof(AdaptiveSuiviTempsRepository), descriptor.ImplementationType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_RegistersISuiviTempsServiceAsScoped()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptor = services.Single(d => d.ServiceType == typeof(ISuiviTempsService));
|
||||
Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_ISuiviTempsServicePointsToSuiviTempsService()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptor = services.Single(d => d.ServiceType == typeof(ISuiviTempsService));
|
||||
Assert.Equal(typeof(SuiviTempsService), descriptor.ImplementationType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddOmikaData_OnlyOneISuiviTempsRepositoryRegistered()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddOmikaData();
|
||||
|
||||
var descriptors = services.Where(d => d.ServiceType == typeof(ISuiviTempsRepository)).ToList();
|
||||
Assert.Single(descriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOmikaData_ResolvesAllSuiviTempsRepositories_WithFakeConnection()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddOmikaData();
|
||||
|
||||
var oldDescriptor = services.Single(d => d.ServiceType == typeof(IHfsqlConnection));
|
||||
services.Remove(oldDescriptor);
|
||||
services.AddScoped<IHfsqlConnection, MinimalFakeHfsqlConnection>();
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
await using var scope = provider.CreateAsyncScope();
|
||||
|
||||
var suiviTempsRepo = scope.ServiceProvider.GetRequiredService<ISuiviTempsRepository>();
|
||||
Assert.IsType<AdaptiveSuiviTempsRepository>(suiviTempsRepo);
|
||||
|
||||
var batikRepo = scope.ServiceProvider.GetRequiredService<BatikSuiviTempsRepository>();
|
||||
Assert.NotNull(batikRepo);
|
||||
|
||||
var omikaRepo = scope.ServiceProvider.GetRequiredService<OmikaSuiviTempsRepository>();
|
||||
Assert.NotNull(omikaRepo);
|
||||
|
||||
var service = scope.ServiceProvider.GetRequiredService<ISuiviTempsService>();
|
||||
Assert.IsType<SuiviTempsService>(service);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// ARCHITECTURE
|
||||
// =========================================================================
|
||||
|
||||
[Fact]
|
||||
public void Repository_ImplementsISuiviTempsRepository()
|
||||
{
|
||||
Assert.True(typeof(ISuiviTempsRepository).IsAssignableFrom(typeof(AdaptiveSuiviTempsRepository)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Repository_ContainsNoSql()
|
||||
{
|
||||
string source = ReadSourceFile(typeof(AdaptiveSuiviTempsRepository));
|
||||
|
||||
Assert.DoesNotContain("SELECT ", source, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("FROM ", source, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("WHERE ", source, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Repository_DoesNotDependOnHfsqlConnection()
|
||||
{
|
||||
var type = typeof(AdaptiveSuiviTempsRepository);
|
||||
|
||||
bool hasHfsqlConnectionCtor = type.GetConstructors()
|
||||
.Any(c => c.GetParameters()
|
||||
.Any(p => typeof(IHfsqlConnection).IsAssignableFrom(p.ParameterType)));
|
||||
|
||||
Assert.False(hasHfsqlConnectionCtor,
|
||||
"AdaptiveSuiviTempsRepository ne doit pas dépendre directement de IHfsqlConnection.");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// HELPERS
|
||||
// =========================================================================
|
||||
|
||||
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.Data", "Repositories", type.Name + ".cs"));
|
||||
}
|
||||
|
||||
// --- Dictionnaires de lignes physiques ----------------------------------
|
||||
|
||||
private static Dictionary<string, object?> BatikSuiviTempsDict(
|
||||
long pkid, string? libelle, string? reference)
|
||||
=> new()
|
||||
{
|
||||
["SDT_PKID"] = pkid,
|
||||
["SDT_DateDebut"] = new DateTime(2026, 1, 15, 9, 0, 0),
|
||||
["SDT_IDIntervention"] = 0L,
|
||||
["SDT_IDCollaborateur"] = 0L,
|
||||
["SDT_IDPartenaire"] = 0L,
|
||||
["SDT_NbMinutes"] = 60,
|
||||
["SDT_Libelle"] = libelle,
|
||||
["SDT_IDTypeTemps"] = 0L,
|
||||
["SDT_IDRdv"] = 0L,
|
||||
["SDT_Ponderation"] = 0L,
|
||||
["SDT_IDImport"] = 0L,
|
||||
["SDT_DateHeureFin"] = new DateTime(2026, 1, 15, 10, 0, 0),
|
||||
["GUID"] = Guid.NewGuid().ToString("D"),
|
||||
["SDT_IDTypeRdv"] = 0L,
|
||||
["SDT_Controle"] = false,
|
||||
["SDT_IDGroupe"] = 0L,
|
||||
["CreationDate"] = new DateTime(2026, 1, 15, 9, 0, 0),
|
||||
["CreationUser"] = 0L,
|
||||
["ModifDate"] = new DateTime(2026, 1, 15, 9, 0, 0),
|
||||
["ModifUser"] = 0L
|
||||
};
|
||||
|
||||
private static Dictionary<string, object?> OmikaSuiviTempsDict(
|
||||
long pkid, string? libelle, string? reference)
|
||||
=> new()
|
||||
{
|
||||
["SDT_PKID"] = pkid,
|
||||
["SDT_DateDebut"] = new DateTime(2026, 1, 15, 9, 0, 0),
|
||||
["SDT_IDIntervention"] = 0L,
|
||||
["SDT_IDCollaborateur"] = 0L,
|
||||
["SDT_IDPartenaire"] = 0L,
|
||||
["SDT_NbMinutes"] = 60,
|
||||
["SDT_Libelle"] = libelle,
|
||||
["SDT_IDTypeTemps"] = 0L,
|
||||
["SDT_IDRdv"] = 0L,
|
||||
["SDT_Ponderation"] = 0L,
|
||||
["SDT_IDImport"] = 0L,
|
||||
["SDT_DateHeureFin"] = new DateTime(2026, 1, 15, 10, 0, 0),
|
||||
["GUID"] = Guid.NewGuid().ToString("D"),
|
||||
["SDT_IDTypeRdv"] = 0L,
|
||||
["SDT_Controle"] = false,
|
||||
["SDT_IDGroupe"] = 0L,
|
||||
["CreationDate"] = new DateTime(2026, 1, 15, 9, 0, 0),
|
||||
["CreationUser"] = 0L,
|
||||
["ModifDate"] = new DateTime(2026, 1, 15, 9, 0, 0),
|
||||
["ModifUser"] = 0L,
|
||||
["SDT_CleType"] = null,
|
||||
["SDT_CleTypeRDV"] = null,
|
||||
["SDT_NoteColb"] = null,
|
||||
["SDT_NoteAdmin"] = null,
|
||||
["SDT_GUID_Rdv_Colb"] = null
|
||||
};
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// FakeSuiviTempsRepository (service)
|
||||
// =========================================================================
|
||||
|
||||
private sealed class FakeSuiviTempsRepository : ISuiviTempsRepository
|
||||
{
|
||||
public bool SearchCalled { get; private set; }
|
||||
public bool GetByIdCalled { get; private set; }
|
||||
public bool CreateCalled { get; private set; }
|
||||
|
||||
public Task<PagedResult<SuiviTemps>> SearchAsync(
|
||||
SuiviTempsSearchCriteria criteria, CancellationToken cancellationToken = default)
|
||||
{
|
||||
SearchCalled = true;
|
||||
return Task.FromResult(new PagedResult<SuiviTemps>([], 1, 50, 0, 0));
|
||||
}
|
||||
|
||||
public Task<SuiviTemps?> GetByIdAsync(
|
||||
string id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
GetByIdCalled = true;
|
||||
return Task.FromResult<SuiviTemps?>(null);
|
||||
}
|
||||
|
||||
public Task<SuiviTemps> CreateAsync(
|
||||
SuiviTempsCreateCommand command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
CreateCalled = true;
|
||||
return Task.FromResult(new SuiviTemps
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Date = command.Date,
|
||||
DureeMinutes = command.DureeMinutes
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// SpyingHfsqlConnection
|
||||
// =========================================================================
|
||||
|
||||
private sealed class SpyingHfsqlConnection : IHfsqlConnection, IHfsqlWriteConnection
|
||||
{
|
||||
public HfsqlOptions Options => new();
|
||||
public HfsqlConnectionState State { get; private set; } = HfsqlConnectionState.Closed;
|
||||
|
||||
public DbTransaction? ActiveTransaction => null;
|
||||
|
||||
public int QueryCount { get; private set; }
|
||||
|
||||
public bool ThrowOnQuery { get; set; }
|
||||
public string? LastSql { get; private set; }
|
||||
public IReadOnlyList<object?>? LastParameters { get; private set; }
|
||||
public CancellationToken LastCancellationToken { get; private set; }
|
||||
public IReadOnlyList<string> AllSql => _allSql;
|
||||
private readonly List<string> _allSql = [];
|
||||
|
||||
public IReadOnlyList<IReadOnlyDictionary<string, object?>> SuiviTempsRows { get; set; } = [];
|
||||
|
||||
private void Record(string sql, IReadOnlyList<object?>? parameters, CancellationToken cancellationToken)
|
||||
{
|
||||
LastSql = sql;
|
||||
LastParameters = parameters;
|
||||
LastCancellationToken = cancellationToken;
|
||||
QueryCount++;
|
||||
_allSql.Add(sql);
|
||||
}
|
||||
|
||||
public Task OpenAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
State = HfsqlConnectionState.Open;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CloseAsync()
|
||||
{
|
||||
State = HfsqlConnectionState.Closed;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<object?> ExecuteScalarAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Record(sql, parameters, cancellationToken);
|
||||
return Task.FromResult<object?>(null);
|
||||
}
|
||||
|
||||
public Task<int> ExecuteNonQueryAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Record(sql, parameters, cancellationToken);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<IReadOnlyDictionary<string, object?>>> QueryAsync(
|
||||
string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Record(sql, parameters, cancellationToken);
|
||||
|
||||
if (ThrowOnQuery)
|
||||
throw new InvalidOperationException("Panne d'infrastructure simulée.");
|
||||
|
||||
return Task.FromResult(ResolveRows(sql));
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<HfsqlColumnMetadata>> GetColumnsAsync(
|
||||
string tableName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<HfsqlColumnMetadata>>([]);
|
||||
}
|
||||
|
||||
public Task BeginTransactionAsync(CancellationToken cancellationToken = default)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public Task CommitAsync(CancellationToken cancellationToken = default)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public Task RollbackAsync(CancellationToken cancellationToken = default)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public Task<IReadOnlyList<long?>> ExecuteInTransactionAsync(
|
||||
IReadOnlyList<HfsqlWriteOperation> operations,
|
||||
CancellationToken cancellationToken = default,
|
||||
DbTransaction? transaction = null)
|
||||
{
|
||||
foreach (HfsqlWriteOperation operation in operations)
|
||||
{
|
||||
Record(operation.Sql, operation.Parameters, cancellationToken);
|
||||
}
|
||||
|
||||
// Retourne une identité auto-générée pour chaque opération.
|
||||
return Task.FromResult<IReadOnlyList<long?>>(
|
||||
operations.Select(_ => (long?)1L).ToList());
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
State = HfsqlConnectionState.Closed;
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
private IReadOnlyList<IReadOnlyDictionary<string, object?>> ResolveRows(string sql)
|
||||
|
||||
{
|
||||
if (ContainsFrom(sql, "SuiviDesTemps"))
|
||||
return SuiviTempsRows;
|
||||
return [];
|
||||
}
|
||||
|
||||
private static bool ContainsFrom(string sql, string table)
|
||||
{
|
||||
string marker = $"FROM {table}";
|
||||
int index = sql.IndexOf(marker, StringComparison.OrdinalIgnoreCase);
|
||||
while (index >= 0)
|
||||
{
|
||||
int next = index + marker.Length;
|
||||
if (next >= sql.Length || (!char.IsLetterOrDigit(sql[next]) && sql[next] != '_'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
index = sql.IndexOf(marker, index + 1, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// MinimalFakeHfsqlConnection (DI)
|
||||
// =========================================================================
|
||||
|
||||
private sealed class MinimalFakeHfsqlConnection : IHfsqlConnection
|
||||
{
|
||||
public HfsqlOptions Options => new();
|
||||
public HfsqlConnectionState State { get; private set; } = HfsqlConnectionState.Closed;
|
||||
|
||||
public Task OpenAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
State = HfsqlConnectionState.Open;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CloseAsync()
|
||||
{
|
||||
State = HfsqlConnectionState.Closed;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<object?> ExecuteScalarAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<object?>(null);
|
||||
|
||||
public Task<int> ExecuteNonQueryAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(0);
|
||||
|
||||
public Task<IReadOnlyList<IReadOnlyDictionary<string, object?>>> QueryAsync(
|
||||
string sql, IReadOnlyList<object?>? parameters = null, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<IReadOnlyList<IReadOnlyDictionary<string, object?>>>([]);
|
||||
|
||||
public Task<IReadOnlyList<HfsqlColumnMetadata>> GetColumnsAsync(
|
||||
string tableName, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<IReadOnlyList<HfsqlColumnMetadata>>([]);
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
State = HfsqlConnectionState.Closed;
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user