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,110 @@
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));
}
}