using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Implementation; using SeaHaven.DataServices.Interfaces; using SeaHaven.Services.DTOs; using SeaHaven.Services.Implementation; namespace SeaHavenIndustries.Tests; public class WorkOrderBoardDataServiceTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } [Fact] public async Task GetBoardRows_ReturnsScheduledInWeekAndUnscheduled() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC", ContactName = "Tech", Phone = "555-0100" }); context.Dispatches.Add(new Dispatch { Id = 10, VendorId = 1, ScheduledDate = new DateTime(2026, 6, 24, 8, 0, 0) }); context.workOrders.AddRange( new WorkOrder { Id = 1, InternalWONumber = "10000000001", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 10, SiteCode = "BK5" }, new WorkOrder { Id = 2, InternalWONumber = "10000000002", ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, SiteCode = "BK6" }, new WorkOrder { Id = 3, InternalWONumber = "10000000003", ScheduledDate = new DateTime(2026, 7, 1), LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, false, null, false, null, null)); Assert.Single(result.ScheduledRows); Assert.Equal(1, result.ScheduledRows[0].Id); Assert.Equal("Acme HVAC", result.ScheduledRows[0].VendorName); Assert.Single(result.UnscheduledRows); Assert.Equal(2, result.UnscheduledRows[0].Id); Assert.Equal(1, result.ScheduledTotalBeforeSearch); } [Fact] public async Task GetBoardRows_FiltersByDispatcher() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, AssignTo = "user-a", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 2, AssignTo = "user-b", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), new[] { "user-a" }, false, null, false, null, null)); Assert.Single(result.ScheduledRows); Assert.Equal("user-a", result.ScheduledRows[0].AssignTo); } [Fact] public async Task GetBoardRows_MyWorkOrders_FiltersCurrentUser() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, AssignTo = "me", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 2, AssignTo = "other", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, true, null, false, null, "me")); Assert.Single(result.ScheduledRows); Assert.Equal(1, result.ScheduledRows[0].Id); } } public class WorkOrderBoardPendingUpliftTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } [Fact] public async Task GetBoardRows_PendingUpliftOnPrimaryDispatch_SurfacesCount() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" }); context.Dispatches.Add(new Dispatch { Id = 10, VendorId = 1, WorkOrderId = 1, ScheduledDate = new DateTime(2026, 6, 24, 8, 0, 0) }); context.workOrders.Add(new WorkOrder { Id = 1, InternalWONumber = "10000000001", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 10, SiteCode = "BK5" }); context.DispatchUpliftRequests.Add(new DispatchUpliftRequest { Id = 100, DispatchId = 10, RequestedNTE = 1500m, Status = "Pending", RequiredTier = 1, NotificationStatus = "Pending" }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, false, null, false, null, null)); Assert.Single(result.ScheduledRows); Assert.Equal(1, result.ScheduledRows[0].PendingUpliftCount); var dto = WorkOrderBoardService.MapRawRow(result.ScheduledRows[0], DateTime.UtcNow); Assert.Equal(1, dto.PendingUpliftCount); Assert.NotNull(dto.UpliftSummary); Assert.True(dto.UpliftSummary!.HasUplift); Assert.Equal(1, dto.UpliftSummary.PendingCount); Assert.Equal("pending", dto.UpliftSummary.PrimaryStatus); Assert.Equal(1500m, dto.UpliftSummary.Amount); } [Fact] public async Task GetBoardRows_ApprovedUplift_DoesNotBlock() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" }); context.Dispatches.Add(new Dispatch { Id = 10, VendorId = 1, WorkOrderId = 1 }); context.workOrders.Add(new WorkOrder { Id = 1, InternalWONumber = "10000000001", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 10 }); context.DispatchUpliftRequests.Add(new DispatchUpliftRequest { Id = 100, DispatchId = 10, RequestedNTE = 1500m, Status = "Approved", RequiredTier = 1, NotificationStatus = "Sent" }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, false, null, false, null, null)); Assert.Single(result.ScheduledRows); Assert.Equal(0, result.ScheduledRows[0].PendingUpliftCount); } [Fact] public async Task GetBoardRows_PendingUpliftViaDispatchWorkOrderLink_SurfacesCount() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" }); // Secondary link only via DispatchWorkOrders (no WorkOrderId / PrimaryDispatch). context.Dispatches.Add(new Dispatch { Id = 20, VendorId = 1, WorkOrderId = null }); context.DispatchWorkOrders.Add(new DispatchWorkOrder { DispatchId = 20, WorkOrderId = 2 }); context.workOrders.Add(new WorkOrder { Id = 2, InternalWONumber = "10000000002", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled }); context.DispatchUpliftRequests.Add(new DispatchUpliftRequest { Id = 200, DispatchId = 20, RequestedNTE = 900m, Status = "Pending", RequiredTier = 1, NotificationStatus = "Pending" }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, false, null, false, null, null)); Assert.Single(result.ScheduledRows); Assert.Equal(2, result.ScheduledRows[0].Id); Assert.Equal(1, result.ScheduledRows[0].PendingUpliftCount); } [Fact] public async Task GetBoardRows_PendingUpliftOnSoftDeletedDispatch_DoesNotCount() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" }); context.Dispatches.Add(new Dispatch { Id = 10, VendorId = 1, WorkOrderId = 1, ScheduledDate = new DateTime(2026, 6, 24, 8, 0, 0), IsDeleted = true }); context.workOrders.Add(new WorkOrder { Id = 1, InternalWONumber = "10000000001", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 10, SiteCode = "BK5" }); context.DispatchUpliftRequests.Add(new DispatchUpliftRequest { Id = 100, DispatchId = 10, RequestedNTE = 1500m, Status = "Pending", RequiredTier = 1, NotificationStatus = "Pending" }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, false, null, false, null, null)); Assert.Single(result.ScheduledRows); Assert.Equal(0, result.ScheduledRows[0].PendingUpliftCount); } } public class WorkOrderBoardServiceTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } [Fact] public async Task GetBoardAsync_ComputesCountsAndPastDue() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.Add(new WorkOrder { Id = 1, InternalWONumber = "10000000001", DueDate = new DateTime(2026, 6, 20), ScheduledDate = new DateTime(2026, 6, 24, 12, 0, 0, DateTimeKind.Utc), LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var dataService = new WorkOrderBoardDataService(context); var boardService = new WorkOrderBoardService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var response = await boardService.GetBoardAsync(new WorkOrderBoardQueryDto { WeekStart = weekStart }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, response.Counts.Total); Assert.Single(response.Scheduled); Assert.True(response.Scheduled[0].IsPastDue); Assert.Equal("wednesday", response.Scheduled[0].DayGroup); } [Fact] public async Task GetBoardAsync_MapsIsAddOn() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.Add(new WorkOrder { Id = 1, InternalWONumber = "10000000001", WorkOrderType = WorkOrderType.PM, IsAddOn = true, ScheduledDate = new DateTime(2026, 6, 24, 12, 0, 0, DateTimeKind.Utc), LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var boardService = new WorkOrderBoardService( new WorkOrderBoardDataService(context), WorkOrderAccountTestHelpers.Resolver(context)); var response = await boardService.GetBoardAsync(new WorkOrderBoardQueryDto { WeekStart = weekStart }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Single(response.Scheduled); Assert.True(response.Scheduled[0].IsAddOn); Assert.Equal(WorkOrderType.PM, response.Scheduled[0].WorkOrderType); } [Fact] public async Task GetBoardAsync_InvalidWeekWindow_Throws() { await using var context = CreateContext(); var boardService = new WorkOrderBoardService(new WorkOrderBoardDataService(context), WorkOrderAccountTestHelpers.Resolver(context)); await Assert.ThrowsAsync(() => boardService.GetBoardAsync(new WorkOrderBoardQueryDto { WeekStart = new DateOnly(2026, 6, 22), WeekEnd = new DateOnly(2026, 6, 15) }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null)); } }