Dreamine.FullKit.Tests 1.0.0.0
Dreamine.FullKit.Tests 기능을 검증하는 자동화 테스트 프로젝트입니다.
로딩중...
검색중...
일치하는것 없음
RabbitMqMessageBusTests.cs
이 파일의 문서화 페이지로 가기
1using System.Text.Json;
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;
7
9
18public sealed class RabbitMqMessageBusTests
19{
36 [Fact]
38 {
39 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions
40 {
41 ExchangeName = "dreamine.exchange",
42 QueueName = "dreamine.queue",
43 RoutingKey = "dreamine.route",
44 ExchangeType = "topic",
45 Durable = true,
46 AutoDelete = true,
47 Exclusive = true
48 });
49
50 await fixture.Bus.ConnectAsync();
51
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]);
56 }
57
74 [Fact]
76 {
77 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions
78 {
79 ExchangeName = "dreamine.exchange",
80 RoutingKey = "fallback.route",
81 PersistentMessages = true
82 });
83
84 await fixture.Bus.ConnectAsync();
85
86 var message = new MessageEnvelope
87 {
88 Name = "Rabbit.Publish",
89 Route = "message.route",
90 Payload = [1, 2, 3],
91 Headers = new Dictionary<string, string> { ["Protocol"] = "RabbitMQ" }
92 };
93
94 await fixture.Bus.PublishAsync(message);
95
96 Assert.Single(fixture.Channel.PublishedMessages);
97
98 var published = fixture.Channel.PublishedMessages[0];
99 var roundTripped = JsonSerializer.Deserialize<MessageEnvelope>(published.Body.Span);
100
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);
110 }
111
128 [Fact]
130 {
131 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions
132 {
133 RoutingKey = "fallback.route"
134 });
135
136 await fixture.Bus.ConnectAsync();
137
138 await fixture.Bus.PublishAsync(new MessageEnvelope { Name = "Rabbit.Publish" });
139
140 Assert.Equal("fallback.route", fixture.Channel.PublishedMessages[0].RoutingKey);
141 }
142
159 [Fact]
161 {
162 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions
163 {
164 ExchangeName = "dreamine.exchange",
165 QueueName = "dreamine.queue"
166 });
167
168 await fixture.Bus.ConnectAsync();
169
170 MessageEnvelope? handled = null;
171 await fixture.Bus.SubscribeAsync(
172 "custom.route",
173 (message, _) =>
174 {
175 handled = message;
176 return Task.CompletedTask;
177 });
178
179 var delivery = new RabbitMqDelivery(
180 7,
181 "custom.route",
182 JsonSerializer.SerializeToUtf8Bytes(new MessageEnvelope
183 {
184 Name = "Rabbit.Receive",
185 Route = "custom.route",
186 Payload = [9, 8, 7]
187 }));
188
189 await fixture.Channel.DeliverAsync(delivery);
190
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);
197 }
198
215 [Fact]
217 {
218 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions
219 {
220 ExchangeName = "dreamine.exchange",
221 QueueName = "dreamine.queue"
222 });
223 var calls = new List<string>();
224
225 await fixture.Bus.ConnectAsync();
226 await fixture.Bus.SubscribeAsync(
227 "custom.route",
228 (_, _) =>
229 {
230 calls.Add("first");
231 return Task.CompletedTask;
232 });
233 await fixture.Bus.SubscribeAsync(
234 "custom.route",
235 (_, _) =>
236 {
237 calls.Add("second");
238 return Task.CompletedTask;
239 });
240
241 var delivery = new RabbitMqDelivery(
242 13,
243 "custom.route",
244 JsonSerializer.SerializeToUtf8Bytes(new MessageEnvelope
245 {
246 Name = "Rabbit.Receive",
247 Route = "custom.route"
248 }));
249
250 await fixture.Channel.DeliverAsync(delivery);
251
252 Assert.Equal(["first", "second"], calls);
253 Assert.Equal([13UL], fixture.Channel.AckedDeliveryTags);
254 Assert.Empty(fixture.Channel.NackedDeliveryTags);
255 }
256
273 [Fact]
275 {
276 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions
277 {
278 RequeueOnHandlerError = true
279 });
280
281 await fixture.Bus.ConnectAsync();
282 await fixture.Bus.SubscribeAsync(
283 "failing.route",
284 (_, _) => throw new InvalidOperationException("handler failed"));
285
286 var delivery = new RabbitMqDelivery(
287 11,
288 "failing.route",
289 JsonSerializer.SerializeToUtf8Bytes(new MessageEnvelope
290 {
291 Name = "Rabbit.Receive",
292 Route = "failing.route"
293 }));
294
295 await Assert.ThrowsAsync<InvalidOperationException>(() => fixture.Channel.DeliverAsync(delivery));
296
297 Assert.Equal([11UL], fixture.Channel.NackedDeliveryTags);
298 Assert.True(fixture.Channel.LastNackRequeue);
299 Assert.Empty(fixture.Channel.AckedDeliveryTags);
300 }
301
318 [Fact]
320 {
321 var fixture = new RabbitMqFixture(new RabbitMqMessageBusOptions());
322
323 await fixture.Bus.ConnectAsync();
324 await fixture.Bus.SubscribeAsync("route", (_, _) => Task.CompletedTask);
325 await fixture.Bus.DisconnectAsync();
326
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);
331 }
332
341 private sealed class RabbitMqFixture
342 {
359 public RabbitMqFixture(RabbitMqMessageBusOptions options)
360 {
363 Bus = new RabbitMqMessageBus(options, new FakeRabbitMqConnectionFactory(Connection));
364 }
365
374 public RabbitMqMessageBus Bus { get; }
375
385
395 }
396
405 private sealed class FakeRabbitMqConnectionFactory : IRabbitMqConnectionFactory
406 {
416
434 {
435 _connection = connection;
436 }
437
462 public IRabbitMqConnection CreateConnection(RabbitMqMessageBusOptions options)
463 {
464 return _connection;
465 }
466 }
467
476 private sealed class FakeRabbitMqConnection : IRabbitMqConnection
477 {
487
505 {
506 _channel = channel;
507 }
508
517 public bool IsOpen => !Closed;
518
527 public bool Closed { get; private set; }
528
545 public IRabbitMqChannel CreateChannel()
546 {
547 return _channel;
548 }
549
558 public void Close()
559 {
560 Closed = true;
561 }
562
571 public void Dispose()
572 {
573 Closed = true;
574 }
575 }
576
585 private sealed class FakeRabbitMqChannel : IRabbitMqChannel
586 {
595 private Func<RabbitMqDelivery, CancellationToken, Task>? _onReceived;
596
605 public bool IsOpen => !Closed;
606
615 public bool Closed { get; private set; }
616
625 public bool LastNackRequeue { get; private set; }
626
635 public List<string> Operations { get; } = [];
636
645 public List<PublishedMessage> PublishedMessages { get; } = [];
646
655 public List<ulong> AckedDeliveryTags { get; } = [];
656
665 public List<ulong> NackedDeliveryTags { get; } = [];
666
675 public List<ulong> RejectedDeliveryTags { get; } = [];
676
685 public List<string> CancelledConsumerTags { get; } = [];
686
727 public void ExchangeDeclare(
728 string exchange,
729 string type,
730 bool durable,
731 bool autoDelete)
732 {
733 Operations.Add($"exchange:{exchange}:{type}:{durable}:{autoDelete}");
734 }
735
776 public void QueueDeclare(
777 string queue,
778 bool durable,
779 bool exclusive,
780 bool autoDelete)
781 {
782 Operations.Add($"queue:{queue}:{durable}:{exclusive}:{autoDelete}");
783 }
784
817 public void QueueBind(
818 string queue,
819 string exchange,
820 string routingKey)
821 {
822 Operations.Add($"bind:{queue}:{exchange}:{routingKey}");
823 }
824
841 public IRabbitMqBasicProperties CreateBasicProperties()
842 {
843 return new FakeRabbitMqBasicProperties();
844 }
845
894 public void BasicPublish(
895 string exchange,
896 string routingKey,
897 bool mandatory,
898 IRabbitMqBasicProperties properties,
899 ReadOnlyMemory<byte> body)
900 {
902 exchange,
903 routingKey,
904 mandatory,
906 {
907 Persistent = properties.Persistent,
908 ContentType = properties.ContentType,
909 Type = properties.Type
910 },
911 body.ToArray()));
912 }
913
954 public string BasicConsume(
955 string queue,
956 bool autoAck,
957 Func<RabbitMqDelivery, CancellationToken, Task> onReceived)
958 {
959 Operations.Add($"consume:{queue}:{autoAck}");
960 _onReceived = onReceived;
961 return "consumer-1";
962 }
963
996 public Task DeliverAsync(RabbitMqDelivery delivery)
997 {
998 return _onReceived?.Invoke(delivery, CancellationToken.None)
999 ?? throw new InvalidOperationException("Consumer is not registered.");
1000 }
1001
1026 public void BasicAck(ulong deliveryTag, bool multiple)
1027 {
1028 AckedDeliveryTags.Add(deliveryTag);
1029 }
1030
1063 public void BasicNack(ulong deliveryTag, bool multiple, bool requeue)
1064 {
1065 NackedDeliveryTags.Add(deliveryTag);
1066 LastNackRequeue = requeue;
1067 }
1068
1093 public void BasicReject(ulong deliveryTag, bool requeue)
1094 {
1095 RejectedDeliveryTags.Add(deliveryTag);
1096 }
1097
1114 public void BasicCancel(string consumerTag)
1115 {
1116 CancelledConsumerTags.Add(consumerTag);
1117 }
1118
1127 public void Close()
1128 {
1129 Closed = true;
1130 }
1131
1140 public void Dispose()
1141 {
1142 Closed = true;
1143 }
1144 }
1145
1154 private sealed class FakeRabbitMqBasicProperties : IRabbitMqBasicProperties
1155 {
1164 public bool Persistent { get; set; }
1165
1174 public string? ContentType { get; set; }
1175
1184 public string? Type { get; set; }
1185 }
1186
1195 private sealed record PublishedMessage(
1196 string Exchange,
1197 string RoutingKey,
1198 bool Mandatory,
1199 FakeRabbitMqBasicProperties Properties,
1200 ReadOnlyMemory<byte> Body);
1201}
record PublishedMessage(string Exchange, string RoutingKey, bool Mandatory, FakeRabbitMqBasicProperties Properties, ReadOnlyMemory< byte > Body)
void QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete)
void BasicPublish(string exchange, string routingKey, bool mandatory, IRabbitMqBasicProperties properties, ReadOnlyMemory< byte > body)
string BasicConsume(string queue, bool autoAck, Func< RabbitMqDelivery, CancellationToken, Task > onReceived)
void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete)