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 Throw — non-null scalar [Test] public async Task Throw_WithNonNullResponse_ReturnsResponse() { var sender = CreateSender("hello"); var request = new StubRequest(); var result = await sender.GetOr(request).Throw(() => new InvalidOperationException()); Assert.That(result, Is.EqualTo("hello")); } #endregion #region Throw — null response [Test] public void Throw_WithNullResponse_ThrowsCustomException() { var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => sender.GetOr(request).Throw(() => new InvalidOperationException("custom"))); Assert.That(ex!.Message, Is.EqualTo("custom")); } #endregion #region Throw — empty collection [Test] public void Throw_WithEmptyList_ThrowsCustomException() { var sender = CreateSender>(new List()); var request = new StubRequest?>(); Assert.ThrowsAsync( () => sender.GetOr(request).Throw(() => new ArgumentException("empty"))); } #endregion #region Throw — non-empty collection [Test] public async Task Throw_WithNonEmptyList_ReturnsResponse() { var expected = new List { 1, 2 }; var sender = CreateSender>(expected); var request = new StubRequest?>(); var result = await sender.GetOr(request).Throw(() => new InvalidOperationException()); Assert.That(result, Is.EqualTo(expected)); } #endregion #region Throw — string edge case (string implements IEnumerable) [Test] public async Task Throw_WithEmptyString_ReturnsEmptyString() { var sender = CreateSender(""); var request = new StubRequest(); var result = await sender.GetOr(request).Throw(() => new InvalidOperationException("should not throw")); Assert.That(result, Is.EqualTo("")); } #endregion #region ThrowNotFound — non-null scalar [Test] public async Task ThrowNotFound_WithNonNullResponse_ReturnsResponse() { var sender = CreateSender("hello"); var request = new StubRequest(); var result = await sender.GetOr(request).ThrowNotFound(); Assert.That(result, Is.EqualTo("hello")); } [Test] public async Task ThrowNotFound_WithExceptionMessage_AndNonNullResponse_ReturnsResponse() { var sender = CreateSender(42); var request = new StubRequest(); var result = await sender.GetOr(request).ThrowNotFound("not found"); Assert.That(result, Is.EqualTo(42)); } #endregion #region ThrowNotFound — null response [Test] public void ThrowNotFound_WithNullResponse_ThrowsNotFoundException() { var sender = CreateSender(null); var request = new StubRequest(); Assert.ThrowsAsync(() => sender.GetOr(request).ThrowNotFound()); } [Test] public void ThrowNotFound_WithNullResponse_AndCustomMessage_ContainsMessage() { const string message = "Entity not found"; var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => sender.GetOr(request).ThrowNotFound(message)); Assert.That(ex!.Message, Does.Contain(message)); } [Test] public void ThrowNotFound_WithNullResponse_HasDefaultMessageWithTypeName() { var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync(() => sender.GetOr(request).ThrowNotFound()); Assert.That(ex!.Message, Does.Contain(nameof(String))); } #endregion #region ThrowNotFound — empty collection [Test] public void ThrowNotFound_WithEmptyList_ThrowsNotFoundException() { var sender = CreateSender>(new List()); var request = new StubRequest?>(); Assert.ThrowsAsync(() => sender.GetOr(request).ThrowNotFound()); } [Test] public void ThrowNotFound_WithEmptyArray_ThrowsNotFoundException() { var sender = CreateSender(Array.Empty()); var request = new StubRequest(); Assert.ThrowsAsync(() => sender.GetOr(request).ThrowNotFound()); } [Test] public void ThrowNotFound_WithEmptyCollection_AndCustomMessage_ContainsMessage() { const string message = "No items found"; var sender = CreateSender>(new List()); var request = new StubRequest?>(); var ex = Assert.ThrowsAsync( () => sender.GetOr(request).ThrowNotFound(message)); Assert.That(ex!.Message, Does.Contain(message)); } #endregion #region ThrowNotFound — non-empty collection [Test] public async Task ThrowNotFound_WithNonEmptyList_ReturnsResponse() { var expected = new List { "a", "b" }; var sender = CreateSender>(expected); var request = new StubRequest?>(); var result = await sender.GetOr(request).ThrowNotFound(); Assert.That(result, Is.EqualTo(expected)); } [Test] public async Task ThrowNotFound_WithNonEmptyArray_ReturnsResponse() { var expected = new[] { 1, 2, 3 }; var sender = CreateSender(expected); var request = new StubRequest(); var result = await sender.GetOr(request).ThrowNotFound(); Assert.That(result, Is.EqualTo(expected)); } #endregion #region ThrowInvalidOperation — null response [Test] public void ThrowInvalidOperation_WithNullResponse_ThrowsInvalidOperationException() { var sender = CreateSender(null); var request = new StubRequest(); Assert.ThrowsAsync( () => sender.GetOr(request).ThrowInvalidOperation()); } [Test] public void ThrowInvalidOperation_WithNullResponse_AndCustomMessage_ContainsMessage() { const string message = "Something went wrong"; var sender = CreateSender(null); var request = new StubRequest(); var ex = Assert.ThrowsAsync( () => sender.GetOr(request).ThrowInvalidOperation(message)); Assert.That(ex!.Message, Does.Contain(message)); } #endregion #region CancellationToken [Test] public void Throw_WithCancelledToken_ThrowsOperationCanceledException() { var sender = CreateSender("value"); var request = new StubRequest(); var cts = new CancellationTokenSource(); cts.Cancel(); Assert.ThrowsAsync( () => sender.GetOr(request, cts.Token).Throw(() => new InvalidOperationException())); } [Test] public void ThrowNotFound_WithCancelledToken_ThrowsOperationCanceledException() { var sender = CreateSender("value"); var request = new StubRequest(); var cts = new CancellationTokenSource(); cts.Cancel(); Assert.ThrowsAsync( () => sender.GetOr(request, cts.Token).ThrowNotFound()); } #endregion }