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); } }