Files
2026-10-23 18:46:50 +02:00

143 lines
5.3 KiB
C#

using Omika.Core.Observability;
using Omika.Core.Observability.Outbox;
namespace Omika.Tests.Observability.Outbox;
/// <summary>Tests du DiagnosticOutbox : filtrage P0..P3, saturation, éviction, rétention, fail-open.</summary>
public class DiagnosticOutboxTests
{
private static (DiagnosticOutbox Outbox, InMemoryDiagnosticOutboxStore Store, DiagnosticCounters Counters) Create(
DiagnosticOutboxOptions? options = null)
{
var store = new InMemoryDiagnosticOutboxStore();
var counters = new DiagnosticCounters();
var outbox = new DiagnosticOutbox(store, options ?? new DiagnosticOutboxOptions(), counters);
return (outbox, store, counters);
}
[Fact]
public async Task Store_StoresP0ThroughP3_ByDefault()
{
(DiagnosticOutbox outbox, _, DiagnosticCounters counters) = Create();
int stored = await outbox.StoreAsync(
[
OutboxTestHarness.NewEvent(TelemetryPriority.P0),
OutboxTestHarness.NewEvent(TelemetryPriority.P1),
OutboxTestHarness.NewEvent(TelemetryPriority.P2),
OutboxTestHarness.NewEvent(TelemetryPriority.P3)
]);
Assert.Equal(4, stored);
Assert.Equal(4, counters.OutboxStored);
Assert.Equal(0, counters.OutboxDropped);
}
[Fact]
public async Task Store_DropsP4_ByDefault()
{
(DiagnosticOutbox outbox, _, DiagnosticCounters counters) = Create();
int stored = await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P4)]);
Assert.Equal(0, stored);
Assert.Equal(1, counters.OutboxDropped);
}
[Fact]
public async Task Store_IncludeP4Option_StoresP4()
{
var options = new DiagnosticOutboxOptions { IncludeP4 = true };
(DiagnosticOutbox outbox, _, DiagnosticCounters counters) = Create(options);
int stored = await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P4)]);
Assert.Equal(1, stored);
Assert.Equal(1, counters.OutboxStored);
}
[Fact]
public async Task Store_EvictsP3_BeforeP0_WhenSaturated()
{
var options = new DiagnosticOutboxOptions { MaxRows = 10 };
(DiagnosticOutbox outbox, InMemoryDiagnosticOutboxStore store, DiagnosticCounters counters) = Create(options);
// Remplit l'outbox avec 10 P3.
List<OmikaEvent> p3s = Enumerable.Range(0, 10)
.Select(_ => (OmikaEvent)OutboxTestHarness.NewEvent(TelemetryPriority.P3))
.ToList();
await outbox.StoreAsync(p3s);
Assert.Equal(10, await store.CountAsync());
// Un nouveau P0 doit évincer un P3 (pas un P0).
await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P0)]);
Assert.Equal(10, await store.CountAsync());
Assert.Contains(store.Snapshot(), r => r.Priority == TelemetryPriority.P0);
Assert.Equal(1, counters.OutboxEvictedP3);
Assert.Equal(0, counters.OutboxEvictedP0);
}
[Fact]
public async Task Store_FullOfP0_DropsNewP0_WithCounter()
{
var options = new DiagnosticOutboxOptions { MaxRows = 5 };
(DiagnosticOutbox outbox, InMemoryDiagnosticOutboxStore store, DiagnosticCounters counters) = Create(options);
await outbox.StoreAsync(Enumerable.Range(0, 5)
.Select(_ => OutboxTestHarness.NewEvent(TelemetryPriority.P0))
.ToList());
Assert.Equal(5, await store.CountAsync());
int stored = await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P0)]);
Assert.Equal(0, stored);
Assert.Equal(5, await store.CountAsync());
Assert.Equal(1, counters.OutboxDroppedFull);
}
[Fact]
public async Task Store_Disabled_StoresNothing()
{
var options = new DiagnosticOutboxOptions { Enabled = false };
(DiagnosticOutbox outbox, InMemoryDiagnosticOutboxStore store, _) = Create(options);
int stored = await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P0)]);
Assert.Equal(0, stored);
Assert.Equal(0, await store.CountAsync());
}
[Fact]
public async Task CleanupExpired_RemovesExpiredOnly()
{
(DiagnosticOutbox outbox, InMemoryDiagnosticOutboxStore store, DiagnosticCounters counters) = Create();
// Utilise une rétention courte pour forcer l'expiration.
var options = new DiagnosticOutboxOptions { Retention = TimeSpan.FromMilliseconds(1) };
(outbox, store, counters) = Create(options);
await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P3)]);
await Task.Delay(20);
int removed = await outbox.CleanupExpiredAsync();
Assert.Equal(1, removed);
Assert.Equal(1, counters.OutboxExpired);
Assert.Equal(0, await store.CountAsync());
}
[Fact]
public async Task Store_ThrowingStore_FailOpen_DegradesAndCounts()
{
var store = new InMemoryDiagnosticOutboxStore { ThrowOnStore = true };
var counters = new DiagnosticCounters();
var outbox = new DiagnosticOutbox(store, new DiagnosticOutboxOptions(), counters);
int stored = await outbox.StoreAsync([OutboxTestHarness.NewEvent(TelemetryPriority.P0)]);
Assert.Equal(0, stored);
Assert.Equal(1, counters.OutboxDegraded);
Assert.True(outbox.IsDegraded);
}
}