using Grpc.Core;
namespace MxGateway.Client.Tests;
/// Tests for the shared gRPC-to-native exception mapping used by the transports.
public sealed class RpcExceptionMapperTests
{
/// Verifies that an unauthenticated status maps to the authentication exception.
[Fact]
public void Map_UnauthenticatedStatus_ProducesAuthenticationException()
{
RpcException rpc = new(new Status(StatusCode.Unauthenticated, "no key"));
Exception mapped = RpcExceptionMapper.Map(rpc, CancellationToken.None);
MxGatewayAuthenticationException authentication =
Assert.IsType(mapped);
Assert.Equal(StatusCode.Unauthenticated, authentication.StatusCode);
}
/// Verifies that a permission-denied status maps to the authorization exception.
[Fact]
public void Map_PermissionDeniedStatus_ProducesAuthorizationException()
{
RpcException rpc = new(new Status(StatusCode.PermissionDenied, "missing scope"));
Exception mapped = RpcExceptionMapper.Map(rpc, CancellationToken.None);
MxGatewayAuthorizationException authorization =
Assert.IsType(mapped);
Assert.Equal(StatusCode.PermissionDenied, authorization.StatusCode);
}
/// Verifies that a cancelled status maps to OperationCanceledException.
[Fact]
public void Map_CancelledStatus_ProducesOperationCanceledException()
{
RpcException rpc = new(new Status(StatusCode.Cancelled, "cancelled"));
Exception mapped = RpcExceptionMapper.Map(rpc, CancellationToken.None);
Assert.IsType(mapped);
}
///
/// Verifies that non-auth statuses surface the originating gRPC status code on the
/// mapped exception so callers can distinguish transient from permanent failures
/// without reflecting into InnerException.
///
[Theory]
[InlineData(StatusCode.NotFound)]
[InlineData(StatusCode.InvalidArgument)]
[InlineData(StatusCode.ResourceExhausted)]
[InlineData(StatusCode.FailedPrecondition)]
[InlineData(StatusCode.Unavailable)]
[InlineData(StatusCode.Internal)]
public void Map_NonAuthStatus_CarriesStatusCodeOnMxGatewayException(StatusCode statusCode)
{
RpcException rpc = new(new Status(statusCode, "boom"));
Exception mapped = RpcExceptionMapper.Map(rpc, CancellationToken.None);
MxGatewayException gatewayException = Assert.IsType(mapped);
Assert.Equal(statusCode, gatewayException.StatusCode);
Assert.Same(rpc, gatewayException.InnerException);
}
/// Verifies that an MxGatewayException built without a gRPC status reports a null StatusCode.
[Fact]
public void StatusCode_IsNull_WhenNoGrpcStatusProvided()
{
MxGatewayException gatewayException = new("plain failure");
Assert.Null(gatewayException.StatusCode);
}
}