Refactor: Rename OutgoingEmail to SendingEmail
This commit renames and refactors all instances of `OutgoingEmail` to `SendingEmail` across the codebase to improve terminology consistency and align with domain language. - Renamed classes, interfaces, and records (e.g., `OutgoingEmailPublisher` → `SendingEmailPublisher`, `OutgoingEmailEvent` → `SendingEmailEvent`). - Updated method signatures, parameters, and return types to use `SendingEmail`. - Adjusted dependency injection registrations to reflect the new naming. - Updated mappings in `EmailMappingProfile` to map `SendEmailCommand` to `SendingEmailEvent`. - Refactored `SendEmailCommand` and its handler to work with `SendingEmailEvent`. - Updated `EmailsController` to use `SendingEmailEvent` in the `SendEmail` action. - Refactored integration tests to test `SendingEmailPublisher` and updated test data accordingly. - Updated log messages, error handling, and comments to reflect the new terminology. - Revised documentation and utility methods to use `SendingEmailEvent`. This refactor ensures consistency, improves readability, and reduces ambiguity in the codebase.
This commit is contained in:
@@ -330,7 +330,7 @@ return Accepted(); // HTTP 202 - command queued for processing
|
|||||||
arguments: null);
|
arguments: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task EnqueueAsync(OutgoingEmail email, CancellationToken cancellationToken)
|
public async Task EnqueueAsync(SendingEmail email, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var json = JsonSerializer.Serialize(email);
|
var json = JsonSerializer.Serialize(email);
|
||||||
var body = Encoding.UTF8.GetBytes(json);
|
var body = Encoding.UTF8.GetBytes(json);
|
||||||
@@ -347,7 +347,7 @@ return Accepted(); // HTTP 202 - command queued for processing
|
|||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OutgoingEmail?> DequeueAsync(CancellationToken cancellationToken)
|
public async Task<SendingEmail?> DequeueAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var result = _channel.BasicGet(QueueName, autoAck: false);
|
var result = _channel.BasicGet(QueueName, autoAck: false);
|
||||||
|
|
||||||
@@ -355,7 +355,7 @@ return Accepted(); // HTTP 202 - command queued for processing
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var json = Encoding.UTF8.GetString(result.Body.ToArray());
|
var json = Encoding.UTF8.GetString(result.Body.ToArray());
|
||||||
var email = JsonSerializer.Deserialize<OutgoingEmail>(json);
|
var email = JsonSerializer.Deserialize<SendingEmail>(json);
|
||||||
|
|
||||||
_channel.BasicAck(result.DeliveryTag, false);
|
_channel.BasicAck(result.DeliveryTag, false);
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ namespace DigitalData.MessagingService.Abstraction;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Email queue interface for outgoing emails.
|
/// Email queue interface for outgoing emails.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IOutgoingEmailPublisher
|
public interface ISendingEmailPublisher
|
||||||
{
|
{
|
||||||
Task EnqueueAsync(OutgoingEmailEvent outgoingEmailEvent, CancellationToken cancellationToken = default);
|
Task EnqueueAsync(SendingEmailEvent sendingEmailEvent, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task<int> GetQueueDepthAsync(CancellationToken cancellationToken = default);
|
Task<int> GetQueueDepthAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace DigitalData.MessagingService.Abstraction;
|
namespace DigitalData.MessagingService.Abstraction;
|
||||||
|
|
||||||
public record OutgoingEmailCreateDto
|
public record SendingEmailCreateDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recipient email address
|
/// Recipient email address
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace DigitalData.MessagingService.Abstraction;
|
namespace DigitalData.MessagingService.Abstraction;
|
||||||
|
|
||||||
public record OutgoingEmailEvent
|
public record SendingEmailEvent
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ public class EmailMappingProfile : Profile
|
|||||||
{
|
{
|
||||||
public EmailMappingProfile()
|
public EmailMappingProfile()
|
||||||
{
|
{
|
||||||
// SendEmailCommand -> OutgoingEmailEvent
|
// SendEmailCommand -> SendingEmailEvent
|
||||||
// Sender is resolved via MediatR in the handler and set separately after mapping.
|
// Sender is resolved via MediatR in the handler and set separately after mapping.
|
||||||
CreateMap<SendEmailCommand, OutgoingEmailEvent>()
|
CreateMap<SendEmailCommand, SendingEmailEvent>()
|
||||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid()))
|
.ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid()))
|
||||||
.ForMember(dest => dest.QueuedAt, opt => opt.MapFrom(_ => DateTime.Now))
|
.ForMember(dest => dest.QueuedAt, opt => opt.MapFrom(_ => DateTime.Now))
|
||||||
.ForMember(dest => dest.Sender, opt => opt.Ignore());
|
.ForMember(dest => dest.Sender, opt => opt.Ignore());
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace DigitalData.MessagingService.Application.EmailSending.Commands;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Command to send an email (enqueue to RabbitMQ)
|
/// Command to send an email (enqueue to RabbitMQ)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record SendEmailCommand : IRequest<OutgoingEmailEvent>
|
public record SendEmailCommand : IRequest<SendingEmailEvent>
|
||||||
{
|
{
|
||||||
public required GetSenderQuery Sender { get; init; }
|
public required GetSenderQuery Sender { get; init; }
|
||||||
|
|
||||||
@@ -36,23 +36,23 @@ public record SendEmailCommand : IRequest<OutgoingEmailEvent>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handler for SendEmailCommand
|
/// Handler for SendEmailCommand
|
||||||
/// Resolves the sender account via MediatR, maps to OutgoingEmailEvent and enqueues to RabbitMQ
|
/// Resolves the sender account via MediatR, maps to SendingEmailEvent and enqueues to RabbitMQ
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SendEmailCommandHandler(
|
public class SendEmailCommandHandler(
|
||||||
ISender Sender,
|
ISender Sender,
|
||||||
IOutgoingEmailPublisher Publisher,
|
ISendingEmailPublisher Publisher,
|
||||||
IMapper Mapper) : IRequestHandler<SendEmailCommand, OutgoingEmailEvent>
|
IMapper Mapper) : IRequestHandler<SendEmailCommand, SendingEmailEvent>
|
||||||
{
|
{
|
||||||
public async Task<OutgoingEmailEvent> Handle(SendEmailCommand request, CancellationToken cancellationToken)
|
public async Task<SendingEmailEvent> Handle(SendEmailCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var senderAccount = await Sender.Send(request.Sender, cancellationToken)
|
var senderAccount = await Sender.Send(request.Sender, cancellationToken)
|
||||||
?? throw new NotFoundException(
|
?? throw new NotFoundException(
|
||||||
$"No email account found for the given sender criteria (Id: {request.Sender.Id}, Username: {request.Sender.Username}).");
|
$"No email account found for the given sender criteria (Id: {request.Sender.Id}, Username: {request.Sender.Username}).");
|
||||||
|
|
||||||
var outgoingEmailEvent = Mapper.Map<OutgoingEmailEvent>(request) with { Sender = senderAccount };
|
var sendingEmailEvent = Mapper.Map<SendingEmailEvent>(request) with { Sender = senderAccount };
|
||||||
|
|
||||||
// Enqueue to RabbitMQ
|
// Enqueue to RabbitMQ
|
||||||
await Publisher.EnqueueAsync(outgoingEmailEvent, cancellationToken);
|
await Publisher.EnqueueAsync(sendingEmailEvent, cancellationToken);
|
||||||
return outgoingEmailEvent;
|
return sendingEmailEvent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public static class DependencyInjection
|
|||||||
services.AddSingleton<IEncryptionService, DataProtectionEncryptionService>();
|
services.AddSingleton<IEncryptionService, DataProtectionEncryptionService>();
|
||||||
|
|
||||||
// --- Email Queue (RabbitMQ) ---
|
// --- Email Queue (RabbitMQ) ---
|
||||||
services.AddSingleton<OutgoingEmailConsumer>();
|
services.AddSingleton<SendingEmailConsumer>();
|
||||||
services.AddMessagingServicePublisher();
|
services.AddMessagingServicePublisher();
|
||||||
|
|
||||||
// --- RabbitMQ Configuration ---
|
// --- RabbitMQ Configuration ---
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace DigitalData.MessagingService.Infrastructure.Queue;
|
|||||||
/// Provides message persistence, scalability, and reliability.
|
/// Provides message persistence, scalability, and reliability.
|
||||||
/// Uses Lazy<T> initialization pattern to avoid blocking constructor.
|
/// Uses Lazy<T> initialization pattern to avoid blocking constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class OutgoingEmailConsumer : IAsyncDisposable
|
public sealed class SendingEmailConsumer : IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly RabbitMqConfiguration _config;
|
private readonly RabbitMqConfiguration _config;
|
||||||
|
|
||||||
@@ -23,9 +23,9 @@ public sealed class OutgoingEmailConsumer : IAsyncDisposable
|
|||||||
|
|
||||||
private readonly Lazy<Task> _lazyInit;
|
private readonly Lazy<Task> _lazyInit;
|
||||||
|
|
||||||
private readonly ILogger<OutgoingEmailConsumer>? _logger;
|
private readonly ILogger<SendingEmailConsumer>? _logger;
|
||||||
|
|
||||||
public OutgoingEmailConsumer(IOptions<RabbitMqConfiguration> config, IEmailService EmailService, RabbitMqConnectionFactory CnnFactory, ILogger<OutgoingEmailConsumer>? logger = null)
|
public SendingEmailConsumer(IOptions<RabbitMqConfiguration> config, IEmailService EmailService, RabbitMqConnectionFactory CnnFactory, ILogger<SendingEmailConsumer>? logger = null)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_config = config.Value;
|
_config = config.Value;
|
||||||
@@ -38,11 +38,11 @@ public sealed class OutgoingEmailConsumer : IAsyncDisposable
|
|||||||
|
|
||||||
consumer.ReceivedAsync += async (sender, args) =>
|
consumer.ReceivedAsync += async (sender, args) =>
|
||||||
{
|
{
|
||||||
OutgoingEmailEvent? oMailEvent = null;
|
SendingEmailEvent? oMailEvent = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = Encoding.UTF8.GetString(args.Body.ToArray());
|
var json = Encoding.UTF8.GetString(args.Body.ToArray());
|
||||||
oMailEvent = JsonSerializer.Deserialize<OutgoingEmailEvent>(json);
|
oMailEvent = JsonSerializer.Deserialize<SendingEmailEvent>(json);
|
||||||
|
|
||||||
if (oMailEvent is not null)
|
if (oMailEvent is not null)
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,7 @@ public sealed class OutgoingEmailConsumer : IAsyncDisposable
|
|||||||
|
|
||||||
// TODO: Error Reporting Strategy
|
// TODO: Error Reporting Strategy
|
||||||
// Option 1: Separate RabbitMQ Queue (emailprofiler.errors)
|
// Option 1: Separate RabbitMQ Queue (emailprofiler.errors)
|
||||||
// - Create EmailErrorReport entity { OutgoingEmailEventId, Exception, StackTrace, Timestamp, RetryAttempt }
|
// - Create EmailErrorReport entity { SendingEmailEventId, Exception, StackTrace, Timestamp, RetryAttempt }
|
||||||
// - Publish to error queue: await _errorQueue.EnqueueAsync(errorReport)
|
// - Publish to error queue: await _errorQueue.EnqueueAsync(errorReport)
|
||||||
// - Separate worker processes error queue → Log to DB/File/External monitoring
|
// - Separate worker processes error queue → Log to DB/File/External monitoring
|
||||||
//
|
//
|
||||||
@@ -111,7 +111,7 @@ public sealed class OutgoingEmailConsumer : IAsyncDisposable
|
|||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
if (_lazyInit.IsValueCreated)
|
if (_lazyInit.IsValueCreated)
|
||||||
_logger?.LogWarning("OutgoingEmailConsumer already initialized. InitAsync() called multiple times.");
|
_logger?.LogWarning("SendingEmailConsumer already initialized. InitAsync() called multiple times.");
|
||||||
|
|
||||||
await _lazyInit.Value;
|
await _lazyInit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace DigitalData.MessagingService.Infrastructure.Services.Background;
|
|||||||
/// Leverages a push-based, event-driven RabbitMQ consumer to eliminate polling overhead.
|
/// Leverages a push-based, event-driven RabbitMQ consumer to eliminate polling overhead.
|
||||||
/// Email account configuration is resolved exclusively from application settings; no database access is performed.
|
/// Email account configuration is resolved exclusively from application settings; no database access is performed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AsyncInitWorker(OutgoingEmailConsumer EmailConsumer) : BackgroundService
|
public class AsyncInitWorker(SendingEmailConsumer EmailConsumer) : BackgroundService
|
||||||
{
|
{
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public static class DependencyInjection
|
|||||||
services.AddRabbitMqConnectionFactory(configure);
|
services.AddRabbitMqConnectionFactory(configure);
|
||||||
|
|
||||||
// --- Email Queue (RabbitMQ) ---
|
// --- Email Queue (RabbitMQ) ---
|
||||||
services.AddSingleton<IOutgoingEmailPublisher, OutgoingEmailPublisher>();
|
services.AddSingleton<ISendingEmailPublisher, SendingEmailPublisher>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ namespace DigitalData.MessagingService.Publisher;
|
|||||||
/// Provides message persistence, scalability, and reliability.
|
/// Provides message persistence, scalability, and reliability.
|
||||||
/// Uses Lazy<T> initialization pattern to avoid blocking constructor.
|
/// Uses Lazy<T> initialization pattern to avoid blocking constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class OutgoingEmailPublisher : IOutgoingEmailPublisher, IAsyncDisposable
|
public sealed class SendingEmailPublisher : ISendingEmailPublisher, IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly RabbitMqConfiguration _config;
|
private readonly RabbitMqConfiguration _config;
|
||||||
private readonly ILogger<OutgoingEmailPublisher> _logger;
|
private readonly ILogger<SendingEmailPublisher> _logger;
|
||||||
private readonly RabbitMqConnectionFactory _cnnFactory;
|
private readonly RabbitMqConnectionFactory _cnnFactory;
|
||||||
private readonly Lazy<Task<IChannel>> _lazyChannel;
|
private readonly Lazy<Task<IChannel>> _lazyChannel;
|
||||||
|
|
||||||
public OutgoingEmailPublisher(IOptions<RabbitMqConfiguration> config, ILogger<OutgoingEmailPublisher> logger, RabbitMqConnectionFactory cnnFactory)
|
public SendingEmailPublisher(IOptions<RabbitMqConfiguration> config, ILogger<SendingEmailPublisher> logger, RabbitMqConnectionFactory cnnFactory)
|
||||||
{
|
{
|
||||||
_config = config.Value;
|
_config = config.Value;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -66,9 +66,9 @@ public sealed class OutgoingEmailPublisher : IOutgoingEmailPublisher, IAsyncDisp
|
|||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task EnqueueAsync(OutgoingEmailEvent outgoingEmailEvent, CancellationToken cancellationToken = default)
|
public async Task EnqueueAsync(SendingEmailEvent sendingEmailEvent, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var json = JsonSerializer.Serialize(outgoingEmailEvent);
|
var json = JsonSerializer.Serialize(sendingEmailEvent);
|
||||||
var body = Encoding.UTF8.GetBytes(json);
|
var body = Encoding.UTF8.GetBytes(json);
|
||||||
|
|
||||||
var properties = new BasicProperties
|
var properties = new BasicProperties
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ public class EmailsController(IMediator mediator) : ControllerBase
|
|||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
public async Task<IActionResult> SendEmail([FromBody] SendEmailCommand command, CancellationToken cancellationToken)
|
public async Task<IActionResult> SendEmail([FromBody] SendEmailCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var outgoingEmailEvent = await mediator.Send(command, cancellationToken);
|
var sendingEmailEvent = await mediator.Send(command, cancellationToken);
|
||||||
|
|
||||||
return Accepted(new
|
return Accepted(new
|
||||||
{
|
{
|
||||||
outgoingEmailEvent.Id,
|
sendingEmailEvent.Id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ public record Email
|
|||||||
public bool IsHtml { get; set; } = true;
|
public bool IsHtml { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts this <see cref="Email"/> instance to an <see cref="OutgoingEmailEvent"/>.
|
/// Converts this <see cref="Email"/> instance to an <see cref="SendingEmailEvent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A new <see cref="OutgoingEmailEvent"/> representing this email.</returns>
|
/// <returns>A new <see cref="SendingEmailEvent"/> representing this email.</returns>
|
||||||
internal OutgoingEmailEvent ToEvent()
|
internal SendingEmailEvent ToEvent()
|
||||||
{
|
{
|
||||||
return new OutgoingEmailEvent
|
return new SendingEmailEvent
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Recipients = Recipients,
|
Recipients = Recipients,
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ public static class EmailSender
|
|||||||
/// Thrown when <see cref="ConnectRabbitMq(Action{RabbitMqConfiguration}, OnReconnect)"/> has not been called prior to sending.
|
/// Thrown when <see cref="ConnectRabbitMq(Action{RabbitMqConfiguration}, OnReconnect)"/> has not been called prior to sending.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This method maps <see cref="Email"/> to <see cref="OutgoingEmailEvent"/>,
|
/// This method maps <see cref="Email"/> to <see cref="SendingEmailEvent"/>,
|
||||||
/// then resolves <see cref="IOutgoingEmailPublisher"/> from the internal
|
/// then resolves <see cref="ISendingEmailPublisher"/> from the internal
|
||||||
/// service provider and calls <c>EnqueueAsync</c> in a fire-and-forget manner.
|
/// service provider and calls <c>EnqueueAsync</c> in a fire-and-forget manner.
|
||||||
/// Ensure that any unhandled exceptions from the async operation are handled
|
/// Ensure that any unhandled exceptions from the async operation are handled
|
||||||
/// at the publisher level.
|
/// at the publisher level.
|
||||||
@@ -124,7 +124,7 @@ public static class EmailSender
|
|||||||
if(!IsConnected)
|
if(!IsConnected)
|
||||||
throw new InvalidOperationException("Messaging service is not connected. Call ConnectRabbitMq first.");
|
throw new InvalidOperationException("Messaging service is not connected. Call ConnectRabbitMq first.");
|
||||||
|
|
||||||
var publisher = LazyProvider.Value.GetRequiredService<IOutgoingEmailPublisher>();
|
var publisher = LazyProvider.Value.GetRequiredService<ISendingEmailPublisher>();
|
||||||
publisher.EnqueueAsync(email.ToEvent());
|
publisher.EnqueueAsync(email.ToEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,31 +10,31 @@ using RabbitMQ.Client;
|
|||||||
namespace DigitalData.MessagingService.Tests.Integration;
|
namespace DigitalData.MessagingService.Tests.Integration;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Integration tests for <see cref="OutgoingEmailPublisher"/> against the real RabbitMQ broker.
|
/// Integration tests for <see cref="SendingEmailPublisher"/> against the real RabbitMQ broker.
|
||||||
/// Each test publishes a message and immediately reads it back via BasicGetAsync to verify
|
/// Each test publishes a message and immediately reads it back via BasicGetAsync to verify
|
||||||
/// the full round-trip without starting the consumer (which requires Limilabs Mail.dll).
|
/// the full round-trip without starting the consumer (which requires Limilabs Mail.dll).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
public sealed class SendingEmailPublisherTests : IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly ServiceProvider _serviceProvider;
|
private readonly ServiceProvider _serviceProvider;
|
||||||
private readonly IOutgoingEmailPublisher _publisher;
|
private readonly ISendingEmailPublisher _publisher;
|
||||||
private readonly RabbitMqConnectionFactory _factory;
|
private readonly RabbitMqConnectionFactory _factory;
|
||||||
|
|
||||||
public OutgoingEmailPublisherTests()
|
public SendingEmailPublisherTests()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
services.AddMessagingServicePublisher(RabbitMqTestConfig.Apply);
|
services.AddMessagingServicePublisher(RabbitMqTestConfig.Apply);
|
||||||
|
|
||||||
_serviceProvider = services.BuildServiceProvider();
|
_serviceProvider = services.BuildServiceProvider();
|
||||||
_publisher = _serviceProvider.GetRequiredService<IOutgoingEmailPublisher>();
|
_publisher = _serviceProvider.GetRequiredService<ISendingEmailPublisher>();
|
||||||
_factory = _serviceProvider.GetRequiredService<RabbitMqConnectionFactory>();
|
_factory = _serviceProvider.GetRequiredService<RabbitMqConnectionFactory>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task EnqueueAsync_PublishesMessage_MessageArrivesInQueue()
|
public async Task EnqueueAsync_PublishesMessage_MessageArrivesInQueue()
|
||||||
{
|
{
|
||||||
var email = new OutgoingEmailEvent
|
var email = new SendingEmailEvent
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Recipients = ["test@example.com"],
|
Recipients = ["test@example.com"],
|
||||||
@@ -59,7 +59,7 @@ public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task EnqueueAsync_MultipleMessages_AllArrivesInQueue()
|
public async Task EnqueueAsync_MultipleMessages_AllArrivesInQueue()
|
||||||
{
|
{
|
||||||
var emails = Enumerable.Range(1, 3).Select(i => new OutgoingEmailEvent
|
var emails = Enumerable.Range(1, 3).Select(i => new SendingEmailEvent
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Recipients = new List<string> { $"recipient{i}@example.com" },
|
Recipients = new List<string> { $"recipient{i}@example.com" },
|
||||||
@@ -82,7 +82,7 @@ public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetQueueDepthAsync_AfterPublish_ReturnsPositiveDepth()
|
public async Task GetQueueDepthAsync_AfterPublish_ReturnsPositiveDepth()
|
||||||
{
|
{
|
||||||
var email = new OutgoingEmailEvent
|
var email = new SendingEmailEvent
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Recipients = ["depth-test@example.com"],
|
Recipients = ["depth-test@example.com"],
|
||||||
@@ -105,7 +105,7 @@ public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
|||||||
{
|
{
|
||||||
var id = Guid.NewGuid();
|
var id = Guid.NewGuid();
|
||||||
|
|
||||||
var email = new OutgoingEmailEvent
|
var email = new SendingEmailEvent
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
Recipients = new List<string> { "serialize@example.com" },
|
Recipients = new List<string> { "serialize@example.com" },
|
||||||
@@ -132,7 +132,7 @@ public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads a single message from the queue without acknowledging it (peek via nack+requeue).
|
/// Reads a single message from the queue without acknowledging it (peek via nack+requeue).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<OutgoingEmailEvent?> PeekMessageAsync()
|
private async Task<SendingEmailEvent?> PeekMessageAsync()
|
||||||
{
|
{
|
||||||
var connection = await _factory.GetDefaultConnectionAsync();
|
var connection = await _factory.GetDefaultConnectionAsync();
|
||||||
await using var channel = await connection.CreateChannelAsync();
|
await using var channel = await connection.CreateChannelAsync();
|
||||||
@@ -146,21 +146,21 @@ public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
|||||||
await channel.BasicNackAsync(result.DeliveryTag, multiple: false, requeue: true);
|
await channel.BasicNackAsync(result.DeliveryTag, multiple: false, requeue: true);
|
||||||
|
|
||||||
var json = Encoding.UTF8.GetString(result.Body.ToArray());
|
var json = Encoding.UTF8.GetString(result.Body.ToArray());
|
||||||
return JsonSerializer.Deserialize<OutgoingEmailEvent>(json);
|
return JsonSerializer.Deserialize<SendingEmailEvent>(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scans queue messages (up to a limit) to find a message matching the given <paramref name="id"/>.
|
/// Scans queue messages (up to a limit) to find a message matching the given <paramref name="id"/>.
|
||||||
/// All messages are re-queued after inspection.
|
/// All messages are re-queued after inspection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<OutgoingEmailEvent?> FindMessageAsync(Guid id, int maxMessages = 50)
|
private async Task<SendingEmailEvent?> FindMessageAsync(Guid id, int maxMessages = 50)
|
||||||
{
|
{
|
||||||
var connection = await _factory.GetDefaultConnectionAsync();
|
var connection = await _factory.GetDefaultConnectionAsync();
|
||||||
await using var channel = await connection.CreateChannelAsync();
|
await using var channel = await connection.CreateChannelAsync();
|
||||||
|
|
||||||
var requeue = new List<(ulong DeliveryTag, byte[] Body)>();
|
var requeue = new List<(ulong DeliveryTag, byte[] Body)>();
|
||||||
|
|
||||||
OutgoingEmailEvent? found = null;
|
SendingEmailEvent? found = null;
|
||||||
|
|
||||||
for (int i = 0; i < maxMessages; i++)
|
for (int i = 0; i < maxMessages; i++)
|
||||||
{
|
{
|
||||||
@@ -171,7 +171,7 @@ public sealed class OutgoingEmailPublisherTests : IAsyncDisposable
|
|||||||
requeue.Add((result.DeliveryTag, result.Body.ToArray()));
|
requeue.Add((result.DeliveryTag, result.Body.ToArray()));
|
||||||
|
|
||||||
var json = Encoding.UTF8.GetString(result.Body.ToArray());
|
var json = Encoding.UTF8.GetString(result.Body.ToArray());
|
||||||
var evt = JsonSerializer.Deserialize<OutgoingEmailEvent>(json);
|
var evt = JsonSerializer.Deserialize<SendingEmailEvent>(json);
|
||||||
|
|
||||||
if (evt?.Id == id)
|
if (evt?.Id == id)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user