2using Dreamine.Communication.Abstractions.Enums;
3using Dreamine.Communication.Abstractions.Models;
4using Dreamine.Communication.RabbitMQ.Buses;
5using Dreamine.Communication.RabbitMQ.Infrastructure;
6using Dreamine.Communication.RabbitMQ.Options;
41 ExchangeName =
"dreamine.exchange",
42 QueueName =
"dreamine.queue",
43 RoutingKey =
"dreamine.route",
44 ExchangeType =
"topic",
50 await fixture.
Bus.ConnectAsync();
52 Assert.Equal(ConnectionState.Connected, fixture.Bus.State);
53 Assert.Equal(
"exchange:dreamine.exchange:topic:True:True", fixture.Channel.Operations[0]);
54 Assert.Equal(
"queue:dreamine.queue:True:True:True", fixture.Channel.Operations[1]);
55 Assert.Equal(
"bind:dreamine.queue:dreamine.exchange:dreamine.route", fixture.Channel.Operations[2]);
79 ExchangeName =
"dreamine.exchange",
80 RoutingKey =
"fallback.route",
81 PersistentMessages =
true
84 await fixture.
Bus.ConnectAsync();
86 var message =
new MessageEnvelope
88 Name =
"Rabbit.Publish",
89 Route =
"message.route",
91 Headers =
new Dictionary<string, string> { [
"Protocol"] =
"RabbitMQ" }
94 await fixture.Bus.PublishAsync(message);
96 Assert.Single(fixture.Channel.PublishedMessages);
98 var published = fixture.Channel.PublishedMessages[0];
99 var roundTripped = JsonSerializer.Deserialize<MessageEnvelope>(published.Body.Span);
101 Assert.Equal(
"dreamine.exchange", published.Exchange);
102 Assert.Equal(
"message.route", published.RoutingKey);
103 Assert.True(published.Properties.Persistent);
104 Assert.Equal(
"application/json", published.Properties.ContentType);
105 Assert.Equal(nameof(MessageEnvelope), published.Properties.Type);
106 Assert.NotNull(roundTripped);
107 Assert.Equal(message.Name, roundTripped.Name);
108 Assert.Equal(message.Route, roundTripped.Route);
109 Assert.Equal(message.Payload, roundTripped.Payload);
133 RoutingKey =
"fallback.route"
136 await fixture.
Bus.ConnectAsync();
138 await fixture.Bus.PublishAsync(
new MessageEnvelope { Name =
"Rabbit.Publish" });
140 Assert.Equal(
"fallback.route", fixture.Channel.PublishedMessages[0].RoutingKey);
164 ExchangeName =
"dreamine.exchange",
165 QueueName =
"dreamine.queue"
168 await fixture.
Bus.ConnectAsync();
170 MessageEnvelope? handled =
null;
171 await fixture.Bus.SubscribeAsync(
176 return Task.CompletedTask;
179 var delivery =
new RabbitMqDelivery(
182 JsonSerializer.SerializeToUtf8Bytes(
new MessageEnvelope
184 Name =
"Rabbit.Receive",
185 Route =
"custom.route",
189 await fixture.Channel.DeliverAsync(delivery);
191 Assert.NotNull(handled);
192 Assert.Equal(
"Rabbit.Receive", handled.Name);
193 Assert.Contains(
"bind:dreamine.queue:dreamine.exchange:custom.route", fixture.Channel.Operations);
194 Assert.Equal(
"consume:dreamine.queue:False", fixture.Channel.Operations.Last(x => x.StartsWith(
"consume:", StringComparison.Ordinal)));
195 Assert.Equal([7UL], fixture.Channel.AckedDeliveryTags);
196 Assert.Empty(fixture.Channel.NackedDeliveryTags);
220 ExchangeName =
"dreamine.exchange",
221 QueueName =
"dreamine.queue"
223 var calls =
new List<string>();
225 await fixture.Bus.ConnectAsync();
226 await fixture.Bus.SubscribeAsync(
231 return Task.CompletedTask;
233 await fixture.Bus.SubscribeAsync(
238 return Task.CompletedTask;
241 var delivery =
new RabbitMqDelivery(
244 JsonSerializer.SerializeToUtf8Bytes(
new MessageEnvelope
246 Name =
"Rabbit.Receive",
247 Route =
"custom.route"
250 await fixture.Channel.DeliverAsync(delivery);
252 Assert.Equal([
"first",
"second"], calls);
253 Assert.Equal([13UL], fixture.Channel.AckedDeliveryTags);
254 Assert.Empty(fixture.Channel.NackedDeliveryTags);
278 RequeueOnHandlerError =
true
281 await fixture.
Bus.ConnectAsync();
282 await fixture.Bus.SubscribeAsync(
284 (_, _) =>
throw new InvalidOperationException(
"handler failed"));
286 var delivery =
new RabbitMqDelivery(
289 JsonSerializer.SerializeToUtf8Bytes(
new MessageEnvelope
291 Name =
"Rabbit.Receive",
292 Route =
"failing.route"
295 await Assert.ThrowsAsync<InvalidOperationException>(() => fixture.Channel.DeliverAsync(delivery));
297 Assert.Equal([11UL], fixture.Channel.NackedDeliveryTags);
298 Assert.True(fixture.Channel.LastNackRequeue);
299 Assert.Empty(fixture.Channel.AckedDeliveryTags);
323 await fixture.Bus.ConnectAsync();
324 await fixture.Bus.SubscribeAsync(
"route", (_, _) => Task.CompletedTask);
325 await fixture.Bus.DisconnectAsync();
327 Assert.Equal(ConnectionState.Disconnected, fixture.Bus.State);
328 Assert.Contains(
"consumer-1", fixture.Channel.CancelledConsumerTags);
329 Assert.True(fixture.Channel.Closed);
330 Assert.True(fixture.Connection.Closed);
374 public RabbitMqMessageBus
Bus {
get; }
595 private Func<RabbitMqDelivery, CancellationToken, Task>?
_onReceived;
733 Operations.Add($
"exchange:{exchange}:{type}:{durable}:{autoDelete}");
782 Operations.Add($
"queue:{queue}:{durable}:{exclusive}:{autoDelete}");
822 Operations.Add($
"bind:{queue}:{exchange}:{routingKey}");
898 IRabbitMqBasicProperties properties,
899 ReadOnlyMemory<byte> body)
907 Persistent = properties.Persistent,
908 ContentType = properties.ContentType,
909 Type = properties.Type
957 Func<RabbitMqDelivery, CancellationToken, Task> onReceived)
998 return _onReceived?.Invoke(delivery, CancellationToken.None)
999 ??
throw new InvalidOperationException(
"Consumer is not registered.");
1063 public void BasicNack(ulong deliveryTag,
bool multiple,
bool requeue)
1200 ReadOnlyMemory<byte> Body);
async Task SubscribeAsync_AllowsMultipleHandlersForSameRoute()
async Task PublishAsync_UsesConfiguredRoutingKeyWhenMessageRouteIsEmpty()
async Task DisconnectAsync_CancelsConsumerAndClosesResources()
async Task ConnectAsync_DeclaresConfiguredTopology()
async Task PublishAsync_UsesMessageRouteAndSerializesEnvelope()
record PublishedMessage(string Exchange, string RoutingKey, bool Mandatory, FakeRabbitMqBasicProperties Properties, ReadOnlyMemory< byte > Body)
async Task SubscribeAsync_NacksDeliveryWhenHandlerFails()
async Task SubscribeAsync_BindsRouteConsumesAndAcknowledgesHandledDelivery()
FakeRabbitMqConnection Connection
RabbitMqFixture(RabbitMqMessageBusOptions options)
FakeRabbitMqChannel Channel
FakeRabbitMqConnectionFactory(FakeRabbitMqConnection connection)
readonly FakeRabbitMqConnection _connection
IRabbitMqConnection CreateConnection(RabbitMqMessageBusOptions options)
FakeRabbitMqConnection(FakeRabbitMqChannel channel)
IRabbitMqChannel CreateChannel()
readonly FakeRabbitMqChannel _channel
void QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete)
void BasicAck(ulong deliveryTag, bool multiple)
List< ulong > AckedDeliveryTags
void BasicPublish(string exchange, string routingKey, bool mandatory, IRabbitMqBasicProperties properties, ReadOnlyMemory< byte > body)
void QueueBind(string queue, string exchange, string routingKey)
List< string > CancelledConsumerTags
Task DeliverAsync(RabbitMqDelivery delivery)
string BasicConsume(string queue, bool autoAck, Func< RabbitMqDelivery, CancellationToken, Task > onReceived)
List< string > Operations
void BasicNack(ulong deliveryTag, bool multiple, bool requeue)
void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete)
List< ulong > NackedDeliveryTags
List< PublishedMessage > PublishedMessages
void BasicReject(ulong deliveryTag, bool requeue)
IRabbitMqBasicProperties CreateBasicProperties()
List< ulong > RejectedDeliveryTags
void BasicCancel(string consumerTag)
Func< RabbitMqDelivery, CancellationToken, Task >? _onReceived