using BuildingBlock.Identity.Contracts.Conventions; using Thalos.Service.Application.Secrets; using Thalos.Service.Application.Sessions; namespace Thalos.Service.Application.UnitTests; public class HmacIdentitySessionTokenCodecTests { [Fact] public void EncodeAndTryDecode_WhenTokenValid_RoundTripsDescriptor() { var codec = new HmacIdentitySessionTokenCodec(new FakeSecretMaterialProvider()); var descriptor = new IdentitySessionDescriptor( "user-9", "tenant-9", IdentityAuthProvider.AzureAd, DateTimeOffset.UtcNow.AddMinutes(5)); var token = codec.Encode(descriptor); var ok = codec.TryDecode(token, out var decoded); Assert.True(ok); Assert.Equal("user-9", decoded.SubjectId); Assert.Equal("tenant-9", decoded.TenantId); Assert.Equal(IdentityAuthProvider.AzureAd, decoded.Provider); } [Fact] public void TryDecode_WhenTokenTampered_ReturnsFalse() { var codec = new HmacIdentitySessionTokenCodec(new FakeSecretMaterialProvider()); var descriptor = new IdentitySessionDescriptor( "user-9", "tenant-9", IdentityAuthProvider.InternalJwt, DateTimeOffset.UtcNow.AddMinutes(5)); var token = codec.Encode(descriptor) + "tamper"; var ok = codec.TryDecode(token, out _); Assert.False(ok); } [Fact] public void Encode_WhenSigningSecretUnavailable_ThrowsExplicitRuntimeError() { var codec = new HmacIdentitySessionTokenCodec(new MissingSecretMaterialProvider()); var descriptor = new IdentitySessionDescriptor( "user-9", "tenant-9", IdentityAuthProvider.InternalJwt, DateTimeOffset.UtcNow.AddMinutes(5)); var error = Assert.Throws(() => codec.Encode(descriptor)); Assert.Contains("SessionSigning", error.Message, StringComparison.Ordinal); } private sealed class FakeSecretMaterialProvider : IIdentitySecretMaterialProvider { public bool TryGetSecret(string secretKey, out string secretValue) { secretValue = "unit-test-secret"; return true; } public string GetSecret(string secretKey) => "unit-test-secret"; } private sealed class MissingSecretMaterialProvider : IIdentitySecretMaterialProvider { public bool TryGetSecret(string secretKey, out string secretValue) { secretValue = string.Empty; return false; } public string GetSecret(string secretKey) { throw new InvalidOperationException( $"Identity secret '{secretKey}' is not configured for the current runtime."); } } }