Premiere commit - Omika

This commit is contained in:
2026-10-23 18:46:50 +02:00
commit 8ea76c1b24
2155 changed files with 984301 additions and 0 deletions
@@ -0,0 +1,139 @@
using Omika.Core.Configuration;
using Omika.Core.Observability;
namespace Omika.Tests.Observability;
public class TelemetryWorkerTests
{
[Fact]
public async Task Worker_DrainsToExporter()
{
var (queue, counters) = CreateQueue();
var exporter = new CollectingExporter();
var worker = new TelemetryWorker(queue, exporter);
queue.TryEnqueue(ObservabilityTestHarness.NewEvent(TelemetryEventCodes.MobileAppStarted));
queue.TryEnqueue(ObservabilityTestHarness.NewEvent(TelemetryEventCodes.MobileAppStarted));
using var cts = new CancellationTokenSource();
Task run = worker.ExecuteAsync(cts.Token);
await ObservabilityTestHarness.WaitUntilAsync(() => exporter.Count == 2);
cts.Cancel();
await run;
}
[Fact]
public async Task Worker_DoesNotStarveLowPriority()
{
var (queue, _) = CreateQueue();
var exporter = new CollectingExporter();
var worker = new TelemetryWorker(queue, exporter);
// Fort arriéré de P0 + quelques P4 : les P4 doivent quand même être exportés.
for (int i = 0; i < 100; i++)
{
queue.TryEnqueue(ObservabilityTestHarness.NewEvent(
TelemetryEventCodes.MobileSyncOperationReceived, priority: TelemetryPriority.P0));
}
for (int i = 0; i < 10; i++)
{
queue.TryEnqueue(ObservabilityTestHarness.NewEvent(
TelemetryEventCodes.MobileAppStarted, priority: TelemetryPriority.P4));
}
using var cts = new CancellationTokenSource();
Task run = worker.ExecuteAsync(cts.Token);
await ObservabilityTestHarness.WaitUntilAsync(() => exporter.Count == 110);
cts.Cancel();
await run;
Assert.Equal(100, exporter.ByPriority(TelemetryPriority.P0));
Assert.Equal(10, exporter.ByPriority(TelemetryPriority.P4));
}
[Fact]
public async Task Worker_ExporterThrows_DoesNotPropagate()
{
var (queue, _) = CreateQueue();
var worker = new TelemetryWorker(queue, new ThrowingExporter());
queue.TryEnqueue(ObservabilityTestHarness.NewEvent(TelemetryEventCodes.MobileAppStarted));
using var cts = new CancellationTokenSource();
Task run = worker.ExecuteAsync(cts.Token);
// Laisse au worker le temps de tenter l'export, d'échouer et de reprendre.
await Task.Delay(300);
cts.Cancel();
await run;
// Le lot a été abandonné (perte de log acceptable) ; aucune exception.
Assert.Equal(0, queue.Count);
}
[Fact]
public async Task Worker_Cancellation_Stops()
{
var (queue, _) = CreateQueue();
var worker = new TelemetryWorker(queue, new NullTelemetryExporter());
using var cts = new CancellationTokenSource();
Task run = worker.ExecuteAsync(cts.Token);
cts.Cancel();
await run;
}
private static (PriorityTelemetryQueue Queue, TelemetryCounters Counters) CreateQueue()
{
var options = new ObservabilityOptions();
var counters = new TelemetryCounters();
return (new PriorityTelemetryQueue(options, counters), counters);
}
private sealed class CollectingExporter : ITelemetryExporter
{
private readonly object _lock = new();
private readonly List<OmikaEvent> _exported = [];
public int Count
{
get
{
lock (_lock)
{
return _exported.Count;
}
}
}
public int ByPriority(TelemetryPriority priority)
{
lock (_lock)
{
return _exported.Count(e => e.EffectivePriority == priority);
}
}
public Task ExportAsync(IReadOnlyList<OmikaEvent> events, CancellationToken cancellationToken = default)
{
lock (_lock)
{
_exported.AddRange(events);
}
return Task.CompletedTask;
}
}
private sealed class ThrowingExporter : ITelemetryExporter
{
public Task ExportAsync(IReadOnlyList<OmikaEvent> events, CancellationToken cancellationToken = default) =>
throw new InvalidOperationException("exporteur défaillant (test)");
}
}