elsa-core/test/unit/Elsa.Diagnostics.StructuredLogs.Persistence.Relational.UnitTests/RelationalStructuredLogServiceCollectionExtensionsTests.cs
Sipke Schoorstra 507522469b
fix(diagnostics): align Relational structured-log sort and ListSources with InMemory (#8148)
* fix(diagnostics): align Relational structured-log sort and ListSources with InMemory

Include SourceId in the Relational ORDER BY tie-break chain, MarkSeen on the durable write path, and prefer the in-process source registry for ListSources so heartbeat and identity metadata survive Sqlite persistence.

Fixes #8117

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* fix(diagnostics): keep MarkSeen monotonic and ListSources IDs distinct

Do not regress LastSeen when a delayed older flush replays MarkSeen. Use Ordinal keys when merging registry sources so case-distinct IDs stay distinct.

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-14 09:05:01 +02:00

54 lines
2.4 KiB
C#

using System.Data.Common;
using Elsa.Diagnostics.StructuredLogs.Contracts;
using Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Contracts;
using Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Extensions;
using Elsa.Diagnostics.StructuredLogs.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Diagnostics.StructuredLogs.Persistence.Relational.UnitTests;
public class RelationalStructuredLogServiceCollectionExtensionsTests
{
[Fact]
public async Task AddRelationalStructuredLogPersistence_WhenCalledTwice_DoesNotDuplicateRelationalRegistrations()
{
var services = new ServiceCollection();
services.AddSingleton<IRelationalStructuredLogConnectionFactory, FakeConnectionFactory>();
services.AddSingleton<IRelationalStructuredLogDialect, FakeDialect>();
services.AddSingleton<IStructuredLogSourceRegistry, StructuredLogSourceRegistry>();
services.AddRelationalStructuredLogPersistence();
var diagnosticsCount = Count<IStructuredLogStorageDiagnostics>(services);
var storeCount = Count<IStructuredLogStore>(services);
var writeBufferCount = Count<IStructuredLogWriteBuffer>(services);
services.AddRelationalStructuredLogPersistence();
Assert.Equal(diagnosticsCount, Count<IStructuredLogStorageDiagnostics>(services));
Assert.Equal(storeCount, Count<IStructuredLogStore>(services));
Assert.Equal(writeBufferCount, Count<IStructuredLogWriteBuffer>(services));
await using var serviceProvider = services.BuildServiceProvider();
Assert.NotNull(serviceProvider.GetRequiredService<IStructuredLogStorageDiagnostics>());
}
private static int Count<T>(IEnumerable<ServiceDescriptor> services) => services.Count(x => x.ServiceType == typeof(T));
private class FakeConnectionFactory : IRelationalStructuredLogConnectionFactory
{
public ValueTask<DbConnection> OpenConnectionAsync(CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
}
private class FakeDialect : IRelationalStructuredLogDialect
{
public string ProviderName => "Fake";
public string ParameterPrefix => "@";
public string QuoteIdentifier(string identifier) => identifier;
public string ApplyLimit(string sql, int limit) => sql;
public string ApplyOffset(string sql, int offset) => sql;
}
}