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

Information
Class: AsyncResponse.Transports.RabbitMQ.RabbitMqReplyTargetProvider
Assembly: AsyncResponse.Transports.RabbitMQ
File(s): /_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqReplyTargetProvider.cs
Line coverage
100%
Covered lines: 57
Uncovered lines: 0
Coverable lines: 57
Total lines: 90
Line coverage: 100%
Branch coverage
89%
Covered branches: 25
Total branches: 28
Branch coverage: 89.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetReplyTarget(...)85%2020100%
ResolveTarget(...)100%88100%

File(s)

/_/src/Transports/AsyncResponse.Transports.RabbitMQ/RabbitMqReplyTargetProvider.cs

#LineLine coverage
 1using Microsoft.Extensions.Options;
 2
 3namespace AsyncResponse.Transports.RabbitMQ;
 4
 2115internal sealed class RabbitMqReplyTargetProvider(
 2116    IOptions<RabbitMqAsyncResponseOptions> _options) : IAsyncResponseReplyTargetProvider
 7{
 8    /// <summary>Gets the configured reply target.</summary>
 9    public AsyncResponseReplyTarget GetReplyTarget(string? name = null)
 10    {
 1611        var options = _options.Value;
 12        // Options-validator parity with the other transports' providers: a hand-off address must
 13        // come from a configuration that passes the transport's own checks.
 1614        RabbitMqOptionsValidator.ValidateConnection(options);
 1615        var targetName = string.IsNullOrWhiteSpace(name)
 1616            ? options.DefaultReplyTargetName
 1617            : name;
 18
 1619        var target = ResolveTarget(options, targetName);
 1220        var exchange = RabbitMqOptionsValidator.Required(
 1221            target.Exchange,
 1222            $"{nameof(RabbitMqReplyTargetOptions)}.{nameof(RabbitMqReplyTargetOptions.Exchange)}");
 1223        var routingKey = RabbitMqOptionsValidator.Required(
 1224            target.RoutingKey,
 1225            $"{nameof(RabbitMqReplyTargetOptions)}.{nameof(RabbitMqReplyTargetOptions.RoutingKey)}");
 1226        var queue = target.Queue ?? options.ResponseQueue;
 27
 28        // A NAMED target must not route into the worker or dead-letter side (DB-transport parity):
 29        // the worker publish pair delivers its responses as worker jobs, the dead-letter exchange
 30        // mixes them into buried traffic, and a declared queue equal to the worker/dead-letter
 31        // queue does the same one hop later — while the waiter times out.
 1232        var routesToWorker = StringComparer.Ordinal.Equals(exchange, options.WorkerExchange)
 1233            && StringComparer.Ordinal.Equals(routingKey, options.WorkerRoutingKey);
 1234        var routesToDeadLetter = !string.IsNullOrWhiteSpace(options.DeadLetterExchange)
 1235            && StringComparer.Ordinal.Equals(exchange, options.DeadLetterExchange);
 1236        var queueCollides = !string.IsNullOrWhiteSpace(queue)
 1237            && (StringComparer.Ordinal.Equals(queue, options.WorkerQueue)
 1238                || (!string.IsNullOrWhiteSpace(options.DeadLetterQueue) && StringComparer.Ordinal.Equals(queue, options.
 1239        if (routesToWorker || routesToDeadLetter || queueCollides)
 40        {
 441            throw new InvalidOperationException(
 442                $"RabbitMQ async-response reply target '{targetName}' routes to '{exchange}:{routingKey}'" +
 443                $"{(string.IsNullOrWhiteSpace(queue) ? "" : $" (queue '{queue}')")}, which collides with the worker or d
 444                "its responses would be consumed as worker jobs (or mixed into dead letters).");
 45        }
 46
 847        var properties = new Dictionary<string, string>(target.Properties, StringComparer.Ordinal)
 848        {
 849            ["exchange"] = exchange,
 850            ["routingKey"] = routingKey
 851        };
 52
 853        if (!string.IsNullOrWhiteSpace(queue))
 654            properties["queue"] = queue;
 55
 856        return new AsyncResponseReplyTarget
 857        {
 858            Name = targetName,
 859            Transport = RabbitMqAsyncResponseOptions.TransportName,
 860            Address = $"{exchange}:{routingKey}",
 861            Properties = properties
 862        };
 63    }
 64
 65    private static RabbitMqReplyTargetOptions ResolveTarget(
 66        RabbitMqAsyncResponseOptions options,
 67        string targetName)
 68    {
 1669        if (options.ReplyTargets.TryGetValue(targetName, out var configured))
 1070            return configured;
 71
 672        if (StringComparer.Ordinal.Equals(targetName, options.DefaultReplyTargetName)
 673            && !string.IsNullOrWhiteSpace(options.ResponseExchange)
 674            && !string.IsNullOrWhiteSpace(options.ResponseRoutingKey))
 75        {
 276            return new RabbitMqReplyTargetOptions
 277            {
 278                Exchange = options.ResponseExchange,
 279                RoutingKey = options.ResponseRoutingKey,
 280                Queue = options.ResponseQueue
 281            };
 82        }
 83
 484        throw new InvalidOperationException(
 485            $"RabbitMQ async-response reply target '{targetName}' is not configured. " +
 486            $"Configure {nameof(RabbitMqAsyncResponseOptions.ResponseExchange)} and " +
 487            $"{nameof(RabbitMqAsyncResponseOptions.ResponseRoutingKey)} for the default target " +
 488            $"or add a named target with {nameof(RabbitMqAsyncResponseOptions.AddReplyTarget)}.");
 89    }
 90}