< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.Transports.SQS.SqsOptionsValidator
Assembly: AsyncResponse.Transports.SQS
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.SQS/SqsOptionsValidator.cs
Line coverage
100%
Covered lines: 84
Uncovered lines: 0
Coverable lines: 84
Total lines: 159
Line coverage: 100%
Branch coverage
100%
Covered branches: 64
Total branches: 64
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
ValidateCommon(...)100%2828100%
ValidateSubscriber(...)100%3232100%
Required(...)100%22100%
Positive(...)100%22100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.SQS/SqsOptionsValidator.cs

#LineLine coverage
 1namespace AsyncResponse.Transports.SQS;
 2
 3internal static class SqsOptionsValidator
 4{
 35    private static readonly TimeSpan MaxReceiveWaitTime = TimeSpan.FromSeconds(20);
 36    private static readonly TimeSpan MaxVisibilityTimeout = TimeSpan.FromHours(12);
 7
 8    public static void ValidateCommon(SqsAsyncResponseOptions options)
 9    {
 310        Required(options.WorkerQueue, nameof(options.WorkerQueue));
 311        Required(options.ResponseQueue, nameof(options.ResponseQueue));
 312        Required(options.CorrelationIdAttribute, nameof(options.CorrelationIdAttribute));
 313        Required(options.DefaultReplyTargetName, nameof(options.DefaultReplyTargetName));
 14
 315        if (StringComparer.Ordinal.Equals(options.WorkerQueue, options.ResponseQueue))
 16        {
 217            throw new InvalidOperationException(
 218                $"{nameof(SqsAsyncResponseOptions)}.{nameof(options.WorkerQueue)} and " +
 219                $"{nameof(options.ResponseQueue)} must be distinct so worker and response subscribers do not consume eac
 20        }
 21
 322        if (options.MaxMessagesPerReceive is < 1 or > 10)
 23        {
 224            throw new InvalidOperationException(
 225                $"{nameof(SqsAsyncResponseOptions)}.{nameof(options.MaxMessagesPerReceive)} must be between 1 and 10 (th
 26        }
 27
 328        if (options.ReceiveWaitTime < TimeSpan.Zero || options.ReceiveWaitTime > MaxReceiveWaitTime)
 29        {
 230            throw new InvalidOperationException(
 231                $"{nameof(SqsAsyncResponseOptions)}.{nameof(options.ReceiveWaitTime)} must be between 0 and 20 seconds (
 32        }
 33
 334        if (options.PublishMaxAttempts <= 0)
 235            throw new InvalidOperationException($"{nameof(SqsAsyncResponseOptions)}.{nameof(options.PublishMaxAttempts)}
 36
 337        if (options.CreateQueues)
 38        {
 339            Required(options.DeadLetterQueueSuffix, nameof(options.DeadLetterQueueSuffix));
 340            if (options.MaxReceiveCount is < 1 or > 1000)
 41            {
 242                throw new InvalidOperationException(
 243                    $"{nameof(SqsAsyncResponseOptions)}.{nameof(options.MaxReceiveCount)} must be between 1 and 1000 (th
 44            }
 45        }
 46
 347        if (SqsQueueAddress.IsFifo(options.WorkerQueue))
 248            Required(options.FifoMessageGroupIdFallback, nameof(options.FifoMessageGroupIdFallback));
 49
 350        Positive(options.PublishRetryBaseDelay, nameof(options.PublishRetryBaseDelay));
 351        Positive(options.PublishRetryMaxDelay, nameof(options.PublishRetryMaxDelay));
 352        Positive(options.SubscriberRetryBaseDelay, nameof(options.SubscriberRetryBaseDelay));
 353        Positive(options.SubscriberRetryMaxDelay, nameof(options.SubscriberRetryMaxDelay));
 54
 355        if (options.PublishRetryBaseDelay > options.PublishRetryMaxDelay)
 256            throw new InvalidOperationException($"{nameof(SqsAsyncResponseOptions)}.{nameof(options.PublishRetryBaseDela
 357        if (options.SubscriberRetryBaseDelay > options.SubscriberRetryMaxDelay)
 258            throw new InvalidOperationException($"{nameof(SqsAsyncResponseOptions)}.{nameof(options.SubscriberRetryBaseD
 359    }
 60
 61    public static void ValidateSubscriber(
 62        SqsAsyncResponseOptions transportOptions,
 63        SqsSubscriberOptions subscriberOptions,
 64        SqsSubscriberRole role)
 65    {
 366        var optionPath = role is SqsSubscriberRole.Worker
 367            ? $"{nameof(SqsAsyncResponseOptions)}.{nameof(SqsAsyncResponseOptions.WorkerSubscriber)}"
 368            : $"{nameof(SqsAsyncResponseOptions)}.{nameof(SqsAsyncResponseOptions.ResponseSubscriber)}";
 69
 370        if (subscriberOptions.VisibilityTimeout is { } visibilityTimeout
 371            && (visibilityTimeout <= TimeSpan.Zero || visibilityTimeout > MaxVisibilityTimeout))
 72        {
 273            throw new InvalidOperationException(
 274                $"{optionPath}.{nameof(SqsSubscriberOptions.VisibilityTimeout)} must be positive and at most 12 hours (t
 75        }
 76
 377        if (subscriberOptions.RedeliveryDelay is { } redeliveryDelay
 378            && (redeliveryDelay < TimeSpan.Zero || redeliveryDelay > MaxVisibilityTimeout))
 79        {
 280            throw new InvalidOperationException(
 281                $"{optionPath}.{nameof(SqsSubscriberOptions.RedeliveryDelay)} must be between zero and 12 hours (the SQS
 82        }
 83
 384        if (subscriberOptions.VisibilityRenewalInterval is { } renewalInterval)
 85        {
 286            if (renewalInterval <= TimeSpan.Zero)
 87            {
 288                throw new InvalidOperationException(
 289                    $"{optionPath}.{nameof(SqsSubscriberOptions.VisibilityRenewalInterval)} must be positive when set.")
 90            }
 91
 292            if (subscriberOptions.VisibilityTimeout is not { } renewedVisibility)
 93            {
 294                throw new InvalidOperationException(
 295                    $"{optionPath}.{nameof(SqsSubscriberOptions.VisibilityRenewalInterval)} requires " +
 296                    $"{nameof(SqsSubscriberOptions.VisibilityTimeout)} so the heartbeat knows how far to extend each mes
 97            }
 98
 299            if (renewalInterval >= renewedVisibility)
 100            {
 2101                throw new InvalidOperationException(
 2102                    $"{optionPath}.{nameof(SqsSubscriberOptions.VisibilityRenewalInterval)} must be shorter than " +
 2103                    $"{nameof(SqsSubscriberOptions.VisibilityTimeout)}, or messages become visible between heartbeats.")
 104            }
 105        }
 106
 3107        switch (subscriberOptions.AckMode)
 108        {
 109            case SqsAckMode.AckAfterHandlerCompletes:
 3110                return;
 111
 112            case SqsAckMode.AckAfterEnqueue:
 3113                if (subscriberOptions.BackgroundWorkerCount <= 0)
 114                {
 2115                    throw new InvalidOperationException(
 2116                        $"{optionPath}.{nameof(SqsSubscriberOptions.BackgroundWorkerCount)} must be explicitly configure
 2117                        $"when {nameof(SqsSubscriberOptions.AckMode)} is {nameof(SqsAckMode.AckAfterEnqueue)}.");
 118                }
 119
 3120                if (subscriberOptions.BackgroundQueueCapacity <= 0)
 121                {
 2122                    throw new InvalidOperationException(
 2123                        $"{optionPath}.{nameof(SqsSubscriberOptions.BackgroundQueueCapacity)} must be explicitly configu
 2124                        $"when {nameof(SqsSubscriberOptions.AckMode)} is {nameof(SqsAckMode.AckAfterEnqueue)}.");
 125                }
 126
 3127                if (subscriberOptions.BackgroundDrainTimeout <= TimeSpan.Zero)
 128                {
 2129                    throw new InvalidOperationException(
 2130                        $"{optionPath}.{nameof(SqsSubscriberOptions.BackgroundDrainTimeout)} must be positive.");
 131                }
 132
 133                // SQS subscribers spend only the background drain at shutdown; the receive loop
 134                // stops with the host token and the SDK client needs no bounded close.
 3135                ShutdownBudgetValidator.Validate(
 3136                    "SQS",
 3137                    $"{nameof(SqsAsyncResponseOptions)}.{nameof(SqsAsyncResponseOptions.HostShutdownTimeout)}",
 3138                    transportOptions.HostShutdownTimeout,
 3139                    ($"{optionPath}.{nameof(SqsSubscriberOptions.BackgroundDrainTimeout)}", subscriberOptions.Background
 140
 3141                return;
 142
 143            default:
 2144                throw new InvalidOperationException(
 2145                    $"{optionPath}.{nameof(SqsSubscriberOptions.AckMode)} has unsupported value '{subscriberOptions.AckM
 146        }
 147    }
 148
 149    public static string Required(string? value, string name)
 3150        => !string.IsNullOrWhiteSpace(value)
 3151            ? value
 3152            : throw new InvalidOperationException($"{nameof(SqsAsyncResponseOptions)}.{name} must be configured.");
 153
 154    private static void Positive(TimeSpan value, string name)
 155    {
 3156        if (value <= TimeSpan.Zero)
 2157            throw new InvalidOperationException($"{nameof(SqsAsyncResponseOptions)}.{name} must be positive.");
 3158    }
 159}