using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Implementation; using SeaHaven.Services.DTOs; using SeaHaven.Services.Exceptions; using SeaHaven.Services.Helpers; using SeaHaven.Services.Implementation; using Xunit; namespace SeaHavenIndustries.Tests; public class WorkOrderUploadFileRulesTests { [Theory] [InlineData("report.pdf", ".pdf")] [InlineData(@"C:\temp\photo.PNG", ".png")] [InlineData("../evil.jpg", ".jpg")] public void TryGetAllowedExtension_AcceptsSafeTypes(string name, string expectedExt) { var ok = WorkOrderUploadFileRules.TryGetAllowedExtension(name, out var ext, out var error); Assert.True(ok, error); Assert.Equal(expectedExt, ext); } [Theory] [InlineData("payload.exe")] [InlineData("noext")] [InlineData("")] [InlineData("..")] public void TryGetAllowedExtension_RejectsUnsafeTypes(string name) { var ok = WorkOrderUploadFileRules.TryGetAllowedExtension(name, out _, out var error); Assert.False(ok); Assert.False(string.IsNullOrWhiteSpace(error)); } [Fact] public void BuildServerFileName_IsGuidPlusExtension_NoClientName() { var name = WorkOrderUploadFileRules.BuildServerFileName(".pdf"); Assert.StartsWith(name[..32], name); Assert.EndsWith(".pdf", name); Assert.DoesNotContain("..", name); Assert.DoesNotContain("/", name); Assert.DoesNotContain("\\", name); } } public class WorkOrderUploadValidationOrderTests { private static (ApplicationDbContext Context, WorkOrderCompletionService Completion, WorkOrderMediaService Media) CreateSut() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; var context = new ApplicationDbContext(options); var templateData = new CompletionDocTemplateDataService(context); var detailData = new WorkOrderDetailDataService(context); var fieldLocks = new WorkOrderFieldLockService(context); var audit = new WorkOrderAuditService(context, fieldLocks); var completion = new WorkOrderCompletionService(context, templateData, detailData, audit); var media = new WorkOrderMediaService(context, detailData); return (context, completion, media); } [Fact] public async Task EnsureCanUploadCompletionDoc_NotFound_ThrowsBeforeAnyPersist() { var (_, completion, _) = CreateSut(); var ex = await Assert.ThrowsAsync(() => completion.EnsureCanUploadCompletionDocAsync( 999, new WorkOrderCompletionDocUploadDto { WorkOrderVersion = Convert.ToBase64String(new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }) })); Assert.Equal("NotFound", ex.Code); } [Fact] public async Task EnsureCanUploadCompletionDoc_ReadOnly_Throws() { var (context, completion, _) = CreateSut(); context.workOrders.Add(new WorkOrder { Id = 1, LifecycleStatus = LifecycleStatus.Completed, RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 } }); await context.SaveChangesAsync(); var ex = await Assert.ThrowsAsync(() => completion.EnsureCanUploadCompletionDocAsync( 1, new WorkOrderCompletionDocUploadDto { WorkOrderVersion = Convert.ToBase64String(new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }) })); Assert.Equal("ReadOnly", ex.Code); } [Fact] public async Task EnsureCanAddMedia_NotFound_Throws() { var (_, _, media) = CreateSut(); var ex = await Assert.ThrowsAsync(() => media.EnsureCanAddMediaAsync(999, WorkOrderMediaCategory.Before)); Assert.Equal("NotFound", ex.Code); } }