Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
using Omika.Diagnostics.Models;
|
||||
using Omika.Diagnostics.Queries;
|
||||
using Omika.Diagnostics.Tests.Fakes;
|
||||
|
||||
namespace Omika.Diagnostics.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tâche T14-C — Normalisation des dimensions d'agrégation (§9/§10) : les variantes de
|
||||
/// casse/espaces de <c>component</c> et <c>error_code</c> ne doivent plus produire de
|
||||
/// dimensions distinctes dans les statistiques. Aucune donnée persistée n'est modifiée :
|
||||
/// seule la clé d'agrégation est normalisée (déterministe, côté requête).
|
||||
/// </summary>
|
||||
public class DiagnosticNormalizationTests
|
||||
{
|
||||
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? component = null, string? errorCode = null)
|
||||
{
|
||||
return new DiagnosticEventRecord
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
EventCode = "TEST",
|
||||
TimestampUtc = From.AddMinutes(1),
|
||||
Component = component,
|
||||
ErrorCode = errorCode,
|
||||
Level = "Error",
|
||||
Priority = "P1",
|
||||
Source = DiagnosticSources.Mobile,
|
||||
IngestedAtUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------- T14-C Test 8 : Component --
|
||||
[Fact]
|
||||
public async Task Component_NormalizesCaseAndWhitespace()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(component: "Omika.Api"),
|
||||
Ev(component: "OMIKA.API"),
|
||||
Ev(component: "omika.api"),
|
||||
Ev(component: " Omika.Api "),
|
||||
Ev(component: "Omika.Worker")
|
||||
});
|
||||
|
||||
IReadOnlyList<DiagnosticGroupCount> result = await repo.CountGroupedAsync(
|
||||
DiagnosticGroupDimension.Component,
|
||||
new DiagnosticAggregateQuery { FromUtc = From, ToUtc = To });
|
||||
|
||||
// 4 variantes du même composant + 1 autre = 2 dimensions logiques.
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Contains(result, g => g.Key == "Omika.Api" && g.Count == 4);
|
||||
Assert.Contains(result, g => g.Key == "Omika.Worker" && g.Count == 1);
|
||||
}
|
||||
|
||||
// -------------------------------------------------- T14-C Test 9 : ErrorCode --
|
||||
[Fact]
|
||||
public async Task ErrorCode_NormalizesCaseAndWhitespace()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(errorCode: "timeout"),
|
||||
Ev(errorCode: "TIMEOUT"),
|
||||
Ev(errorCode: "Timeout"),
|
||||
Ev(errorCode: " timeout "),
|
||||
Ev(errorCode: "CRASH")
|
||||
});
|
||||
|
||||
IReadOnlyList<DiagnosticGroupCount> result = await repo.CountGroupedAsync(
|
||||
DiagnosticGroupDimension.ErrorCode,
|
||||
new DiagnosticAggregateQuery { FromUtc = From, ToUtc = To });
|
||||
|
||||
// 4 variantes du même code + 1 autre = 2 codes logiques.
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Contains(result, g => g.Key == "TIMEOUT" && g.Count == 4);
|
||||
Assert.Contains(result, g => g.Key == "CRASH" && g.Count == 1);
|
||||
}
|
||||
|
||||
// ------------------------- Null/vide restent un groupe distinct (compatibilité) --
|
||||
[Fact]
|
||||
public async Task ErrorCode_NullAndEmpty_RemainGroupedAsNull()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(errorCode: null),
|
||||
Ev(errorCode: " "),
|
||||
Ev(errorCode: "TIMEOUT")
|
||||
});
|
||||
|
||||
IReadOnlyList<DiagnosticGroupCount> result = await repo.CountGroupedAsync(
|
||||
DiagnosticGroupDimension.ErrorCode,
|
||||
new DiagnosticAggregateQuery { FromUtc = From, ToUtc = To });
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Contains(result, g => g.Key is null && g.Count == 2);
|
||||
Assert.Contains(result, g => g.Key == "TIMEOUT" && g.Count == 1);
|
||||
}
|
||||
|
||||
// ------------------------- T20 §7 : « Codes d'erreur principaux » ----
|
||||
[Fact]
|
||||
public async Task ErrorCode_WithHasErrorCode_ExcludesNullAndBlank()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(errorCode: null),
|
||||
Ev(errorCode: " "),
|
||||
Ev(errorCode: ""),
|
||||
Ev(errorCode: "TIMEOUT"),
|
||||
Ev(errorCode: "CRASH"),
|
||||
});
|
||||
|
||||
IReadOnlyList<DiagnosticGroupCount> result = await repo.CountGroupedAsync(
|
||||
DiagnosticGroupDimension.ErrorCode,
|
||||
new DiagnosticAggregateQuery { FromUtc = From, ToUtc = To, HasErrorCode = true });
|
||||
|
||||
// Seuls les ErrorCode réels comptent : jamais de groupe « Non renseigné ».
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.DoesNotContain(result, g => g.Key is null);
|
||||
Assert.Contains(result, g => g.Key == "TIMEOUT" && g.Count == 1);
|
||||
Assert.Contains(result, g => g.Key == "CRASH" && g.Count == 1);
|
||||
}
|
||||
|
||||
// --------------------- Équivalent C# documenté (unité, sans repository) --------
|
||||
[Theory]
|
||||
[InlineData("Omika.Api", "Omika.Api")]
|
||||
[InlineData("OMIKA.API", "Omika.Api")]
|
||||
[InlineData("omika.api", "Omika.Api")]
|
||||
[InlineData(" omika.api ", "Omika.Api")]
|
||||
[InlineData("Omika.Diagnostics.Worker", "Omika.Diagnostics.Worker")]
|
||||
[InlineData(null, null)]
|
||||
[InlineData("", null)]
|
||||
[InlineData(" ", null)]
|
||||
public void ComponentHelper_NormalizesDeterministically(string? input, string? expected)
|
||||
{
|
||||
Assert.Equal(expected, DiagnosticDimensionNormalization.Component(input));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("timeout", "TIMEOUT")]
|
||||
[InlineData("TIMEOUT", "TIMEOUT")]
|
||||
[InlineData("Timeout", "TIMEOUT")]
|
||||
[InlineData(" timeout ", "TIMEOUT")]
|
||||
[InlineData("UnhandledException", "UNHANDLEDEXCEPTION")]
|
||||
[InlineData(null, null)]
|
||||
[InlineData("", null)]
|
||||
[InlineData(" ", null)]
|
||||
public void ErrorCodeHelper_NormalizesDeterministically(string? input, string? expected)
|
||||
{
|
||||
Assert.Equal(expected, DiagnosticDimensionNormalization.ErrorCode(input));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user