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.Implementation; using SeaHaven.Services.Validation; namespace SeaHavenIndustries.Tests; public class WorkOrderBoardCreateServiceTests { private static (ApplicationDbContext Context, WorkOrderBoardCreateService Service) CreateSut() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; var context = new ApplicationDbContext(options); var boardData = new WorkOrderBoardDataService(context); var boardService = new WorkOrderBoardService(boardData); var fieldLocks = new WorkOrderFieldLockService(context); var audit = new WorkOrderAuditService(context, fieldLocks); var validator = new WorkOrderBoardCreateValidation(); var service = new WorkOrderBoardCreateService(context, boardData, boardService, audit, validator); return (context, service); } [Fact] public async Task Create_Minimal_ReturnsIncomplete() { var (_, service) = CreateSut(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5" }, "actor-1"); Assert.Equal(LifecycleStatus.Incomplete, result.LifecycleStatus); Assert.Equal("BK5", result.SiteCode); Assert.Equal(WorkOrderType.PM, result.WorkOrderType); Assert.False(string.IsNullOrWhiteSpace(result.WoNumber)); Assert.Equal(11, result.WoNumber!.Length); } [Fact] public async Task Create_WithManualWoNumber_NormalizesTo11Digits() { var (_, service) = CreateSut(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WoNumber = "12345", WorkOrderType = WorkOrderType.PO, SiteCode = "DAL" }, "actor-1"); Assert.Equal("00000012345", result.WoNumber); } [Fact] public async Task Create_DuplicateWoNumber_Throws() { var (context, service) = CreateSut(); context.workOrders.Add(new WorkOrder { InternalWONumber = "00000099999", WorkerOrderNumber = "00000099999" }); await context.SaveChangesAsync(); var ex = await Assert.ThrowsAsync(() => service.CreateAsync(new WorkOrderBoardCreateRequestDto { WoNumber = "99999", WorkOrderType = WorkOrderType.PM, SiteCode = "BK5" }, "actor-1")); Assert.Equal("DuplicateWoNumber", ex.Code); } [Fact] public async Task Create_AutoSchedulesWhenDateAndAssignee() { var (_, service) = CreateSut(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5", AssignTo = "dispatcher-1", ScheduledDate = new DateTime(2026, 6, 25) }, "actor-1"); Assert.Equal(LifecycleStatus.Scheduled, result.LifecycleStatus); Assert.Equal(new DateTime(2026, 6, 25), result.ScheduledDate); } [Fact] public async Task Create_WeekOnlyScheduling_SetsTargetWeek() { var (_, service) = CreateSut(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5", ScheduleWeekOnly = true, TargetWeek = new DateOnly(2026, 6, 22) }, "actor-1"); Assert.True(result.ScheduleWeekOnly); Assert.Equal(new DateOnly(2026, 6, 22), result.TargetWeek); } [Fact] public async Task Create_WeekOnlyWithoutTargetWeek_ThrowsValidation() { var (_, service) = CreateSut(); await Assert.ThrowsAsync(() => service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5", ScheduleWeekOnly = true }, "actor-1")); } [Fact] public async Task Create_WithVendor_CreatesPrimaryDispatch() { var (context, service) = CreateSut(); context.Vendors.Add(new Vendor { Id = 5, CompanyName = "Acme HVAC" }); await context.SaveChangesAsync(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5", VendorId = 5, ApptDate = new DateTime(2026, 6, 26) }, "actor-1"); Assert.Equal(5, result.VendorId); Assert.NotNull(result.PrimaryDispatchId); } [Fact] public async Task Create_CreatesFieldLocksForPopulatedFields() { var (context, service) = CreateSut(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5", Description = "Test WO" }, "actor-1"); var locks = await context.WorkOrderFieldLocks.ToListAsync(); Assert.Contains(locks, l => l.FieldName == "SiteCode"); Assert.Contains(locks, l => l.FieldName == "WorkOrderType"); Assert.Contains(locks, l => l.FieldName == "Description"); Assert.All(locks, l => Assert.Equal(result.Id, l.WorkOrderId)); } [Fact] public async Task Create_WritesCreateAuditEvent() { var (context, service) = CreateSut(); var result = await service.CreateAsync(new WorkOrderBoardCreateRequestDto { WorkOrderType = WorkOrderType.PM, SiteCode = "BK5" }, "actor-1"); var logs = await context.WorkOrderAuditLogs.ToListAsync(); Assert.Contains(logs, l => l.Action == "Create"); Assert.Contains(logs, l => l.Action == "FieldChanged" && l.FieldName == "SiteCode"); Assert.All(logs, l => Assert.Equal(result.Id, l.WorkOrderId)); Assert.True(result.Id > 0); } }