diff --git a/src/Transports/RabbitMQ/Wolverine.RabbitMQ.Tests/ConventionalRouting/when_using_handler_type_naming.cs b/src/Transports/RabbitMQ/Wolverine.RabbitMQ.Tests/ConventionalRouting/when_using_handler_type_naming.cs index 4c5ae3583..85cf36bae 100644 --- a/src/Transports/RabbitMQ/Wolverine.RabbitMQ.Tests/ConventionalRouting/when_using_handler_type_naming.cs +++ b/src/Transports/RabbitMQ/Wolverine.RabbitMQ.Tests/ConventionalRouting/when_using_handler_type_naming.cs @@ -21,9 +21,8 @@ public when_using_handler_type_naming() _host = WolverineHost.For(opts => { opts.UseRabbitMq() - .UseConventionalRouting(NamingSource.FromHandlerType) .AutoProvision() - .AutoPurgeOnStartup(); + .UseConventionalRouting(NamingSource.FromHandlerType); opts.Discovery.DisableConventionalDiscovery() .IncludeType(typeof(HandlerTypeNamingHandler)); @@ -69,6 +68,40 @@ public void listener_endpoint_should_be_active() .ShouldBeTrue($"Expected active listener at {expectedUri}"); } + [Fact] + public void exchange_should_be_named_after_message_type_not_handler_type() + { + var messageName = typeof(HandlerTypeNamingMessage).ToMessageTypeName(); + var handlerName = typeof(HandlerTypeNamingHandler).ToMessageTypeName(); + + var transport = _runtime.Options.RabbitMqTransport(); + + // The exchange should be named after the message type + transport.Exchanges.Any(e => e.Name == messageName).ShouldBeTrue( + $"Expected exchange named '{messageName}' for message type, but found exchanges: {string.Join(", ", transport.Exchanges.Select(e => e.Name))}"); + + // The exchange should NOT be named after the handler type + transport.Exchanges.Any(e => e.Name == handlerName).ShouldBeFalse( + $"Exchange should not be named '{handlerName}' (handler type). Exchanges should use the message type name."); + } + + [Fact] + public void queue_should_be_bound_to_message_type_exchange() + { + var queueName = typeof(HandlerTypeNamingHandler).ToMessageTypeName(); + var messageName = typeof(HandlerTypeNamingMessage).ToMessageTypeName(); + + var transport = _runtime.Options.RabbitMqTransport(); + var queue = transport.Queues[queueName]; + + queue.HasBindings.ShouldBeTrue( + $"Queue '{queueName}' should have bindings"); + + // The queue should be bound to the exchange named after the message type + queue.Bindings().Any(b => b.ExchangeName == messageName).ShouldBeTrue( + $"Queue '{queueName}' should be bound to exchange '{messageName}', but found bindings to: {string.Join(", ", queue.Bindings().Select(b => b.ExchangeName))}"); + } + public void Dispose() { _host.Dispose(); diff --git a/src/Wolverine/Transports/MessageRoutingConvention.cs b/src/Wolverine/Transports/MessageRoutingConvention.cs index a20ff26d9..128f74673 100644 --- a/src/Wolverine/Transports/MessageRoutingConvention.cs +++ b/src/Wolverine/Transports/MessageRoutingConvention.cs @@ -57,7 +57,7 @@ void IMessageRoutingConvention.DiscoverListeners(IWolverineRuntime runtime, IRea foreach (var handler in chain.Handlers) { var handlerType = handler.HandlerType; - var endpoint = maybeCreateListenerForMessageOrHandlerType(transport, handlerType, runtime); + var endpoint = maybeCreateListenerForMessageOrHandlerType(transport, handlerType, runtime, messageType); if (endpoint != null) { endpoint.StickyHandlers.Add(handlerType); @@ -118,7 +118,7 @@ void IMessageRoutingConvention.DiscoverListeners(IWolverineRuntime runtime, IRea return endpoint; } - private Endpoint? maybeCreateListenerForMessageOrHandlerType(TTransport transport, Type messageOrHandlerType, IWolverineRuntime runtime) + private Endpoint? maybeCreateListenerForMessageOrHandlerType(TTransport transport, Type messageOrHandlerType, IWolverineRuntime runtime, Type? originalMessageType = null) { // Can be null, so bail out if there's no queue var queueName = _queueNameForListener(messageOrHandlerType); @@ -139,8 +139,11 @@ void IMessageRoutingConvention.DiscoverListeners(IWolverineRuntime runtime, IRea _configureListener(configuration, context); configuration!.As().Apply(); - - ApplyListenerRoutingDefaults(corrected, transport, messageOrHandlerType); + + // When using FromHandlerType naming, the exchange should still be named + // after the message type so that senders (which always use message type) + // and listeners share the same exchange. See GH-2397. + ApplyListenerRoutingDefaults(corrected, transport, originalMessageType ?? messageOrHandlerType); return endpoint; }