mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
Break collection initializer entries onto one line each in WorkOrderBoardRegions and WorkOrderBoardSearchTests so the changed-file formatting gate (dotnet format --verify-no-changes) passes.
1532 lines
57 KiB
C#
1532 lines
57 KiB
C#
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<ApplicationDbContext>()
|
|
.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<ApplicationDbContext>()
|
|
.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<ArgumentException>(() =>
|
|
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<ApplicationDbContext>()
|
|
.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<string> { "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<string> { "West" };
|
|
var west = await service.SearchAsync(query, WorkOrderAccountTestHelpers.OrgWideAdmin(), null);
|
|
Assert.Equal(new[] { 1 }, west.Items.Select(item => item.Id));
|
|
|
|
query.Regions = new List<string> { "California" };
|
|
var california = await service.SearchAsync(query, WorkOrderAccountTestHelpers.OrgWideAdmin(), null);
|
|
Assert.Equal(new[] { 2 }, california.Items.Select(item => item.Id));
|
|
|
|
query.Regions = new List<string> { "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<string> { "__unassigned__" },
|
|
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<string> { "__unassigned__" },
|
|
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<string> { "__unassigned__" },
|
|
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<string> { "__unassigned__" },
|
|
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> { 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_ReturnsUnion()
|
|
{
|
|
await using var context = CreateContext();
|
|
var today = DateTime.UtcNow.Date;
|
|
|
|
context.workOrders.AddRange(
|
|
new WorkOrder
|
|
{
|
|
Id = 1,
|
|
WorkOrderType = WorkOrderType.PM,
|
|
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> { WorkOrderType.Other },
|
|
Overdue = true
|
|
}, WorkOrderAccountTestHelpers.OrgWideAdmin(), null);
|
|
|
|
Assert.Equal(2, result.TotalCount);
|
|
Assert.Contains(result.Items, i => i.Id == 1 && i.IsPastDue);
|
|
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_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> { 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_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)
|
|
}, 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<ArgumentException>(() => 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> { 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<int> { 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<string> { "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());
|
|
Assert.Equal(2, withFlagFalse.TotalCount);
|
|
Assert.Contains(withFlagFalse.Items, i => i.Id == 1);
|
|
Assert.Contains(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<string> { "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);
|
|
}
|
|
}
|