mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
Add set-once IsAddOn with server cutoff at create, board DTO exposure, legacy type-7 backfill, and Types=AddOn search compat. Aligns with FE PR #61 frozen contract.
670 lines
25 KiB
C#
670 lines
25 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.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, 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_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(100, result.PageSize);
|
|
}
|
|
|
|
[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(1, 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(-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(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(2, overdue.Id);
|
|
Assert.True(overdue.IsPastDue);
|
|
Assert.DoesNotContain(result.Items, i => i.Id == 1);
|
|
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);
|
|
|
|
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 }
|
|
}, 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);
|
|
}
|
|
}
|