365 lines
14 KiB
C#
365 lines
14 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Omika.Core.Abstractions;
|
|
using Omika.Core.Compatibility;
|
|
using Omika.Core.Runtime;
|
|
using Omika.Data;
|
|
using Omika.Data.Runtime;
|
|
using Omika.Data.SchemaDetection;
|
|
|
|
namespace Omika.Tests.Runtime;
|
|
|
|
[Collection("ApplicationRuntimeContext")]
|
|
public class DatabaseRuntimeInitializerTests
|
|
{
|
|
|
|
// --- Auto + détecté Batik => succès --------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_AutoWithDetectedBatik_ReturnsSuccess()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Batik);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsSuccess);
|
|
Assert.True(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsOmika);
|
|
Assert.Equal(DatabaseSchemaVersion.Batik, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
}
|
|
|
|
// --- Auto + détecté Omika => succès --------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_AutoWithDetectedOmika_ReturnsSuccess()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Omika);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsSuccess);
|
|
Assert.True(ApplicationRuntimeContext.Current.IsOmika);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.Equal(DatabaseSchemaVersion.Omika, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
}
|
|
|
|
// --- Batik + détecté Batik => succès -------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_BatikWithDetectedBatik_ReturnsSuccess()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Batik);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Batik);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsSuccess);
|
|
Assert.True(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.Equal(DatabaseSchemaVersion.Batik, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
}
|
|
|
|
// --- Omika + détecté Omika => succès -------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_OmikaWithDetectedOmika_ReturnsSuccess()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Omika);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Omika);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsSuccess);
|
|
Assert.True(ApplicationRuntimeContext.Current.IsOmika);
|
|
Assert.Equal(DatabaseSchemaVersion.Omika, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
}
|
|
|
|
// --- Batik + détecté Omika => échec --------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_BatikWithDetectedOmika_ReturnsFailureAndKeepsUnknown()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Batik);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Omika);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.NotEmpty(result.Errors);
|
|
Assert.Equal(DatabaseSchemaVersion.Unknown, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsOmika);
|
|
}
|
|
|
|
// --- Omika + détecté Batik => échec --------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_OmikaWithDetectedBatik_ReturnsFailureAndKeepsUnknown()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Omika);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Batik);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.NotEmpty(result.Errors);
|
|
Assert.Equal(DatabaseSchemaVersion.Unknown, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsOmika);
|
|
}
|
|
|
|
// --- Unknown => échec ----------------------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_DetectedUnknown_ReturnsFailureAndKeepsUnknown()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Unknown);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.NotEmpty(result.Errors);
|
|
Assert.Equal(DatabaseSchemaVersion.Unknown, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsOmika);
|
|
}
|
|
|
|
// --- échec du détecteur => runtime non activé ----------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_DetectorFailure_ReturnsFailureAndDoesNotActivateRuntime()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var detector = new FakeDetector(Result<DatabaseSchemaVersion>.Failure("Échec de lecture des métadonnées."));
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.Contains("métadonnées", result.Errors[0], StringComparison.OrdinalIgnoreCase);
|
|
Assert.Equal(DatabaseSchemaVersion.Unknown, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsOmika);
|
|
}
|
|
|
|
// --- cancellation => propagation correcte --------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_CancellationRequested_Propagates()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Batik);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
using var cts = new CancellationTokenSource();
|
|
cts.Cancel();
|
|
|
|
await Assert.ThrowsAnyAsync<OperationCanceledException>(
|
|
() => initializer.InitializeAsync(cts.Token));
|
|
}
|
|
|
|
// --- détecteur appelé une seule fois par initialisation ------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_DetectorCalledExactlyOnce()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Omika);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
await initializer.InitializeAsync();
|
|
|
|
Assert.Equal(1, detector.DetectCallCount);
|
|
}
|
|
|
|
// --- DetectedVersion renseigné uniquement après succès -------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_DetectedVersionSetOnlyOnSuccess()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Omika);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Batik);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsFailure);
|
|
Assert.Equal(DatabaseSchemaVersion.Unknown, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
}
|
|
|
|
// --- IsBatik correct après Batik -----------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_AfterBatik_IsBatikTrue()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var initializer = new DatabaseRuntimeInitializer(new FakeDetector(DatabaseSchemaVersion.Batik));
|
|
|
|
await initializer.InitializeAsync();
|
|
|
|
Assert.True(ApplicationRuntimeContext.Current.IsBatik);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsOmika);
|
|
}
|
|
|
|
// --- IsOmika correct après Omika -----------------------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_AfterOmika_IsOmikaTrue()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var initializer = new DatabaseRuntimeInitializer(new FakeDetector(DatabaseSchemaVersion.Omika));
|
|
|
|
await initializer.InitializeAsync();
|
|
|
|
Assert.True(ApplicationRuntimeContext.Current.IsOmika);
|
|
Assert.False(ApplicationRuntimeContext.Current.IsBatik);
|
|
}
|
|
|
|
// --- capabilities cohérentes avec la version détectée --------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_AfterOmika_CapabilitiesReflectDetectedVersion()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var initializer = new DatabaseRuntimeInitializer(new FakeDetector(DatabaseSchemaVersion.Omika));
|
|
|
|
await initializer.InitializeAsync();
|
|
|
|
var capabilities = ApplicationRuntimeContext.Current.Capabilities;
|
|
Assert.Equal(DatabaseSchemaVersion.Omika, capabilities.Version);
|
|
Assert.True(capabilities.SupportsEntityGuids);
|
|
Assert.True(capabilities.SupportsRoles);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_AfterBatik_CapabilitiesReflectDetectedVersion()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Auto);
|
|
var initializer = new DatabaseRuntimeInitializer(new FakeDetector(DatabaseSchemaVersion.Batik));
|
|
|
|
await initializer.InitializeAsync();
|
|
|
|
var capabilities = ApplicationRuntimeContext.Current.Capabilities;
|
|
Assert.Equal(DatabaseSchemaVersion.Batik, capabilities.Version);
|
|
Assert.False(capabilities.SupportsEntityGuids);
|
|
Assert.False(capabilities.SupportsRoles);
|
|
}
|
|
|
|
// --- aucune sélection configurée utilisée comme version détectée ---------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_SelectionIsNeverUsedAsDetectedVersion()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
// Sélection Omika, mais la base réellement détectée est Batik.
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Omika);
|
|
var initializer = new DatabaseRuntimeInitializer(new FakeDetector(DatabaseSchemaVersion.Batik));
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
// La sélection ne doit jamais être prise pour la version détectée : échec.
|
|
Assert.True(result.IsFailure);
|
|
Assert.Equal(DatabaseSchemaVersion.Unknown, ApplicationRuntimeContext.Current.DetectedVersion);
|
|
}
|
|
|
|
// --- aucun secret dans les messages d'erreur -----------------------------
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_ErrorMessagesDoNotContainSecrets()
|
|
{
|
|
ApplicationRuntimeContext.Reset();
|
|
ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Batik);
|
|
var detector = new FakeDetector(DatabaseSchemaVersion.Omika);
|
|
var initializer = new DatabaseRuntimeInitializer(detector);
|
|
|
|
var result = await initializer.InitializeAsync();
|
|
|
|
Assert.True(result.IsFailure);
|
|
foreach (var error in result.Errors)
|
|
{
|
|
Assert.DoesNotContain("password", error, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("pwd", error, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("connectionstring", error, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("dsn", error, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
|
|
// --- service d'initialisation enregistré en Scoped -----------------------
|
|
|
|
[Fact]
|
|
public void AddOmikaData_RegistersInitializerAsScoped()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddOmikaData();
|
|
|
|
var descriptor = services.Single(d => d.ServiceType == typeof(IDatabaseRuntimeInitializer));
|
|
|
|
Assert.Equal(ServiceLifetime.Scoped, descriptor.Lifetime);
|
|
Assert.Equal(typeof(DatabaseRuntimeInitializer), descriptor.ImplementationType);
|
|
}
|
|
|
|
// --- Program utilise un scope correct pour les services Scoped -----------
|
|
|
|
[Fact]
|
|
public void AddOmikaData_InitializerDependsOnScopedDetector()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddOmikaData();
|
|
|
|
var initializerDescriptor = services.Single(d => d.ServiceType == typeof(IDatabaseRuntimeInitializer));
|
|
var detectorDescriptor = services.Single(d => d.ServiceType == typeof(IDatabaseSchemaDetector));
|
|
|
|
// L'initialisateur consomme le détecteur du scope courant : les deux sont Scoped.
|
|
Assert.Equal(ServiceLifetime.Scoped, initializerDescriptor.Lifetime);
|
|
Assert.Equal(ServiceLifetime.Scoped, detectorDescriptor.Lifetime);
|
|
}
|
|
|
|
// --- Fake de détecteur ---------------------------------------------------
|
|
|
|
private sealed class FakeDetector : IDatabaseSchemaDetector
|
|
{
|
|
private readonly Result<DatabaseSchemaVersion> _result;
|
|
|
|
public FakeDetector(DatabaseSchemaVersion version)
|
|
: this(Result<DatabaseSchemaVersion>.Success(version))
|
|
{
|
|
}
|
|
|
|
public FakeDetector(Result<DatabaseSchemaVersion> result)
|
|
{
|
|
_result = result;
|
|
}
|
|
|
|
public int DetectCallCount { get; private set; }
|
|
|
|
public Task<Result<DatabaseSchemaVersion>> DetectAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
DetectCallCount++;
|
|
return Task.FromResult(_result);
|
|
}
|
|
}
|
|
}
|