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 StubSender : ISender { private readonly object? _response; public StubSender(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(); } private static ISender CreateSender(T? response) => new StubSender(response); #endregion #region GetOrThrow — non-null scalar [Test] public async Task GetOrThrow_WithNonNullResponse_ReturnsResponse() { var sender = CreateSender("hello"); var request = new StubRequest(); var result = await sender.GetOrThrow(request, () => new InvalidOperationException()); Assert.That(result, Is.EqualTo("hello")); } #endregion #region GetOrThrow — null response [Test] public void GetOrThrow_WithNullResponse_ThrowsCustomException() { var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => sender.GetOrThrow(request, () => new InvalidOperationException("custom"))); Assert.That(ex!.Message, Is.EqualTo("custom")); } #endregion #region GetOrThrow — empty collection [Test] public void GetOrThrow_WithEmptyList_ThrowsCustomException() { var sender = CreateSender>(new List()); var request = new StubRequest?>(); Assert.ThrowsAsync( () => sender.GetOrThrow(request, () => new ArgumentException("empty"))); } #endregion #region GetOrThrow — non-empty collection [Test] public async Task GetOrThrow_WithNonEmptyList_ReturnsResponse() { var expected = new List { 1, 2 }; var sender = CreateSender>(expected); var request = new StubRequest?>(); var result = await sender.GetOrThrow(request, () => new InvalidOperationException()); Assert.That(result, Is.EqualTo(expected)); } #endregion #region GetOrThrow — string edge case (string implements IEnumerable) [Test] public async Task GetOrThrow_WithEmptyString_ReturnsEmptyString() { var sender = CreateSender(""); var request = new StubRequest(); var result = await sender.GetOrThrow(request, () => new InvalidOperationException("should not throw")); Assert.That(result, Is.EqualTo("")); } #endregion #region GetOrThrow (NotFoundException) — non-null scalar [Test] public async Task GetOrThrow_NotFound_WithNonNullResponse_ReturnsResponse() { var sender = CreateSender("hello"); var request = new StubRequest(); var result = await sender.GetOrThrow(request); Assert.That(result, Is.EqualTo("hello")); } [Test] public async Task GetOrThrow_NotFound_WithExceptionMessage_AndNonNullResponse_ReturnsResponse() { var sender = CreateSender(42); var request = new StubRequest(); var result = await sender.GetOrThrow(request, "not found", CancellationToken.None); Assert.That(result, Is.EqualTo(42)); } #endregion #region GetOrThrow (NotFoundException) — null response [Test] public void GetOrThrow_NotFound_WithNullResponse_ThrowsNotFoundException() { var sender = CreateSender(null); var request = new StubRequest(); Assert.ThrowsAsync(() => sender.GetOrThrow(request)); } [Test] public void GetOrThrow_NotFound_WithNullResponse_AndCustomMessage_ContainsMessage() { const string message = "Entity not found"; var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => sender.GetOrThrow(request, message, CancellationToken.None)); Assert.That(ex!.Message, Does.Contain(message)); } [Test] public void GetOrThrow_NotFound_WithNullResponse_HasDefaultMessageWithTypeName() { var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync(() => sender.GetOrThrow(request)); Assert.That(ex!.Message, Does.Contain(nameof(String))); } #endregion #region GetOrThrow (NotFoundException) — empty collection [Test] public void GetOrThrow_NotFound_WithEmptyList_ThrowsNotFoundException() { var sender = CreateSender>(new List()); var request = new StubRequest?>(); Assert.ThrowsAsync(() => sender.GetOrThrow(request)); } [Test] public void GetOrThrow_NotFound_WithEmptyArray_ThrowsNotFoundException() { var sender = CreateSender(Array.Empty()); var request = new StubRequest(); Assert.ThrowsAsync(() => sender.GetOrThrow(request)); } [Test] public void GetOrThrow_NotFound_WithEmptyCollection_AndCustomMessage_ContainsMessage() { const string message = "No items found"; var sender = CreateSender>(new List()); var request = new StubRequest?>(); var ex = Assert.ThrowsAsync( () => sender.GetOrThrow(request, message, CancellationToken.None)); Assert.That(ex!.Message, Does.Contain(message)); } #endregion #region GetOrThrow (NotFoundException) — non-empty collection [Test] public async Task GetOrThrow_NotFound_WithNonEmptyList_ReturnsResponse() { var expected = new List { "a", "b" }; var sender = CreateSender>(expected); var request = new StubRequest?>(); var result = await sender.GetOrThrow(request); Assert.That(result, Is.EqualTo(expected)); } [Test] public async Task GetOrThrow_NotFound_WithNonEmptyArray_ReturnsResponse() { var expected = new[] { 1, 2, 3 }; var sender = CreateSender(expected); var request = new StubRequest(); var result = await sender.GetOrThrow(request); Assert.That(result, Is.EqualTo(expected)); } #endregion #region CancellationToken [Test] public void GetOrThrow_WithCancelledToken_ThrowsOperationCanceledException() { var sender = CreateSender("value"); var request = new StubRequest(); var cts = new CancellationTokenSource(); cts.Cancel(); Assert.ThrowsAsync( () => sender.GetOrThrow(request, () => new InvalidOperationException(), cts.Token)); } [Test] public void GetOrThrow_NotFound_WithCancelledToken_ThrowsOperationCanceledException() { var sender = CreateSender("value"); var request = new StubRequest(); var cts = new CancellationTokenSource(); cts.Cancel(); Assert.ThrowsAsync( () => sender.GetOrThrow(request, cts.Token)); } #endregion }