Files
PowderCoatingLogix/tests/PowderCoating.UnitTests/JobPhotoServiceTests.cs
T
spouliot dbe4170986 Add unit tests for 9 new services/controllers and expand existing test coverage
116 tests passing: JobPhotoService, MeasurementConversionService, PlatformSettingsService,
QuoteApprovalController, QuotePhotoService, ShopCapabilityCalculator, StorageMigrationService,
TenantContext, UsageQuotaController — plus expanded PricingCalculation, Registration, and
Subscription tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:27:30 -04:00

174 lines
5.5 KiB
C#

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using PowderCoating.Application.Configuration;
using PowderCoating.Application.Interfaces;
using PowderCoating.Application.Services;
using PowderCoating.Core.Enums;
namespace PowderCoating.UnitTests;
public class JobPhotoServiceTests
{
[Fact]
public async Task SaveJobPhotoAsync_ReturnsError_WhenFileMissing()
{
var service = CreateService();
var result = await service.SaveJobPhotoAsync(null!, 1, 2);
Assert.False(result.Success);
Assert.Equal("No file was uploaded.", result.ErrorMessage);
}
[Fact]
public async Task SaveJobPhotoAsync_ReturnsError_WhenFileTooLarge()
{
var service = CreateService();
var file = CreateFormFile("big.jpg", 10 * 1024 * 1024 + 1);
var result = await service.SaveJobPhotoAsync(file, 1, 2);
Assert.False(result.Success);
Assert.Equal("Photo must be smaller than 10 MB.", result.ErrorMessage);
}
[Fact]
public async Task SaveJobPhotoAsync_ReturnsError_WhenExtensionNotAllowed()
{
var service = CreateService();
var file = CreateFormFile("notes.txt");
var result = await service.SaveJobPhotoAsync(file, 1, 2);
Assert.False(result.Success);
Assert.Equal("Only JPG, PNG, GIF, and WebP images are allowed.", result.ErrorMessage);
}
[Fact]
public async Task SaveJobPhotoAsync_ReturnsBlobError_WhenUploadFails()
{
var blobService = new Mock<IAzureBlobStorageService>();
blobService
.Setup(x => x.UploadAsync("jobimages", It.IsAny<string>(), It.IsAny<Stream>(), "image/png"))
.ReturnsAsync((false, "upload failed"));
var service = CreateService(blobService);
var result = await service.SaveJobPhotoAsync(CreateFormFile("photo.png"), 9, 7);
Assert.False(result.Success);
Assert.Equal("upload failed", result.ErrorMessage);
}
[Fact]
public async Task SaveJobPhotoAsync_UsesTenantScopedBlobPath_WhenSuccessful()
{
var blobService = new Mock<IAzureBlobStorageService>();
blobService
.Setup(x => x.UploadAsync("jobimages", It.IsAny<string>(), It.IsAny<Stream>(), "image/webp"))
.ReturnsAsync((true, string.Empty));
var service = CreateService(blobService);
var result = await service.SaveJobPhotoAsync(CreateFormFile("photo.webp"), 9, 7, "caption", JobPhotoType.After);
Assert.True(result.Success);
Assert.StartsWith("7/job-photos/9/", result.FilePath);
Assert.EndsWith(".webp", result.FilePath);
}
[Fact]
public async Task DeleteJobPhotoAsync_ReturnsError_WhenPathMissing()
{
var service = CreateService();
var result = await service.DeleteJobPhotoAsync(string.Empty);
Assert.False(result.Success);
Assert.Equal("File path is required.", result.ErrorMessage);
}
[Fact]
public async Task GetJobPhotoAsync_ReturnsError_WhenPathMissing()
{
var service = CreateService();
var result = await service.GetJobPhotoAsync(" ");
Assert.False(result.Success);
Assert.Equal("File path is required.", result.ErrorMessage);
Assert.Empty(result.FileContent);
}
[Fact]
public async Task JobPhotoExistsAsync_ReturnsFalse_WhenPathMissing()
{
var service = CreateService();
var result = await service.JobPhotoExistsAsync(null!);
Assert.False(result);
}
[Fact]
public async Task GetJobPhotoAsync_ProxiesBlobDownload()
{
var blobService = new Mock<IAzureBlobStorageService>();
blobService
.Setup(x => x.DownloadAsync("jobimages", "7/job-photos/9/photo.jpg"))
.ReturnsAsync((true, new byte[] { 1, 2 }, "image/jpeg", string.Empty));
var service = CreateService(blobService);
var result = await service.GetJobPhotoAsync("7/job-photos/9/photo.jpg");
Assert.True(result.Success);
Assert.Equal("image/jpeg", result.ContentType);
Assert.Equal(new byte[] { 1, 2 }, result.FileContent);
}
[Fact]
public async Task JobPhotoExistsAsync_UsesBlobServiceForValidPath()
{
var blobService = new Mock<IAzureBlobStorageService>();
blobService
.Setup(x => x.ExistsAsync("jobimages", "7/job-photos/9/photo.jpg"))
.ReturnsAsync(true);
var service = CreateService(blobService);
var result = await service.JobPhotoExistsAsync("7/job-photos/9/photo.jpg");
Assert.True(result);
}
private static JobPhotoService CreateService(Mock<IAzureBlobStorageService>? blobService = null)
{
var settings = Options.Create(new StorageSettings
{
Containers = new StorageContainers
{
JobImages = "jobimages"
}
});
return new JobPhotoService(
(blobService ?? new Mock<IAzureBlobStorageService>()).Object,
settings,
Mock.Of<ILogger<JobPhotoService>>());
}
private static IFormFile CreateFormFile(string fileName, long? lengthOverride = null)
{
var dataLength = lengthOverride.HasValue
? (int)Math.Min(lengthOverride.Value, 1024)
: 16;
var bytes = Enumerable.Repeat((byte)65, dataLength).ToArray();
var stream = new MemoryStream(bytes);
return new FormFile(stream, 0, lengthOverride ?? bytes.Length, "file", fileName);
}
}