Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
using Omika.Diagnostics.Models;
|
||||
using Omika.Diagnostics.Queries;
|
||||
using Omika.Diagnostics.Tests.Fakes;
|
||||
|
||||
namespace Omika.Diagnostics.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tâche 8 — Synchronisation : résumé sync et filtres par liste fermée d'EventCodes.
|
||||
/// Tests sur <see cref="FakeDiagnosticEventRepository"/> ; la production exécute la même
|
||||
/// sémantique via COUNT/COUNT(DISTINCT)/FILTER/AVG côté PostgreSQL.
|
||||
/// </summary>
|
||||
public class DiagnosticSyncTests
|
||||
{
|
||||
private static readonly DateTimeOffset From = new(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
private static readonly DateTimeOffset To = new(2025, 2, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
|
||||
private static DiagnosticEventRecord Ev(
|
||||
string eventCode,
|
||||
DateTimeOffset? ts = null,
|
||||
Guid? operationId = null,
|
||||
string level = "Info",
|
||||
long? durationMs = null) =>
|
||||
new()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
EventCode = eventCode,
|
||||
TimestampUtc = ts ?? From.AddMinutes(1),
|
||||
OperationId = operationId,
|
||||
Level = level,
|
||||
Priority = "P2",
|
||||
DurationMs = durationMs,
|
||||
Source = DiagnosticSources.Mobile,
|
||||
IngestedAtUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
private static DiagnosticAggregateQuery All() => new() { FromUtc = From, ToUtc = To };
|
||||
|
||||
// ------------------------------------------------- S1 : summary multi ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_MultipleEvents_ReturnsSummary()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001", operationId: Guid.NewGuid()),
|
||||
Ev("OMK-WKR-SYNC-001", operationId: Guid.NewGuid())
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(2, summary.EventCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S2 : EventCount ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_EventCount_CountsOnlySync()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001"),
|
||||
Ev("OMK-WKR-SYNC-001"),
|
||||
Ev("NOT-SYNC")
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(2, summary.EventCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S3 : OperationCount distinct ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_OperationCount_IsDistinct()
|
||||
{
|
||||
Guid opA = Guid.NewGuid();
|
||||
Guid opB = Guid.NewGuid();
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001", operationId: opA),
|
||||
Ev("OMK-MOB-SYNC-001", operationId: opA), // même opération
|
||||
Ev("OMK-WKR-SYNC-001", operationId: opB)
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(2, summary.OperationCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S4 : OperationId null exclu ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_NullOperationId_NotCounted()
|
||||
{
|
||||
Guid opA = Guid.NewGuid();
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001", operationId: opA),
|
||||
Ev("OMK-MOB-SYNC-001", operationId: null) // sans OperationId
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(1, summary.OperationCount);
|
||||
Assert.Equal(2, summary.EventCount); // l'événement sans OperationId reste compté
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S5 : ErrorCount casse mixte ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_ErrorCount_MixedCase()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-WKR-SYNC-002", level: "Error"),
|
||||
Ev("OMK-WKR-SYNC-002", level: "error"),
|
||||
Ev("OMK-WKR-SYNC-002", level: "Critical"),
|
||||
Ev("OMK-WKR-SYNC-001", level: "Info")
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(3, summary.ErrorCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S6 : durée avg/min/max ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_DurationStats_AreCorrect()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-WKR-SYNC-001", durationMs: 100),
|
||||
Ev("OMK-WKR-SYNC-001", durationMs: 300)
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(2, summary.DurationCount);
|
||||
Assert.Equal(200, summary.AverageMs);
|
||||
Assert.Equal(100, summary.MinMs);
|
||||
Assert.Equal(300, summary.MaxMs);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S7 : durée NULL ignorée ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_NullDurations_Ignored()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-WKR-SYNC-001", durationMs: 100),
|
||||
Ev("OMK-WKR-SYNC-001", durationMs: null)
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(1, summary.DurationCount);
|
||||
Assert.Equal(100, summary.AverageMs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_NoDurations_NullStats()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001", durationMs: null)
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(0, summary.DurationCount);
|
||||
Assert.Null(summary.AverageMs);
|
||||
Assert.Null(summary.MinMs);
|
||||
Assert.Null(summary.MaxMs);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S8 : période ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_RespectsPeriod()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001", From.AddMinutes(-5)), // avant
|
||||
Ev("OMK-MOB-SYNC-001", From.AddMinutes(5)), // dans
|
||||
Ev("OMK-MOB-SYNC-001", To.AddMinutes(5)) // après
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(1, summary.EventCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S9 : non-sync exclu ----
|
||||
[Fact]
|
||||
public async Task GetSyncSummary_NonSyncExcluded()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001"),
|
||||
Ev("OMK-MOB-APP-001") // non-sync
|
||||
});
|
||||
|
||||
DiagnosticSyncSummary summary = await repo.GetSyncSummaryAsync(All());
|
||||
|
||||
Assert.Equal(1, summary.EventCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S10 : reconnaissance ----
|
||||
[Theory]
|
||||
[InlineData("OMK-MOB-SYNC-001", true)]
|
||||
[InlineData("OMK-WKR-SYNC-001", true)]
|
||||
[InlineData("OMK-WKR-SYNC-002", true)]
|
||||
[InlineData("OMK-MOB-APP-001", false)]
|
||||
[InlineData(null, false)]
|
||||
public void SyncEventCodes_Recognition(string? code, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, DiagnosticSyncEventCodes.IsSyncEventCode(code));
|
||||
}
|
||||
|
||||
// ------------------------------------------------- S11 : distribution EventCode ----
|
||||
[Fact]
|
||||
public async Task CountGrouped_EventCode_SyncOnly_ReturnsOnlySyncCodes()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev("OMK-MOB-SYNC-001"),
|
||||
Ev("OMK-MOB-SYNC-001"),
|
||||
Ev("OMK-WKR-SYNC-001"),
|
||||
Ev("OMK-MOB-APP-001") // non-sync → exclu
|
||||
});
|
||||
|
||||
IReadOnlyList<DiagnosticGroupCount> result = await repo.CountGroupedAsync(
|
||||
DiagnosticGroupDimension.EventCode,
|
||||
new DiagnosticAggregateQuery { FromUtc = From, ToUtc = To, SyncOnly = true });
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.DoesNotContain(result, g => g.Key == "OMK-MOB-APP-001");
|
||||
Assert.Contains(result, g => g.Key == "OMK-MOB-SYNC-001" && g.Count == 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user