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,213 @@
using Omika.Diagnostics.Models;
using Omika.Diagnostics.Queries;
using Omika.Diagnostics.Tests.Fakes;
namespace Omika.Diagnostics.Tests;
/// <summary>
/// Suppression d'un groupe d'erreurs complet (bouton « Supprimer le groupe » /Erreurs).
/// Le repository supprime TOUTES les occurrences d'un groupe (aucune borne de période),
/// restreintes aux niveaux d'erreur réels. Tests sur <see cref="FakeDiagnosticEventRepository"/> ;
/// la production exécute la même sémantique via <c>IS NOT DISTINCT FROM</c> + prédicat
/// d'erreur réel.
/// </summary>
public class DiagnosticErrorGroupDeleteTests
{
private static DiagnosticEventRecord Ev(
string eventCode = "ERR",
DateTimeOffset? ts = null,
string? errorCode = null,
string? exceptionType = null,
string? component = null,
string level = "Error") =>
new()
{
EventId = Guid.NewGuid(),
EventCode = eventCode,
TimestampUtc = ts ?? DateTimeOffset.UtcNow,
ErrorCode = errorCode,
ExceptionType = exceptionType,
Component = component,
Level = level,
Priority = "P2",
Source = DiagnosticSources.Mobile,
IngestedAtUtc = DateTimeOffset.UtcNow
};
/// <summary>
/// Identité de groupe aux clés NORMALISÉES (même normalisation que
/// <see cref="DiagnosticErrorGroupLink.TryDecode"/>, qui re-normalise à la réception).
/// </summary>
private static DiagnosticErrorGroupIdentity Identity(
string eventCode = "ERR",
string? errorCode = null,
string? exceptionType = null,
string? component = null) =>
new(eventCode,
DiagnosticErrorGrouping.NormalizeKey(errorCode),
DiagnosticErrorGrouping.NormalizeKey(exceptionType),
DiagnosticErrorGrouping.NormalizeKey(component));
// --------------------------------------- D1 : supprime tout le groupe ----
[Fact]
public async Task DeleteErrorGroup_RemovesAllOccurrences()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev("ERR", exceptionType: "System.TimeoutException"),
Ev("ERR", exceptionType: "System.TimeoutException"),
Ev("ERR", exceptionType: "System.TimeoutException")
});
long deleted = await repo.DeleteErrorGroupAsync(
Identity(exceptionType: "system.timeoutexception"));
Assert.Equal(3, deleted);
DiagnosticQueryResult<DiagnosticEventRecord> remaining =
await repo.QueryAsync(new DiagnosticEventQuery { PageSize = 10 });
Assert.Equal(0, remaining.TotalCount);
}
// ------------------------------- D2 : aucune borne de période (tout le groupe) ----
[Fact]
public async Task DeleteErrorGroup_NoPeriodBound_RemovesAncientAndRecent()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev("ERR", DateTimeOffset.UtcNow.AddYears(-2), exceptionType: "System.Exception"),
Ev("ERR", DateTimeOffset.UtcNow, exceptionType: "System.Exception")
});
long deleted = await repo.DeleteErrorGroupAsync(
Identity(exceptionType: "system.exception"));
Assert.Equal(2, deleted);
}
// ------------------------------- D3 : casse/espaces → même groupe ----
[Fact]
public async Task DeleteErrorGroup_NormalizesKeys()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev("ERR", exceptionType: "System.TimeoutException", component: "Omika.Api"),
Ev("ERR", exceptionType: "system.timeoutexception", component: " omika.api ")
});
long deleted = await repo.DeleteErrorGroupAsync(
Identity(exceptionType: "SYSTEM.TIMEOUTEXCEPTION", component: "OMIKA.API"));
Assert.Equal(2, deleted);
}
// ------------------------------- D4 : NULL ErrorCode → retrouvé ----
[Fact]
public async Task DeleteErrorGroup_NullErrorCode_IsMatched()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev("ERR", errorCode: null),
Ev("ERR", errorCode: string.Empty),
Ev("ERR", errorCode: " ")
});
long deleted = await repo.DeleteErrorGroupAsync(Identity(errorCode: null));
Assert.Equal(3, deleted); // null / '' / espaces → tous normalisés en NULL
}
// ------------------------------- D5 : autre EventCode → exclu ----
[Fact]
public async Task DeleteErrorGroup_DifferentEventCode_IsExcluded()
{
var repo = new FakeDiagnosticEventRepository();
DiagnosticEventRecord keep = Ev("OTHER", exceptionType: "System.Exception");
await repo.InsertBatchAsync(new[]
{
Ev("ERR", exceptionType: "System.Exception"),
keep
});
long deleted = await repo.DeleteErrorGroupAsync(
Identity(eventCode: "ERR", exceptionType: "system.exception"));
Assert.Equal(1, deleted);
DiagnosticQueryResult<DiagnosticEventRecord> remaining =
await repo.QueryAsync(new DiagnosticEventQuery { PageSize = 10 });
DiagnosticEventRecord item = Assert.Single(remaining.Items);
Assert.Equal(keep.EventId, item.EventId);
}
// ------------------------------- D6 : non-erreur de même signature → conservé ----
[Fact]
public async Task DeleteErrorGroup_NonErrorSameSignature_IsKept()
{
var repo = new FakeDiagnosticEventRepository();
DiagnosticEventRecord info = Ev("ERR", exceptionType: "System.Exception", level: "info");
await repo.InsertBatchAsync(new[]
{
Ev("ERR", exceptionType: "System.Exception", level: "error"),
info
});
long deleted = await repo.DeleteErrorGroupAsync(
Identity(exceptionType: "system.exception"));
Assert.Equal(1, deleted);
DiagnosticQueryResult<DiagnosticEventRecord> remaining =
await repo.QueryAsync(new DiagnosticEventQuery { PageSize = 10 });
DiagnosticEventRecord item = Assert.Single(remaining.Items);
Assert.Equal(info.EventId, item.EventId);
}
// ------------------------------- D7 : groupe inexistant → 0 ----
[Fact]
public async Task DeleteErrorGroup_Nonexistent_ReturnsZero()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[] { Ev("ERR", exceptionType: "System.Exception") });
long deleted = await repo.DeleteErrorGroupAsync(
Identity(exceptionType: "system.nullreferenceexception"));
Assert.Equal(0, deleted);
DiagnosticQueryResult<DiagnosticEventRecord> remaining =
await repo.QueryAsync(new DiagnosticEventQuery { PageSize = 10 });
Assert.Equal(1, remaining.TotalCount);
}
// ------------------------------- D8 : table vide → 0 ----
[Fact]
public async Task DeleteErrorGroup_EmptyTable_ReturnsZero()
{
var repo = new FakeDiagnosticEventRepository();
long deleted = await repo.DeleteErrorGroupAsync(Identity(exceptionType: "system.exception"));
Assert.Equal(0, deleted);
}
// ------------------------------- D9 : ne supprime pas les autres groupes ----
[Fact]
public async Task DeleteErrorGroup_LeavesOtherGroups()
{
var repo = new FakeDiagnosticEventRepository();
DiagnosticEventRecord other = Ev("ERR", exceptionType: "System.NullReferenceException");
await repo.InsertBatchAsync(new[]
{
Ev("ERR", exceptionType: "System.TimeoutException"),
other
});
long deleted = await repo.DeleteErrorGroupAsync(
Identity(exceptionType: "system.timeoutexception"));
Assert.Equal(1, deleted);
DiagnosticQueryResult<DiagnosticEventRecord> remaining =
await repo.QueryAsync(new DiagnosticEventQuery { PageSize = 10 });
DiagnosticEventRecord item = Assert.Single(remaining.Items);
Assert.Equal(other.EventId, item.EventId);
}
}