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>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user