diff --git a/EnvelopeGenerator.Tests/Application/Fake.cs b/EnvelopeGenerator.Tests/Application/Fake.cs index 1281e7bb..5eed8052 100644 --- a/EnvelopeGenerator.Tests/Application/Fake.cs +++ b/EnvelopeGenerator.Tests/Application/Fake.cs @@ -193,13 +193,17 @@ public static class Extensions #endregion #region Envelope - public static CreateEnvelopeCommand CreateEnvelopeCommand(this Faker fake, int userId) => new() + public static CreateEnvelopeCommand CreateEnvelopeCommand(this Faker fake, int userId) { - Message = fake.Lorem.Paragraph(fake.Random.Number(2, 5)), - Title = fake.Lorem.Words(fake.Random.Number(3, 4)).Join(" "), - UserId = userId, - UseSQLExecutor = false - }; + var cmd = new CreateEnvelopeCommand + { + Message = fake.Lorem.Paragraph(fake.Random.Number(2, 5)), + Title = fake.Lorem.Words(fake.Random.Number(3, 4)).Join(" "), + UseSQLExecutor = false + }; + cmd.Authorize(userId); + return cmd; + } public static List CreateEnvelopeCommands(this Faker fake, params int[] userIDs) => Enumerable.Range(0, userIDs.Length) diff --git a/EnvelopeGenerator.Tests/Application/MediatorExtensionsTests.cs b/EnvelopeGenerator.Tests/Application/MediatorExtensionsTests.cs new file mode 100644 index 00000000..ca28f61e --- /dev/null +++ b/EnvelopeGenerator.Tests/Application/MediatorExtensionsTests.cs @@ -0,0 +1,287 @@ +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 +}