Files
DigitalData.MessagingService/tests/DigitalData.MessagingService.Tests/Integration/SendingEmailPublisherTests.cs
TekH 91581649ba Refactor namespaces and remove Abstraction project
Replaced `DigitalData.MessagingService.Abstraction` with
`DigitalData.MessagingService.Application.Common.Dto` and
`DigitalData.MessagingService.Application.Common.Dto.MailSearch`
to improve modularity and organization.

Removed the `Abstraction` project and updated all references
to use the `Application` project. Updated namespaces, `using`
directives, and dependencies across the codebase.

Refactored interfaces, commands, queries, validators, and
services to use the new DTOs. Updated RabbitMQ integration,
AutoMapper profiles, background services, and tests to align
with the new structure.

Performed general cleanup by removing redundant `using`
directives and obsolete references.
2026-08-12 15:13:15 +02:00

211 lines
7.4 KiB
C#

using System.Text;
using System.Text.Json;
using DigitalData.MessagingService.Publisher;
using DigitalData.MessagingService.RabbitMQ;
using Microsoft.Extensions.DependencyInjection;
using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Application.Common.Interfaces;
namespace DigitalData.MessagingService.Tests.Integration;
/// <summary>
/// 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
/// the full round-trip without starting the consumer (which requires Limilabs Mail.dll).
/// </summary>
public sealed class SendingEmailPublisherTests : IAsyncDisposable
{
private readonly ServiceProvider _serviceProvider;
private readonly ISendingEmailPublisher _publisher;
private readonly RabbitMqConnectionFactory _factory;
public SendingEmailPublisherTests()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddMessagingServicePublisher(RabbitMqTestConfig.Apply);
_serviceProvider = services.BuildServiceProvider();
_publisher = _serviceProvider.GetRequiredService<ISendingEmailPublisher>();
_factory = _serviceProvider.GetRequiredService<RabbitMqConnectionFactory>();
}
[Fact]
public async Task EnqueueAsync_PublishesMessage_MessageArrivesInQueue()
{
var email = new SendingEmailEvent
{
Id = Guid.NewGuid(),
Mail = new EmailContext
{
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["test@example.com"],
Subject = "Integration Test - EnqueueAsync",
Body = "<p>Hello from integration test.</p>",
IsHtml = true,
},
QueuedAt = DateTime.Now
};
var depthBefore = await _publisher.GetQueueDepthAsync();
await _publisher.EnqueueAsync(email);
await Task.Delay(300);
var depthAfter = await _publisher.GetQueueDepthAsync();
Assert.True(depthAfter >= depthBefore + 1,
$"Expected queue depth to increase by at least 1. Before: {depthBefore}, After: {depthAfter}.");
}
[Fact]
public async Task EnqueueAsync_MultipleMessages_AllArrivesInQueue()
{
var emails = Enumerable.Range(1, 3).Select(i => new SendingEmailEvent
{
Id = Guid.NewGuid(),
Mail = new EmailContext
{
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = new List<string> { $"recipient{i}@example.com" },
Subject = $"Integration Test - Batch #{i}",
Body = $"Batch message {i}",
IsHtml = false,
},
QueuedAt = DateTime.Now
}).ToList();
foreach (var email in emails)
await _publisher.EnqueueAsync(email);
await Task.Delay(500);
// Verify at least one message is present
var received = await PeekMessageAsync();
Assert.NotNull(received);
}
[Fact]
public async Task GetQueueDepthAsync_AfterPublish_ReturnsPositiveDepth()
{
var email = new SendingEmailEvent
{
Id = Guid.NewGuid(),
Mail = new EmailContext
{
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = ["depth-test@example.com"],
Subject = "Integration Test - GetQueueDepth",
Body = "Queue depth test",
IsHtml = false,
},
QueuedAt = DateTime.Now
};
await _publisher.EnqueueAsync(email);
await Task.Delay(300);
var depth = await _publisher.GetQueueDepthAsync();
Assert.True(depth > 0, $"Expected queue depth > 0, but got {depth}.");
}
[Fact]
public async Task EnqueueAsync_SerializesAllFields_DeserializesCorrectly()
{
var id = Guid.NewGuid();
var email = new SendingEmailEvent
{
Id = id,
Mail = new EmailContext
{
Sender = new EmailAccountDto { Username = "test@example.com", Password = "password", SmtpServer = "smtp.example.com" },
Recipients = new List<string> { "serialize@example.com" },
Subject = "Serialization Test",
Body = "<strong>Bold</strong>",
IsHtml = true,
},
QueuedAt = DateTime.Now
};
await _publisher.EnqueueAsync(email);
await Task.Delay(300);
// Read messages until we find the one we just published
var received = await FindMessageAsync(id);
Assert.NotNull(received);
Assert.Equal(id, received.Id);
Assert.Equal(["serialize@example.com"], received.Mail.Recipients);
Assert.Equal("Serialization Test", received.Mail.Subject);
Assert.Equal("<strong>Bold</strong>", received.Mail.Body);
Assert.True(received.Mail.IsHtml);
}
/// <summary>
/// Reads a single message from the queue without acknowledging it (peek via nack+requeue).
/// </summary>
private async Task<SendingEmailEvent?> PeekMessageAsync()
{
var connection = await _factory.GetDefaultConnectionAsync();
await using var channel = await connection.CreateChannelAsync();
var result = await channel.BasicGetAsync(RabbitMqTestConfig.QueueName, autoAck: false);
if (result is null)
return null;
// Nack with requeue=true so the message stays in the queue for consumer
await channel.BasicNackAsync(result.DeliveryTag, multiple: false, requeue: true);
var json = Encoding.UTF8.GetString(result.Body.ToArray());
return JsonSerializer.Deserialize<SendingEmailEvent>(json);
}
/// <summary>
/// Scans queue messages (up to a limit) to find a message matching the given <paramref name="id"/>.
/// All messages are re-queued after inspection.
/// </summary>
private async Task<SendingEmailEvent?> FindMessageAsync(Guid id, int maxMessages = 50)
{
var connection = await _factory.GetDefaultConnectionAsync();
await using var channel = await connection.CreateChannelAsync();
var requeue = new List<(ulong DeliveryTag, byte[] Body)>();
SendingEmailEvent? found = null;
for (int i = 0; i < maxMessages; i++)
{
var result = await channel.BasicGetAsync(RabbitMqTestConfig.QueueName, autoAck: false);
if (result is null)
break;
requeue.Add((result.DeliveryTag, result.Body.ToArray()));
var json = Encoding.UTF8.GetString(result.Body.ToArray());
var evt = JsonSerializer.Deserialize<SendingEmailEvent>(json);
if (evt?.Id == id)
{
found = evt;
break;
}
}
// Re-queue all inspected messages so the consumer can still process them
foreach (var (tag, _) in requeue)
await channel.BasicNackAsync(tag, multiple: false, requeue: true);
return found;
}
public async ValueTask DisposeAsync()
{
await _factory.DisposeAsync();
await _serviceProvider.DisposeAsync();
}
}