using DigitalData.Core.Exceptions; using EnvelopeGenerator.Application.Common.Extensions; using MediatR; namespace EnvelopeGenerator.Tests.Application; [TestFixture] public class MediatorExtensionsTests { #region Stub infrastructure private sealed class StubRequest : IRequest { } /// /// Minimal stub that returns a pre-configured response for any call. /// private sealed class StubMediator : IMediator { private readonly object? _response; public StubMediator(object? response) => _response = response; public Task Send(IRequest request, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); return Task.FromResult((TResponse)_response!); } public Task Send(TRequest request, CancellationToken cancellationToken = default) where TRequest : IRequest { cancellationToken.ThrowIfCancellationRequested(); return Task.CompletedTask; } public Task Send(object request, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); return Task.FromResult(_response); } public IAsyncEnumerable CreateStream(IStreamRequest request, CancellationToken cancellationToken = default) => throw new NotImplementedException(); public IAsyncEnumerable CreateStream(object request, CancellationToken cancellationToken = default) => throw new NotImplementedException(); public Task Publish(object notification, CancellationToken cancellationToken = default) => Task.CompletedTask; public Task Publish(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification => Task.CompletedTask; } private static IMediator CreateMediator(T? response) => new StubMediator(response); #endregion #region SendOrThrowAsync — non-null scalar [Test] public async Task SendOrThrowAsync_WithNonNullResponse_ReturnsResponse() { var mediator = CreateMediator("hello"); var request = new StubRequest(); var result = await mediator.SendOrThrowAsync(request, () => new InvalidOperationException()); Assert.That(result, Is.EqualTo("hello")); } #endregion #region SendOrThrowAsync — null response [Test] public void SendOrThrowAsync_WithNullResponse_ThrowsCustomException() { var mediator = CreateMediator(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => mediator.SendOrThrowAsync(request, () => new InvalidOperationException("custom"))); Assert.That(ex!.Message, Is.EqualTo("custom")); } #endregion #region SendOrThrowAsync — empty collection [Test] public void SendOrThrowAsync_WithEmptyList_ThrowsCustomException() { var mediator = CreateMediator>(new List()); var request = new StubRequest?>(); Assert.ThrowsAsync( () => mediator.SendOrThrowAsync(request, () => new ArgumentException("empty"))); } #endregion #region SendOrThrowAsync — non-empty collection [Test] public async Task SendOrThrowAsync_WithNonEmptyList_ReturnsResponse() { var expected = new List { 1, 2 }; var mediator = CreateMediator>(expected); var request = new StubRequest?>(); var result = await mediator.SendOrThrowAsync(request, () => new InvalidOperationException()); Assert.That(result, Is.EqualTo(expected)); } #endregion #region SendOrThrowAsync — string edge case (string implements IEnumerable) [Test] public async Task SendOrThrowAsync_WithEmptyString_ReturnsEmptyString() { var mediator = CreateMediator(""); var request = new StubRequest(); var result = await mediator.SendOrThrowAsync(request, () => new InvalidOperationException("should not throw")); Assert.That(result, Is.EqualTo("")); } #endregion #region SendOrNotFoundAsync — non-null scalar [Test] public async Task SendOrNotFoundAsync_WithNonNullResponse_ReturnsResponse() { var mediator = CreateMediator("hello"); var request = new StubRequest(); var result = await mediator.SendOrNotFoundAsync(request); Assert.That(result, Is.EqualTo("hello")); } [Test] public async Task SendOrNotFoundAsync_WithExceptionMessage_AndNonNullResponse_ReturnsResponse() { var mediator = CreateMediator(42); var request = new StubRequest(); var result = await mediator.SendOrNotFoundAsync(request, "not found", CancellationToken.None); Assert.That(result, Is.EqualTo(42)); } #endregion #region SendOrNotFoundAsync — null response [Test] public void SendOrNotFoundAsync_WithNullResponse_ThrowsNotFoundException() { var mediator = CreateMediator(null); var request = new StubRequest(); Assert.ThrowsAsync(() => mediator.SendOrNotFoundAsync(request)); } [Test] public void SendOrNotFoundAsync_WithNullResponse_AndCustomMessage_ThrowsNotFoundExceptionWithMessage() { const string message = "Entity not found"; var mediator = CreateMediator(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => mediator.SendOrNotFoundAsync(request, message, CancellationToken.None)); Assert.That(ex!.Message, Does.Contain(message)); } [Test] public void SendOrNotFoundAsync_WithNullResponse_HasDefaultMessage() { var mediator = CreateMediator(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync(() => mediator.SendOrNotFoundAsync(request)); Assert.That(ex!.Message, Does.Contain(nameof(String))); } #endregion #region SendOrNotFoundAsync — empty collection [Test] public void SendOrNotFoundAsync_WithEmptyList_ThrowsNotFoundException() { var mediator = CreateMediator>(new List()); var request = new StubRequest?>(); Assert.ThrowsAsync(() => mediator.SendOrNotFoundAsync(request)); } [Test] public void SendOrNotFoundAsync_WithEmptyArray_ThrowsNotFoundException() { var mediator = CreateMediator(Array.Empty()); var request = new StubRequest(); Assert.ThrowsAsync(() => mediator.SendOrNotFoundAsync(request)); } [Test] public void SendOrNotFoundAsync_WithEmptyCollection_AndCustomMessage_ThrowsNotFoundExceptionWithMessage() { const string message = "No items found"; var mediator = CreateMediator>(new List()); var request = new StubRequest?>(); var ex = Assert.ThrowsAsync( () => mediator.SendOrNotFoundAsync(request, message, CancellationToken.None)); Assert.That(ex!.Message, Does.Contain(message)); } #endregion #region SendOrNotFoundAsync — non-empty collection [Test] public async Task SendOrNotFoundAsync_WithNonEmptyList_ReturnsResponse() { var expected = new List { "a", "b" }; var mediator = CreateMediator>(expected); var request = new StubRequest?>(); var result = await mediator.SendOrNotFoundAsync(request); Assert.That(result, Is.EqualTo(expected)); } [Test] public async Task SendOrNotFoundAsync_WithNonEmptyArray_ReturnsResponse() { var expected = new[] { 1, 2, 3 }; var mediator = CreateMediator(expected); var request = new StubRequest(); var result = await mediator.SendOrNotFoundAsync(request); Assert.That(result, Is.EqualTo(expected)); } #endregion #region CancellationToken [Test] public void SendOrThrowAsync_WithCancelledToken_ThrowsOperationCanceledException() { var mediator = CreateMediator("value"); var request = new StubRequest(); var cts = new CancellationTokenSource(); cts.Cancel(); Assert.ThrowsAsync( () => mediator.SendOrThrowAsync(request, () => new InvalidOperationException(), cts.Token)); } [Test] public void SendOrNotFoundAsync_WithCancelledToken_ThrowsOperationCanceledException() { var mediator = CreateMediator("value"); var request = new StubRequest(); var cts = new CancellationTokenSource(); cts.Cancel(); Assert.ThrowsAsync( () => mediator.SendOrNotFoundAsync(request, cts.Token)); } #endregion }