using Elsa.Common; using Elsa.Persistence.VNext.Builders; using Elsa.Persistence.VNext.Contracts; using Elsa.Persistence.VNext.Document; using Elsa.Persistence.VNext.Extensions; using Elsa.Persistence.VNext.Extensions.Contracts; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Elsa.Persistence.VNext.UnitTests; public class PersistenceVNextIntegrationTests { [Fact] public void SchemaCatalog_ComposesRegisteredManifests() { var services = new ServiceCollection(); services.AddLogging(); services.AddPersistenceVNext(); services.AddSingleton(); services.AddSingleton(); using var serviceProvider = services.BuildServiceProvider(); var schema = serviceProvider.GetRequiredService().DescribeSchema(); Assert.Equal("Elsa", schema.Name); Assert.Contains(schema.StorageUnits, x => x.Name == "Orders"); Assert.Contains(schema.StorageUnits, x => x.Name == "Customers"); } [Fact] public async Task StartupTask_MaterializesRegisteredDocumentStoresAndRecordsStatus() { var store = new RecordingDocumentStore(); var services = new ServiceCollection(); services.AddLogging(); services.AddPersistenceVNext(); services.AddSingleton(); services.AddSingleton(store); using var serviceProvider = services.BuildServiceProvider(); var startupTask = Assert.Single(serviceProvider.GetServices()); await startupTask.ExecuteAsync(CancellationToken.None); var status = serviceProvider.GetRequiredService().Snapshot; Assert.True(store.WasMaterialized); Assert.True(status.Succeeded); Assert.Contains("Orders", status.StorageUnits); Assert.Contains(typeof(RecordingDocumentStore).FullName!, status.DocumentStoreTypes); } [Fact] public async Task StartupTask_RecordsRecoveryHintsWhenMaterializationFails() { var services = new ServiceCollection(); services.AddLogging(); services.AddPersistenceVNext(); services.AddSingleton(); services.AddSingleton(); using var serviceProvider = services.BuildServiceProvider(); var startupTask = Assert.Single(serviceProvider.GetServices()); await Assert.ThrowsAsync(() => startupTask.ExecuteAsync(CancellationToken.None)); var status = serviceProvider.GetRequiredService().Snapshot; Assert.False(status.Succeeded); Assert.Equal("provider unavailable", status.ErrorMessage); Assert.Contains(status.RecoveryHints, x => x.Contains("connect to its database")); Assert.Contains(status.RecoveryHints, x => x.Contains("materialization lock strategy")); } private class OrdersSchemaProvider : IPersistenceSchemaProvider { public PersistenceSchema DescribeSchema() { return new PersistenceSchemaBuilder("Orders") .StorageUnit("Orders", storage => storage .RequiredField("Id", PersistenceColumnType.String, 450) .RequiredField("Status", PersistenceColumnType.String, 50) .Key("PK_Orders", "Id") .Index("IX_Orders_Status", "Status")) .Build(); } } private class CustomersSchemaProvider : IPersistenceSchemaProvider { public PersistenceSchema DescribeSchema() { return new PersistenceSchemaBuilder("Customers") .StorageUnit("Customers", storage => storage .RequiredField("Id", PersistenceColumnType.String, 450) .RequiredField("Name", PersistenceColumnType.String, 200) .Key("PK_Customers", "Id") .Index("IX_Customers_Name", "Name")) .Build(); } } private class RecordingDocumentStore : IDocumentStore { public bool WasMaterialized { get; private set; } public Task MaterializeAsync(CancellationToken cancellationToken = default) { WasMaterialized = true; return Task.CompletedTask; } public Task SaveAsync(SaveDocumentRequest request, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task LoadAsync(string storageUnit, string id, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task DeleteAsync(string storageUnit, string id, long? expectedVersion = null, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task> QueryAsync(DocumentQuery query, CancellationToken cancellationToken = default) => throw new NotSupportedException(); } private class FailingDocumentStore : IDocumentStore { public Task MaterializeAsync(CancellationToken cancellationToken = default) => throw new InvalidOperationException("provider unavailable"); public Task SaveAsync(SaveDocumentRequest request, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task LoadAsync(string storageUnit, string id, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task DeleteAsync(string storageUnit, string id, long? expectedVersion = null, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public Task> QueryAsync(DocumentQuery query, CancellationToken cancellationToken = default) => throw new NotSupportedException(); } }