using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Helpers; using SeaHaven.DataServices.Implementation; namespace SeaHavenIndustries.Tests; public class WorkOrderBoardSearchFilterTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } [Theory] [InlineData("BK5", 1)] [InlineData("10000000001", 1)] [InlineData("HVAC Tech", 1)] [InlineData("Acme HVAC", 1)] [InlineData("sched", 1)] [InlineData("Leak repair", 1)] [InlineData("Reactive", 1)] [InlineData("6/23", 1)] public async Task Apply_MatchesExpectedField(string search, int expectedId) { await using var context = CreateContext(); await SeedSearchFixtures(context); var ids = await WorkOrderBoardSearchFilter .Apply(context.workOrders.AsQueryable(), search) .Select(w => w.Id) .ToListAsync(); Assert.Contains(expectedId, ids); } [Fact] public void NormalizeSearch_IgnoresSingleCharacter() { Assert.Null(WorkOrderBoardSearchFilter.NormalizeSearch("a")); Assert.Null(WorkOrderBoardSearchFilter.NormalizeSearch(" ")); Assert.Equal("ab", WorkOrderBoardSearchFilter.NormalizeSearch(" ab ")); } [Fact] public async Task Apply_LifecycleLabel_MatchesScheduled() { await using var context = CreateContext(); await SeedSearchFixtures(context); var ids = await WorkOrderBoardSearchFilter .Apply(context.workOrders.AsQueryable(), "scheduled") .Select(w => w.Id) .ToListAsync(); Assert.Single(ids); Assert.Equal(1, ids[0]); } private static async Task SeedSearchFixtures(ApplicationDbContext context) { context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC", ContactName = "HVAC Tech", Phone = "555-0100" }); context.Dispatches.Add(new Dispatch { Id = 10, VendorId = 1 }); context.workOrders.Add(new WorkOrder { Id = 1, SiteCode = "BK5", InternalWONumber = "10000000001", WorkerOrderTitle = "Leak repair", WorkOrderType = WorkOrderType.Reactive, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 10 }); await context.SaveChangesAsync(); } } public class WorkOrderBoardSearchCountsTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } [Fact] public async Task GetBoardRows_SearchReducesScheduledAndKeepsTotal() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, SiteCode = "BK5", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 2, SiteCode = "BK6", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 3, SiteCode = "BK5", ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete }); await context.SaveChangesAsync(); var service = new WorkOrderBoardDataService(context); var result = await service.GetBoardRowsAsync(new SeaHaven.DataServices.Interfaces.WorkOrderBoardQuery( weekStart, weekStart.AddDays(4), null, false, null, false, "BK5", null)); Assert.Equal(2, result.ScheduledTotalBeforeSearch); Assert.Single(result.ScheduledRows); Assert.Equal("BK5", result.ScheduledRows[0].SiteCode); Assert.Empty(result.UnscheduledRows); } } public class WorkOrderSearchDateRangeResolverTests { [Fact] public void Resolve_ThisWeek_UsesIsoMonday() { var reference = new DateOnly(2026, 6, 24); var range = SeaHaven.Services.Helpers.WorkOrderSearchDateRangeResolver.Resolve( SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.ThisWeek, null, null, reference); Assert.Equal(new DateOnly(2026, 6, 22), range.From); Assert.Equal(new DateOnly(2026, 6, 26), range.To); } [Fact] public void Resolve_Custom_RequiresDates() { var ex = Assert.Throws(() => SeaHaven.Services.Helpers.WorkOrderSearchDateRangeResolver.Resolve( SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, null, null)); Assert.Contains("dateFrom", ex.Message); } } public class WorkOrderAdvancedSearchServiceTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } [Fact] public async Task SearchAsync_FiltersBySiteAndPaginates() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, SiteCode = "BK5", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, SiteCode = "BK6", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(6), Sites = new List { "BK5" }, Page = 0, PageSize = 50 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Single(result.Items); Assert.Equal("BK5", result.Items.First().SiteCode); } [Fact] public async Task SearchAsync_FiltersByCanonicalRegionAndUnmappedBucket() { await using var context = CreateContext(); var date = new DateTime(2026, 6, 23); context.Locations.AddRange( new Locations { Id = 1, State = "WA" }, new Locations { Id = 2, State = "CALIFORNIA" }, new Locations { Id = 3, State = "Atlantis" }, new Locations { Id = 4, State = null }); context.workOrders.AddRange( new WorkOrder { Id = 1, LocationId = 1, ScheduledDate = date, LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 2, LocationId = 2, ScheduledDate = date, LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 3, LocationId = 3, ScheduledDate = date, LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 4, LocationId = 4, ScheduledDate = date, LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var query = new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2026, 6, 22), DateTo = new DateOnly(2026, 6, 30), PageSize = 50 }; query.Regions = new List { "West" }; var west = await service.SearchAsync(query, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(new[] { 1 }, west.Items.Select(item => item.Id)); query.Regions = new List { "California" }; var california = await service.SearchAsync(query, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(new[] { 2 }, california.Items.Select(item => item.Id)); query.Regions = new List { "Unmapped/Other" }; var unmapped = await service.SearchAsync(query, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(new[] { 3, 4 }, unmapped.Items.Select(item => item.Id)); } [Fact] public async Task SearchAsync_ClampsPageSizeToMax() { await using var context = CreateContext(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.ThisWeek, PageSize = 500 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(200, result.PageSize); } [Fact] public async Task SearchAsync_AcceptsPageSize200() { await using var context = CreateContext(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.ThisWeek, PageSize = 200 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(200, result.PageSize); } [Theory] [InlineData(25)] [InlineData(50)] [InlineData(200)] public async Task SearchAsync_UnassignedDispatcher_AcceptsBoardPagerPageSizes(int pageSize) { await using var context = CreateContext(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2000, 1, 1), DateTo = new DateOnly(2099, 12, 31), Dispatchers = new List { "__unassigned__" }, IncludeDateless = true, Page = 0, PageSize = pageSize }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(pageSize, result.PageSize); Assert.Equal(0, result.Page); Assert.Equal(0, result.PageBase); } [Fact] public async Task SearchAsync_UnassignedDispatcher_WideCustomRange_IncludesDatedOutsideWeekAndUndated() { await using var context = CreateContext(); context.workOrders.AddRange( new WorkOrder { Id = 1, InternalWONumber = "10000000001", AssignTo = null, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 2, InternalWONumber = "10000000002", AssignTo = null, ScheduledDate = new DateTime(2026, 8, 4), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 3, InternalWONumber = "10000000003", AssignTo = "", ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete }, new WorkOrder { Id = 4, InternalWONumber = "10000000004", AssignTo = "user-a", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2000, 1, 1), DateTo = new DateOnly(2099, 12, 31), Dispatchers = new List { "__unassigned__" }, IncludeDateless = true, Page = 0, PageSize = 50 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(3, result.TotalCount); Assert.Equal(50, result.PageSize); Assert.Contains(result.Items, i => i.Id == 1 && i.DispatcherId == null); Assert.Contains(result.Items, i => i.Id == 2 && i.DispatcherId == null); Assert.Contains(result.Items, i => i.Id == 3); Assert.DoesNotContain(result.Items, i => i.Id == 4); } [Fact] public async Task SearchAsync_UnassignedDispatcher_PaginatesWideCustomRange() { await using var context = CreateContext(); context.workOrders.AddRange( new WorkOrder { Id = 1, InternalWONumber = "10000000001", AssignTo = null, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 2, InternalWONumber = "10000000002", AssignTo = null, ScheduledDate = new DateTime(2026, 8, 4), LifecycleStatus = LifecycleStatus.Scheduled }, new WorkOrder { Id = 3, InternalWONumber = "10000000003", AssignTo = null, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var page0 = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2000, 1, 1), DateTo = new DateOnly(2099, 12, 31), Dispatchers = new List { "__unassigned__" }, IncludeDateless = true, Page = 0, PageSize = 2 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); var page1 = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2000, 1, 1), DateTo = new DateOnly(2099, 12, 31), Dispatchers = new List { "__unassigned__" }, IncludeDateless = true, Page = 1, PageSize = 2 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(3, page0.TotalCount); Assert.Equal(2, page0.PageSize); Assert.Equal(2, page0.Items.Count()); Assert.Equal(3, page1.TotalCount); Assert.Single(page1.Items); Assert.Empty(page0.Items.Select(i => i.Id).Intersect(page1.Items.Select(i => i.Id))); } [Fact] public async Task SearchAsync_PageZero_ReturnsFirstPage() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, SiteCode = "BK5", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, SiteCode = "BK6", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(4), Page = 0, PageSize = 1 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(2, result.TotalCount); Assert.Single(result.Items); Assert.Equal(0, result.Page); } [Fact] public async Task SearchAsync_TypesOther_ReturnsOtherRowsNotPastDueSentinel() { await using var context = CreateContext(); var today = DateTime.UtcNow.Date; context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.PM, ScheduledDate = today.AddDays(-3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.Other, ScheduledDate = today.AddDays(3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = DateOnly.FromDateTime(today.AddMonths(-1)), DateTo = DateOnly.FromDateTime(today.AddMonths(1)), Types = new List { WorkOrderType.Other } }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal(2, result.Items.First().Id); Assert.Equal(WorkOrderType.Other, result.Items.First().WorkOrderType); Assert.False(result.Items.First().IsPastDue); } [Fact] public async Task SearchAsync_OverdueTrue_ReturnsPastDueRowsOnly() { await using var context = CreateContext(); var today = DateTime.UtcNow.Date; context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.PM, DueDate = today.AddDays(-3), ScheduledDate = today.AddDays(5), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.Other, ScheduledDate = today.AddDays(3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }, new WorkOrder { Id = 3, WorkOrderType = WorkOrderType.Reactive, DueDate = today.AddDays(1), ScheduledDate = today.AddDays(-5), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000003" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = DateOnly.FromDateTime(today.AddMonths(-1)), DateTo = DateOnly.FromDateTime(today.AddMonths(1)), Overdue = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal(3, result.Items.First().Id); Assert.True(result.Items.First().IsPastDue); } [Fact] public async Task SearchAsync_OtherPlusOverdue_ReturnsIntersection() { await using var context = CreateContext(); var today = DateTime.UtcNow.Date; context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.Other, DueDate = today.AddDays(5), ScheduledDate = today.AddDays(-3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.Other, ScheduledDate = today.AddDays(3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }, new WorkOrder { Id = 3, WorkOrderType = WorkOrderType.Reactive, DueDate = today.AddDays(1), ScheduledDate = today.AddDays(1), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000003" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = DateOnly.FromDateTime(today.AddMonths(-1)), DateTo = DateOnly.FromDateTime(today.AddMonths(1)), Types = new List { WorkOrderType.Other }, Overdue = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); // Past due is a status overlay: it narrows the type filter instead of widening it. var row = Assert.Single(result.Items); Assert.Equal(1, row.Id); Assert.True(row.IsPastDue); Assert.Equal(WorkOrderType.Other, row.WorkOrderType); } [Fact] public async Task SearchAsync_OverdueDerivesFromDateAndStatus_NotWorkOrderType() { await using var context = CreateContext(); var today = DateTime.UtcNow.Date; context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.Other, DueDate = today.AddDays(5), ScheduledDate = today.AddDays(-5), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.PM, DueDate = today.AddDays(-3), ScheduledDate = today.AddDays(5), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }, new WorkOrder { Id = 3, WorkOrderType = WorkOrderType.Reactive, DueDate = today.AddDays(-2), ScheduledDate = today.AddDays(-2), LifecycleStatus = LifecycleStatus.Completed, InternalWONumber = "10000000003" }, new WorkOrder { Id = 4, WorkOrderType = WorkOrderType.Reactive, DueDate = today.AddDays(-1), ScheduledDate = today.AddDays(-1), LifecycleStatus = LifecycleStatus.Canceled, InternalWONumber = "10000000004" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = DateOnly.FromDateTime(today.AddMonths(-1)), DateTo = DateOnly.FromDateTime(today.AddMonths(1)), Overdue = true, Page = 0, PageSize = 50 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); var overdue = Assert.Single(result.Items); Assert.Equal(0, result.Page); Assert.Equal(1, overdue.Id); Assert.True(overdue.IsPastDue); Assert.DoesNotContain(result.Items, i => i.Id == 2); Assert.DoesNotContain(result.Items, i => i.Id == 3); Assert.DoesNotContain(result.Items, i => i.Id == 4); } [Fact] public async Task SearchAsync_TreatsOtherAsRealTypeFilter_NotOverdueSentinel() { await using var context = CreateContext(); var today = DateTime.UtcNow.Date; context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.PM, ScheduledDate = today.AddDays(2), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.Other, ScheduledDate = today.AddDays(2), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }, new WorkOrder { Id = 3, WorkOrderType = WorkOrderType.Reactive, ScheduledDate = today.AddDays(-3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000003" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = DateOnly.FromDateTime(today.AddMonths(-1)), DateTo = DateOnly.FromDateTime(today.AddMonths(1)), Types = new List { WorkOrderType.PM, WorkOrderType.Other }, Page = 0, PageSize = 50 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(2, result.TotalCount); Assert.Equal(0, result.Page); Assert.Contains(result.Items, i => i.Id == 1 && i.WorkOrderType == WorkOrderType.PM); Assert.Contains(result.Items, i => i.Id == 2 && i.WorkOrderType == WorkOrderType.Other); Assert.DoesNotContain(result.Items, i => i.Id == 3); } [Fact] public async Task SearchAsync_IncludeDateless_IncludesUnscheduledRows() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000002" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(4), IncludeDateless = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(2, result.TotalCount); Assert.Contains(result.Items, i => i.Id == 2); } [Fact] public async Task SearchAsync_InvalidSortBy_Throws() { await using var context = CreateContext(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService, WorkOrderAccountTestHelpers.Resolver(context)); var ex = await Assert.ThrowsAsync(() => service.SearchAsync( new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.ThisWeek, SortBy = "invalid" }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null)); Assert.Contains("sortBy", ex.Message); } [Fact] public async Task SearchAsync_TypesAddOn_MatchesLegacyTypeOrIsAddOnFlag() { await using var context = CreateContext(); var today = DateTime.UtcNow.Date; context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.AddOn, IsAddOn = false, ScheduledDate = today.AddDays(1), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001" }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.PM, IsAddOn = true, ScheduledDate = today.AddDays(2), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002" }, new WorkOrder { Id = 3, WorkOrderType = WorkOrderType.Reactive, IsAddOn = false, ScheduledDate = today.AddDays(3), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000003" }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = DateOnly.FromDateTime(today.AddMonths(-1)), DateTo = DateOnly.FromDateTime(today.AddMonths(1)), Types = new List { WorkOrderType.AddOn } }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(2, result.TotalCount); Assert.Contains(result.Items, i => i.Id == 1 && i.WorkOrderType == WorkOrderType.AddOn); Assert.Contains(result.Items, i => i.Id == 2 && i.IsAddOn && i.WorkOrderType == WorkOrderType.PM); Assert.DoesNotContain(result.Items, i => i.Id == 3); } [Fact] public async Task SearchAsync_SeverityFilter_ReturnsEmergencyReactiveOnly() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, WorkOrderType = WorkOrderType.Emergency, Severity = "1", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001", }, new WorkOrder { Id = 2, WorkOrderType = WorkOrderType.Reactive, Severity = "2", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002", }, new WorkOrder { Id = 3, WorkOrderType = WorkOrderType.PM, Severity = "1", ScheduledDate = new DateTime(2026, 6, 25), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000003", }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync( new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(6), Severities = new List { 1 }, }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal(1, result.Items.Single().Id); } [Fact] public async Task SearchAsync_InternalOnly_ReturnsShPrefixRows() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, InternalWONumber = "SH-1001", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, }, new WorkOrder { Id = 2, InternalWONumber = "10000000002", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync( new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(6), InternalOnly = true, }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal("SH-1001", result.Items.Single().WoNumber); } [Fact] public async Task SearchAsync_HasUpliftWithPendingStatus_FiltersRows() { 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.Dispatches.Add(new Dispatch { Id = 11, VendorId = 1, WorkOrderId = 2 }); context.workOrders.AddRange( new WorkOrder { Id = 1, InternalWONumber = "10000000001", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 10, }, new WorkOrder { Id = 2, InternalWONumber = "10000000002", ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, PrimaryDispatchId = 11, }); context.DispatchUpliftRequests.Add(new DispatchUpliftRequest { Id = 100, DispatchId = 10, RequestedNTE = 1500m, Status = "Pending", RequiredTier = 1, NotificationStatus = "Pending", }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync( new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(6), HasUplift = true, UpliftStatuses = new List { "pending" }, }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal(1, result.Items.Single().Id); } [Fact] public async Task SearchAsync_AvetaOnly_ReturnsFlaggedRows() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, AvetaRequired = true, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001", }, new WorkOrder { Id = 2, AvetaRequired = false, ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002", }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync( new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(6), AvetaOnly = true, }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal(1, result.Items.Single().Id); Assert.True(result.Items.Single().AvetaRequired); } [Fact] public async Task SearchAsync_UnscheduledOnly_ReturnsOnlyUndatedRows() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 2, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000002", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(4), UnscheduledOnly = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Single(result.Items); Assert.Equal(2, result.Items.Single().Id); Assert.Null(result.Items.Single().ScheduledDate); } [Fact] public async Task SearchAsync_UnscheduledOnlyFalse_MatchesDefaultRangeSearch() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 2, ScheduledDate = new DateTime(2026, 8, 4), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002", istemplate = false }, new WorkOrder { Id = 3, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000003", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var withFlagFalse = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(4), UnscheduledOnly = false }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); var withFlagAbsent = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(4) }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(withFlagAbsent.TotalCount, withFlagFalse.TotalCount); Assert.Equal( withFlagAbsent.Items.Select(i => i.Id).ToList(), withFlagFalse.Items.Select(i => i.Id).ToList()); // SH-391: an explicit range narrows strictly, so the undated row 3 is out. Assert.Equal(1, withFlagFalse.TotalCount); Assert.Contains(withFlagFalse.Items, i => i.Id == 1); Assert.DoesNotContain(withFlagFalse.Items, i => i.Id == 3); Assert.DoesNotContain(withFlagFalse.Items, i => i.Id == 2); } [Fact] public async Task SearchAsync_UnscheduledOnly_IgnoresScheduledDateRange() { await using var context = CreateContext(); var weekStart = new DateOnly(2026, 6, 22); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 2, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000002", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = weekStart, DateTo = weekStart.AddDays(4), UnscheduledOnly = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Single(result.Items); Assert.Equal(2, result.Items.Single().Id); } [Fact] public async Task SearchAsync_UnscheduledOnly_CustomPresetWithoutDates_DoesNotThrow() { await using var context = CreateContext(); context.workOrders.Add(new WorkOrder { Id = 1, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000001", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, UnscheduledOnly = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Equal(1, result.Items.Single().Id); } [Fact] public async Task SearchAsync_UnscheduledOnly_ComposesWithAccountScopeAndSiteFacet() { await using var context = CreateContext(); await WorkOrderAccountTestHelpers.EnsureAccountAsync(context, 1, "A"); await WorkOrderAccountTestHelpers.EnsureAccountAsync(context, 2, "B"); context.workOrders.AddRange( new WorkOrder { Id = 1, AccountId = 1, SiteCode = "BK5", ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 2, AccountId = 2, SiteCode = "BK5", ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000002", istemplate = false }, new WorkOrder { Id = 3, AccountId = 1, SiteCode = "BK6", ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000003", istemplate = false }, new WorkOrder { Id = 4, AccountId = 1, SiteCode = "BK5", ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000004", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2026, 6, 22), DateTo = new DateOnly(2026, 6, 26), UnscheduledOnly = true, Sites = new List { "BK5" } }, WorkOrderAccountTestHelpers.AccountUser("disp-1", 1, "Dispatcher"), "disp-1"); Assert.Equal(1, result.TotalCount); Assert.Single(result.Items); Assert.Equal(1, result.Items.Single().Id); } [Fact] public async Task SearchAsync_UnscheduledOnly_NoUndatedRows_ReturnsEmptyPage() { await using var context = CreateContext(); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 2, ScheduledDate = new DateTime(2026, 6, 24), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000002", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2000, 1, 1), DateTo = new DateOnly(2099, 12, 31), UnscheduledOnly = true, Page = 0, PageSize = 25 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(0, result.TotalCount); Assert.Empty(result.Items); Assert.Equal(0, result.Page); Assert.Equal(25, result.PageSize); } [Fact] public async Task SearchAsync_UnscheduledOnly_PaginatesStableOrderWithoutDuplicates() { await using var context = CreateContext(); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000003", istemplate = false }, new WorkOrder { Id = 2, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 3, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000002", istemplate = false }, new WorkOrder { Id = 4, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Scheduled, InternalWONumber = "10000000000", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var page0 = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2026, 6, 22), DateTo = new DateOnly(2026, 6, 26), UnscheduledOnly = true, Page = 0, PageSize = 2 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); var page1 = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2026, 6, 22), DateTo = new DateOnly(2026, 6, 26), UnscheduledOnly = true, Page = 1, PageSize = 2 }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(3, page0.TotalCount); Assert.Equal(2, page0.Items.Count()); Assert.Equal(new[] { 2, 3 }, page0.Items.Select(i => i.Id).ToArray()); Assert.Equal(3, page1.TotalCount); Assert.Single(page1.Items); Assert.Equal(1, page1.Items.Single().Id); Assert.Empty(page0.Items.Select(i => i.Id).Intersect(page1.Items.Select(i => i.Id))); Assert.Equal(new[] { 1, 2, 3 }, page0.Items.Concat(page1.Items).Select(i => i.Id).OrderBy(id => id).ToArray()); } [Fact] public async Task SearchAsync_UnscheduledOnly_ExcludesCompletedAndCanceledUndatedRows() { await using var context = CreateContext(); context.workOrders.AddRange( new WorkOrder { Id = 1, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Incomplete, InternalWONumber = "10000000001", istemplate = false }, new WorkOrder { Id = 2, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Completed, InternalWONumber = "10000000002", istemplate = false }, new WorkOrder { Id = 3, ScheduledDate = null, LifecycleStatus = LifecycleStatus.Canceled, InternalWONumber = "10000000003", istemplate = false }, new WorkOrder { Id = 4, ScheduledDate = new DateTime(2026, 6, 23), LifecycleStatus = LifecycleStatus.Completed, InternalWONumber = "10000000004", istemplate = false }); await context.SaveChangesAsync(); var dataService = new WorkOrderAdvancedSearchDataService(context); var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService( dataService, WorkOrderAccountTestHelpers.Resolver(context)); var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto { DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.Custom, DateFrom = new DateOnly(2026, 6, 22), DateTo = new DateOnly(2026, 6, 26), UnscheduledOnly = true }, WorkOrderAccountTestHelpers.OrgWideAdmin(), null); Assert.Equal(1, result.TotalCount); Assert.Single(result.Items); Assert.Equal(1, result.Items.Single().Id); Assert.Null(result.Items.Single().ScheduledDate); Assert.DoesNotContain(result.Items, i => i.Id == 2); Assert.DoesNotContain(result.Items, i => i.Id == 3); Assert.DoesNotContain(result.Items, i => i.Id == 4); } }