214 lines
8.1 KiB
C#
214 lines
8.1 KiB
C#
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);
|
|
}
|
|
}
|