141 lines
4.9 KiB
C#
141 lines
4.9 KiB
C#
using Omika.Core.Observability.Identity;
|
|
|
|
namespace Omika.Diagnostics.Tests;
|
|
|
|
/// <summary>
|
|
/// Tests unitaires de l'ActorKey (§19) : HMAC-SHA256 serveur, stable par
|
|
/// (tenant, collaborateur), non réversible, fail-open. Aucune clé de production
|
|
/// utilisée ici (clés de test déterministes uniquement).
|
|
/// </summary>
|
|
public class DiagnosticActorKeyTests
|
|
{
|
|
private static string KeyA() => Convert.ToBase64String(new byte[32]); // 32 octets 0x00
|
|
|
|
private static string KeyB() => Convert.ToBase64String(Enumerable.Repeat((byte)7, 32).ToArray());
|
|
|
|
[Fact]
|
|
public void SameTenant_SameCollaborateur_ProducesSameKey()
|
|
{
|
|
var provider = new DiagnosticActorKeyProvider(KeyA());
|
|
|
|
DiagnosticActorKey a = provider.Compute("BATIK.01", "42");
|
|
DiagnosticActorKey b = provider.Compute("BATIK.01", "42");
|
|
|
|
Assert.True(a.HasValue);
|
|
Assert.Equal(a.Value, b.Value);
|
|
Assert.Equal(a.Version, b.Version);
|
|
}
|
|
|
|
[Fact]
|
|
public void DifferentTenant_SameCollaborateur_ProducesDifferentKey()
|
|
{
|
|
var provider = new DiagnosticActorKeyProvider(KeyA());
|
|
|
|
DiagnosticActorKey a = provider.Compute("BATIK.01", "42");
|
|
DiagnosticActorKey b = provider.Compute("BATIK.02", "42");
|
|
|
|
Assert.NotEqual(a.Value, b.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void SameTenant_DifferentCollaborateur_ProducesDifferentKey()
|
|
{
|
|
var provider = new DiagnosticActorKeyProvider(KeyA());
|
|
|
|
DiagnosticActorKey a = provider.Compute("BATIK.01", "42");
|
|
DiagnosticActorKey b = provider.Compute("BATIK.01", "43");
|
|
|
|
Assert.NotEqual(a.Value, b.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void DifferentKey_ProducesDifferentActorKey()
|
|
{
|
|
DiagnosticActorKey a = new DiagnosticActorKeyProvider(KeyA()).Compute("BATIK.01", "42");
|
|
DiagnosticActorKey b = new DiagnosticActorKeyProvider(KeyB()).Compute("BATIK.01", "42");
|
|
|
|
Assert.True(a.HasValue);
|
|
Assert.True(b.HasValue);
|
|
Assert.NotEqual(a.Value, b.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActorKey_IsHexOnly_AndContainsNoNominativeData()
|
|
{
|
|
DiagnosticActorKey key = new DiagnosticActorKeyProvider(KeyA()).Compute("BATIK.01", "42");
|
|
|
|
Assert.NotNull(key.Value);
|
|
Assert.Equal(64, key.Value!.Length);
|
|
Assert.Matches("^[0-9a-f]{64}$", key.Value);
|
|
Assert.DoesNotContain("42", key.Value, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("BATIK", key.Value, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain("collaborateur", key.Value, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActorKey_HasStableFormat_Versioned()
|
|
{
|
|
DiagnosticActorKey key = new DiagnosticActorKeyProvider(KeyA()).Compute("BATIK.01", "7");
|
|
|
|
Assert.Equal(1, key.Version);
|
|
Assert.NotNull(key.Value);
|
|
Assert.Equal(64, key.Value!.Length);
|
|
Assert.Equal(key.Value.ToLowerInvariant(), key.Value); // hex minuscule
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingKey_IsFailOpen_ReturnsEmpty()
|
|
{
|
|
var cases = new[]
|
|
{
|
|
new DiagnosticActorKeyProvider(null),
|
|
new DiagnosticActorKeyProvider(string.Empty),
|
|
new DiagnosticActorKeyProvider(" "),
|
|
new DiagnosticActorKeyProvider("not-base64!!!"),
|
|
new DiagnosticActorKeyProvider(Convert.ToBase64String(new byte[16])) // 128 bits < 256
|
|
};
|
|
|
|
foreach (DiagnosticActorKeyProvider provider in cases)
|
|
{
|
|
DiagnosticActorKey key = provider.Compute("BATIK.01", "42");
|
|
Assert.False(key.HasValue);
|
|
Assert.Null(key.Value);
|
|
Assert.Null(key.Version);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingIdentity_IsFailOpen_ReturnsEmpty()
|
|
{
|
|
var provider = new DiagnosticActorKeyProvider(KeyA());
|
|
|
|
Assert.False(provider.Compute("", "42").HasValue);
|
|
Assert.False(provider.Compute(" ", "42").HasValue);
|
|
Assert.False(provider.Compute("BATIK.01", null).HasValue);
|
|
Assert.False(provider.Compute("BATIK.01", "").HasValue);
|
|
Assert.False(provider.Compute("BATIK.01", " ").HasValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void CanonicalString_IsVersionedAndDeterministic()
|
|
{
|
|
string a = DiagnosticActorKeyProvider.BuildCanonicalString(1, "BATIK.01", "42");
|
|
string b = DiagnosticActorKeyProvider.BuildCanonicalString(1, "BATIK.01", "42");
|
|
|
|
Assert.Equal(a, b);
|
|
Assert.Equal("v1|tenant:BATIK.01|collaborateur:42", a);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActorKey_DoesNotRevealIdentity_WhenComparedAcrossTenants()
|
|
{
|
|
// Deux collaborateurs différents ne peuvent pas être confondus, et la clé
|
|
// seule ne permet pas de retrouver le CollaborateurId (§2 : non réversible).
|
|
var provider = new DiagnosticActorKeyProvider(KeyA());
|
|
DiagnosticActorKey key = provider.Compute("BATIK.01", "12345678");
|
|
|
|
Assert.NotNull(key.Value);
|
|
Assert.DoesNotContain("12345678", key.Value, StringComparison.Ordinal);
|
|
}
|
|
}
|