mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
Harden contextual search on the weekly board and expose paginated cross-week GET /board/search with date presets, FE filter params, SQL indexes, and unit tests.
240 lines
7.6 KiB
C#
240 lines
7.6 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)]
|
|
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",
|
|
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,
|
|
"BK5",
|
|
null));
|
|
|
|
Assert.Equal(2, result.ScheduledTotalBeforeSearch);
|
|
Assert.Single(result.ScheduledRows);
|
|
Assert.Equal("BK5", result.ScheduledRows[0].SiteCode);
|
|
Assert.Single(result.UnscheduledRows);
|
|
Assert.Equal("BK5", result.UnscheduledRows[0].SiteCode);
|
|
}
|
|
}
|
|
|
|
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, 28), 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);
|
|
|
|
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 = 1,
|
|
PageSize = 50
|
|
}, null);
|
|
|
|
Assert.Equal(1, result.TotalCount);
|
|
Assert.Single(result.Items);
|
|
Assert.Equal("BK5", result.Items.First().SiteCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SearchAsync_ClampsPageSizeToMax()
|
|
{
|
|
await using var context = CreateContext();
|
|
var dataService = new WorkOrderAdvancedSearchDataService(context);
|
|
var service = new SeaHaven.Services.Implementation.WorkOrderAdvancedSearchService(dataService);
|
|
|
|
var result = await service.SearchAsync(new SeaHaven.Services.DTOs.WorkOrderAdvancedSearchQueryDto
|
|
{
|
|
DatePreset = SeaHaven.Services.Helpers.WorkOrderAdvancedSearchDatePreset.ThisWeek,
|
|
PageSize = 500
|
|
}, null);
|
|
|
|
Assert.Equal(100, result.PageSize);
|
|
}
|
|
}
|