namespace Hrynco.RabbitMq.Tests; using System; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Xunit; public sealed class RabbitMqConsumerBaseExtensionTests { private static readonly RabbitMqMessageContext Context = new() { QueueName = "messages.test", Exchange = string.Empty, RoutingKey = "messages.test", MessageId = "message-123", MessageType = "Example.Message.v1", CorrelationId = "correlation-123", ReplyTo = "messages.reply", ContentType = "application/json", Redelivered = true }; [Fact] public async Task ContextAwareOverload_ReceivesTransportMetadata() { var consumer = new ContextAwareConsumer(CreateOptions()); var message = CreateMessage(); await consumer.InvokeAsync(message, Context); consumer.HandledMessage.Should().BeSameAs(message); consumer.HandledContext.Should().BeSameAs(Context); } [Fact] public async Task ContextAwareOverload_DelegatesToLegacyOverride() { var consumer = new LegacyConsumer(CreateOptions()); var message = CreateMessage(); await consumer.InvokeAsync(message, Context); consumer.HandledMessage.Should().BeSameAs(message); } [Fact] public void ValidationHook_IsValidByDefault() { var consumer = new LegacyConsumer(CreateOptions()); bool isValid = consumer.Validate(CreateMessage(), Context, out string? error); isValid.Should().BeTrue(); error.Should().BeNull(); } private static TestMessage CreateMessage() => new() { CorrelationContext = new CorrelationContext { CorrelationId = "payload-correlation" }, Data = "payload" }; private static IOptionsMonitor CreateOptions() { return new TestOptionsMonitor(new RabbitMqSettings { Host = "localhost", User = "guest", Password = "guest" }); } private sealed class ContextAwareConsumer(IOptionsMonitor options) : RabbitMqConsumerBase(options, NullLogger.Instance) { protected override string QueueName => "messages.test"; public TestMessage? HandledMessage { get; private set; } public RabbitMqMessageContext? HandledContext { get; private set; } protected override Task HandleMessageAsync( TestMessage message, RabbitMqMessageContext context, CancellationToken cancellationToken) { HandledMessage = message; HandledContext = context; return Task.CompletedTask; } public Task InvokeAsync(TestMessage message, RabbitMqMessageContext context) { return HandleMessageAsync(message, context, CancellationToken.None); } } private sealed class LegacyConsumer(IOptionsMonitor options) : RabbitMqConsumerBase(options, NullLogger.Instance) { protected override string QueueName => "messages.test"; public TestMessage? HandledMessage { get; private set; } protected override Task HandleMessageAsync(TestMessage message, CancellationToken cancellationToken) { HandledMessage = message; return Task.CompletedTask; } public Task InvokeAsync(TestMessage message, RabbitMqMessageContext context) { return HandleMessageAsync(message, context, CancellationToken.None); } public bool Validate(TestMessage message, RabbitMqMessageContext context, out string? error) { return TryValidateMessage(message, context, out error); } } private sealed record TestMessage : IRabbitMqMessage { public CorrelationContext CorrelationContext { get; set; } = null!; public string Data { get; set; } = string.Empty; } private sealed class TestOptionsMonitor(RabbitMqSettings settings) : IOptionsMonitor { public RabbitMqSettings CurrentValue => settings; public RabbitMqSettings Get(string? name) => settings; public IDisposable? OnChange(Action listener) => null; } }