246 lines
8.4 KiB
C#
246 lines
8.4 KiB
C#
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);
|
|
}
|
|
}
|