From d2d78b4323796387111a2b6b8f1c3b8edbf62f8c Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Fri, 28 Aug 2026 00:33:03 -0400 Subject: [PATCH] Add unit tests for UploadPhotoHandler --- web/backend/Biergarten.API.slnx | 1 + .../Commands/UploadPhotoHandlerTests.cs | 95 +++++++++++++++++++ .../Features.PhotoUpload.Tests.csproj | 25 +++++ 3 files changed, 121 insertions(+) create mode 100644 web/backend/Features/Features.PhotoUpload.Tests/Commands/UploadPhotoHandlerTests.cs create mode 100644 web/backend/Features/Features.PhotoUpload.Tests/Features.PhotoUpload.Tests.csproj diff --git a/web/backend/Biergarten.API.slnx b/web/backend/Biergarten.API.slnx index 2abf4157..e62246bc 100644 --- a/web/backend/Biergarten.API.slnx +++ b/web/backend/Biergarten.API.slnx @@ -32,6 +32,7 @@ + diff --git a/web/backend/Features/Features.PhotoUpload.Tests/Commands/UploadPhotoHandlerTests.cs b/web/backend/Features/Features.PhotoUpload.Tests/Commands/UploadPhotoHandlerTests.cs new file mode 100644 index 00000000..09f5179b --- /dev/null +++ b/web/backend/Features/Features.PhotoUpload.Tests/Commands/UploadPhotoHandlerTests.cs @@ -0,0 +1,95 @@ +using Domain.Entities; +using Features.ImageUploads.Commands.UploadPhoto; +using Features.ImageUploads.Repository; +using FluentAssertions; +using Infrastructure.FileUpload; +using Microsoft.AspNetCore.Http; +using Moq; + +namespace Features.PhotoUpload.Tests.Commands; + +public class UploadPhotoHandlerTests +{ + private readonly Mock _fileStorageProviderMock = new(); + private readonly UploadPhotoHandler _handler; + private readonly Mock _repoMock = new(); + + public UploadPhotoHandlerTests() + { + _handler = new UploadPhotoHandler(_fileStorageProviderMock.Object, _repoMock.Object); + } + + private static Mock ValidFile(string content = "fake-image-bytes", string contentType = "image/png") + { + Mock file = new(); + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(content); + file.Setup(f => f.OpenReadStream()).Returns(() => new MemoryStream(bytes)); + file.Setup(f => f.ContentType).Returns(contentType); + file.Setup(f => f.Length).Returns(bytes.Length); + return file; + } + + [Fact] + public async Task Handle_UploadsFileUnderRequestedKey_AndPersistsPhoto_ReturnsNewPhotoId() + { + Mock file = ValidFile(); + UploadPhotoCommand command = new(Guid.NewGuid(), "breweries/some-brewery-id", file.Object); + + string? uploadedKey = null; + string? uploadedContentType = null; + _fileStorageProviderMock + .Setup(p => p.UploadAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Callback((key, _, contentType, _) => + { + uploadedKey = key; + uploadedContentType = contentType; + }) + .Returns(Task.CompletedTask); + + Photo? persisted = null; + _repoMock + .Setup(r => r.CreateAsync(It.IsAny(), It.IsAny())) + .Callback((photo, _) => persisted = photo) + .Returns(Task.CompletedTask); + + Guid photoId = await _handler.Handle(command, CancellationToken.None); + + photoId.Should().NotBe(Guid.Empty); + + uploadedKey.Should().Be($"breweries/some-brewery-id/{photoId}"); + uploadedContentType.Should().Be("image/png"); + + persisted.Should().NotBeNull(); + persisted!.Hyperlink.Should().Be(uploadedKey); + persisted.UploadedById.Should().Be(command.UploadedById); + + _fileStorageProviderMock.Verify( + p => p.UploadAsync(uploadedKey!, It.IsAny(), "image/png", It.IsAny()), + Times.Once); + _repoMock.Verify(r => r.CreateAsync(It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_UploadOrderedBeforePersist_DoesNotPersistWhenUploadFails() + { + Mock file = ValidFile(); + UploadPhotoCommand command = new(Guid.NewGuid(), "avatars", file.Object); + + _fileStorageProviderMock + .Setup(p => p.UploadAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ThrowsAsync(new InvalidOperationException("storage unavailable")); + + Func act = () => _handler.Handle(command, CancellationToken.None); + + await act.Should().ThrowAsync(); + _repoMock.Verify(r => r.CreateAsync(It.IsAny(), It.IsAny()), Times.Never); + } +} diff --git a/web/backend/Features/Features.PhotoUpload.Tests/Features.PhotoUpload.Tests.csproj b/web/backend/Features/Features.PhotoUpload.Tests/Features.PhotoUpload.Tests.csproj new file mode 100644 index 00000000..a0af8703 --- /dev/null +++ b/web/backend/Features/Features.PhotoUpload.Tests/Features.PhotoUpload.Tests.csproj @@ -0,0 +1,25 @@ + + + net10.0 + enable + enable + false + Features.PhotoUpload.Tests + + + + + + + + + + + + + + + + + +