111 lines
3.8 KiB
C#
111 lines
3.8 KiB
C#
using Omika.Diagnostics.Queries;
|
|
|
|
namespace Omika.Diagnostics.Tests;
|
|
|
|
/// <summary>
|
|
/// Tâche 5 — Identifiant URL-safe du groupe d'erreurs (§T5/12/28). Le codec ne doit
|
|
/// JAMAIS lever depuis une URL malformée (décodage défensif → null) et doit faire un
|
|
/// aller-retour fidèle (caractères spéciaux, null, séparateur de contrôle).
|
|
/// </summary>
|
|
public class DiagnosticErrorGroupLinkTests
|
|
{
|
|
[Fact]
|
|
public void EncodeDecode_RoundTrip_PreservesIdentity()
|
|
{
|
|
var identity = new DiagnosticErrorGroupIdentity(
|
|
"ERR",
|
|
"timeout",
|
|
"system.timeoutexception",
|
|
"omika.api");
|
|
|
|
string encoded = DiagnosticErrorGroupLink.Encode(identity);
|
|
DiagnosticErrorGroupIdentity? decoded = DiagnosticErrorGroupLink.TryDecode(encoded);
|
|
|
|
Assert.NotNull(decoded);
|
|
Assert.Equal(identity, decoded);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncodeDecode_WithNulls_RoundTrips()
|
|
{
|
|
var identity = new DiagnosticErrorGroupIdentity("ERR", null, null, null);
|
|
|
|
DiagnosticErrorGroupIdentity? decoded = DiagnosticErrorGroupLink.TryDecode(
|
|
DiagnosticErrorGroupLink.Encode(identity));
|
|
|
|
Assert.NotNull(decoded);
|
|
Assert.Null(decoded.NormalizedErrorCode);
|
|
Assert.Null(decoded.NormalizedExceptionType);
|
|
Assert.Null(decoded.NormalizedComponent);
|
|
Assert.Equal("ERR", decoded.EventCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncodeDecode_SpecialCharacters_RoundTrips()
|
|
{
|
|
var identity = new DiagnosticErrorGroupIdentity(
|
|
"OMK/API-001_2.3",
|
|
"code+avec=et&des?",
|
|
"type avec espaces / accents éà",
|
|
"component\u001Favec-séparateur");
|
|
|
|
DiagnosticErrorGroupIdentity? decoded = DiagnosticErrorGroupLink.TryDecode(
|
|
DiagnosticErrorGroupLink.Encode(identity));
|
|
|
|
Assert.NotNull(decoded);
|
|
Assert.Equal(identity, decoded);
|
|
}
|
|
|
|
[Fact]
|
|
public void Encode_ProducesUrlSafeCharactersOnly()
|
|
{
|
|
string encoded = DiagnosticErrorGroupLink.Encode(
|
|
new DiagnosticErrorGroupIdentity("ERR", "a/b+c=d", "e", "f"));
|
|
|
|
Assert.All(encoded, c => Assert.True(
|
|
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_'));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryDecode_NullOrEmpty_ReturnsNull()
|
|
{
|
|
Assert.Null(DiagnosticErrorGroupLink.TryDecode(null));
|
|
Assert.Null(DiagnosticErrorGroupLink.TryDecode(string.Empty));
|
|
Assert.Null(DiagnosticErrorGroupLink.TryDecode(" "));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("!!!not-base64!!!")]
|
|
[InlineData("abc")] // base64 invalide (longueur)
|
|
[InlineData("YWJjZA==")] // base64 standard avec padding '=' (non URL-safe)
|
|
public void TryDecode_Invalid_ReturnsNull(string encoded)
|
|
{
|
|
Assert.Null(DiagnosticErrorGroupLink.TryDecode(encoded));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryDecode_ReNormalizesKeys()
|
|
{
|
|
// On encode volontairement des clés NON normalisées ; le décodage re-normalise
|
|
// (défense en profondeur) pour rester cohérent avec le regroupement.
|
|
string encoded = DiagnosticErrorGroupLink.Encode(
|
|
new DiagnosticErrorGroupIdentity("ERR", " TimeOut ", "System.TimeoutException", "Omika.Api"));
|
|
|
|
DiagnosticErrorGroupIdentity? decoded = DiagnosticErrorGroupLink.TryDecode(encoded);
|
|
|
|
Assert.NotNull(decoded);
|
|
Assert.Equal("timeout", decoded.NormalizedErrorCode);
|
|
Assert.Equal("system.timeoutexception", decoded.NormalizedExceptionType);
|
|
Assert.Equal("omika.api", decoded.NormalizedComponent);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryDecode_EmptyEventCode_ReturnsNull()
|
|
{
|
|
string encoded = DiagnosticErrorGroupLink.Encode(
|
|
new DiagnosticErrorGroupIdentity("", "a", "b", "c"));
|
|
|
|
Assert.Null(DiagnosticErrorGroupLink.TryDecode(encoded));
|
|
}
|
|
}
|