143 lines
5.3 KiB
C#
143 lines
5.3 KiB
C#
using Omika.App.Local.Diagnostics;
|
|
using Omika.Core.Observability;
|
|
using Omika.Core.Observability.Outbox;
|
|
|
|
namespace Omika.Tests.Observability.Outbox;
|
|
|
|
/// <summary>Tests du store SQLite séparé (omika-diagnostics.db) : persistance, rétention, éviction, redémarrage.</summary>
|
|
public class DiagnosticOutboxSqliteStoreTests : IAsyncLifetime
|
|
{
|
|
private readonly string _dir = Path.Combine(Path.GetTempPath(), "omika-diag-sqlite-tests", Guid.NewGuid().ToString("N"));
|
|
private string DbPath => Path.Combine(_dir, "omika-diagnostics.db");
|
|
|
|
public Task InitializeAsync()
|
|
{
|
|
Directory.CreateDirectory(_dir);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task DisposeAsync()
|
|
{
|
|
try
|
|
{
|
|
if (Directory.Exists(_dir))
|
|
{
|
|
Directory.Delete(_dir, recursive: true);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Best-effort.
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static DiagnosticOutboxRow NewRow(TelemetryPriority priority = TelemetryPriority.P3, string payload = "{}") =>
|
|
new()
|
|
{
|
|
EventId = Guid.NewGuid(),
|
|
Priority = priority,
|
|
CreatedAtUtc = DateTimeOffset.UtcNow,
|
|
ExpiresAtUtc = DateTimeOffset.UtcNow.AddDays(7),
|
|
PayloadVersion = 1,
|
|
PayloadJson = payload,
|
|
AttemptCount = 0,
|
|
NextAttemptUtc = DateTimeOffset.UtcNow
|
|
};
|
|
|
|
[Fact]
|
|
public async Task StoreAndRetrieve_RoundTrip()
|
|
{
|
|
await using var store = new DiagnosticOutboxSqliteStore(new DiagnosticOutboxOptions { DatabasePath = DbPath });
|
|
DiagnosticOutboxRow row = NewRow(payload: """{"eventId":"00000000-0000-0000-0000-000000000001"}""");
|
|
|
|
await store.StoreAsync([row]);
|
|
IReadOnlyList<DiagnosticOutboxRow> batch = await store.GetNextBatchAsync(10, 1024 * 1024, DateTimeOffset.UtcNow);
|
|
|
|
DiagnosticOutboxRow retrieved = Assert.Single(batch);
|
|
Assert.Equal(row.EventId, retrieved.EventId);
|
|
Assert.Equal(row.PayloadJson, retrieved.PayloadJson);
|
|
Assert.Equal(row.Priority, retrieved.Priority);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetNextBatch_RespectsNextAttemptUtc()
|
|
{
|
|
await using var store = new DiagnosticOutboxSqliteStore(new DiagnosticOutboxOptions { DatabasePath = DbPath });
|
|
DiagnosticOutboxRow future = NewRow();
|
|
future = new DiagnosticOutboxRow
|
|
{
|
|
EventId = future.EventId,
|
|
Priority = future.Priority,
|
|
CreatedAtUtc = future.CreatedAtUtc,
|
|
ExpiresAtUtc = future.ExpiresAtUtc,
|
|
PayloadVersion = future.PayloadVersion,
|
|
PayloadJson = future.PayloadJson,
|
|
NextAttemptUtc = DateTimeOffset.UtcNow.AddMinutes(5)
|
|
};
|
|
await store.StoreAsync([future]);
|
|
|
|
IReadOnlyList<DiagnosticOutboxRow> batch = await store.GetNextBatchAsync(10, 1024 * 1024, DateTimeOffset.UtcNow);
|
|
Assert.Empty(batch);
|
|
|
|
IReadOnlyList<DiagnosticOutboxRow> later = await store.GetNextBatchAsync(10, 1024 * 1024, DateTimeOffset.UtcNow.AddMinutes(10));
|
|
Assert.Single(later);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteExpired_RemovesOnlyExpired()
|
|
{
|
|
await using var store = new DiagnosticOutboxSqliteStore(new DiagnosticOutboxOptions { DatabasePath = DbPath });
|
|
DiagnosticOutboxRow expired = NewRow();
|
|
expired = new DiagnosticOutboxRow
|
|
{
|
|
EventId = expired.EventId,
|
|
Priority = expired.Priority,
|
|
CreatedAtUtc = expired.CreatedAtUtc,
|
|
ExpiresAtUtc = DateTimeOffset.UtcNow.AddMinutes(-1),
|
|
PayloadVersion = expired.PayloadVersion,
|
|
PayloadJson = expired.PayloadJson
|
|
};
|
|
await store.StoreAsync([expired, NewRow()]);
|
|
|
|
int removed = await store.DeleteExpiredAsync(DateTimeOffset.UtcNow);
|
|
|
|
Assert.Equal(1, removed);
|
|
Assert.Equal(1, await store.CountAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteOldest_RemovesOldestOfPriority()
|
|
{
|
|
await using var store = new DiagnosticOutboxSqliteStore(new DiagnosticOutboxOptions { DatabasePath = DbPath });
|
|
DiagnosticOutboxRow old = NewRow(TelemetryPriority.P3);
|
|
await store.StoreAsync([old]);
|
|
await Task.Delay(10);
|
|
DiagnosticOutboxRow newer = NewRow(TelemetryPriority.P3);
|
|
await store.StoreAsync([newer]);
|
|
|
|
int removed = await store.DeleteOldestAsync(TelemetryPriority.P3, 1);
|
|
|
|
Assert.Equal(1, removed);
|
|
Assert.False((await store.GetNextBatchAsync(10, 1024 * 1024, DateTimeOffset.UtcNow)).Any(r => r.EventId == old.EventId));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Restart_PersistsRowsAcrossSessions()
|
|
{
|
|
DiagnosticOutboxRow row = NewRow(payload: """{"eventId":"00000000-0000-0000-0000-00000000000A"}""");
|
|
await using (var store = new DiagnosticOutboxSqliteStore(new DiagnosticOutboxOptions { DatabasePath = DbPath }))
|
|
{
|
|
await store.StoreAsync([row]);
|
|
}
|
|
|
|
// « Redémarrage » : nouvelle connexion sur le même fichier.
|
|
await using (var store = new DiagnosticOutboxSqliteStore(new DiagnosticOutboxOptions { DatabasePath = DbPath }))
|
|
{
|
|
IReadOnlyList<DiagnosticOutboxRow> batch = await store.GetNextBatchAsync(10, 1024 * 1024, DateTimeOffset.UtcNow);
|
|
Assert.Equal(row.EventId, Assert.Single(batch).EventId);
|
|
}
|
|
}
|
|
}
|