From 2de65539a17e4b775f8c63a39fb0ce1e73e85dc3 Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Tue, 29 Sep 2026 18:40:57 -0300 Subject: [PATCH] test(workorders): cover technician-name search for cleared assignment WorkOrderBoardSearchFilter now skips the vendor ContactName for work orders whose TechnicianAssigned is explicitly false. Exercise the predicate through SQLite so relational null semantics are covered: legacy null and true rows still match a technician-name search, the explicitly cleared row does not, and the vendor company name still matches all three. --- ...rderTechnicianAssignmentRelationalTests.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/SeaHavenIndustries.Tests/WorkOrderTechnicianAssignmentRelationalTests.cs b/SeaHavenIndustries.Tests/WorkOrderTechnicianAssignmentRelationalTests.cs index 9c6d746..add0f4a 100644 --- a/SeaHavenIndustries.Tests/WorkOrderTechnicianAssignmentRelationalTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderTechnicianAssignmentRelationalTests.cs @@ -2,6 +2,7 @@ using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; +using SeaHaven.DataServices.Helpers; using SeaHaven.DataServices.Implementation; using SeaHaven.Services.DTOs; using SeaHaven.Services.Helpers; @@ -116,6 +117,78 @@ public class WorkOrderTechnicianAssignmentRelationalTests Assert.Equal(5, workOrderB.VendorId); } + [Fact] + public async Task TechnicianNameSearch_MatchesLegacyAndAssignedRows_ButNotExplicitlyClearedRow() + { + await using var connection = new SqliteConnection("Data Source=:memory:;Foreign Keys=True"); + await connection.OpenAsync(); + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + + await using (var seed = new SqliteBoardTestDbContext(options)) + { + await seed.Database.EnsureCreatedAsync(); + await WorkOrderAccountTestHelpers.EnsureAccountAsync(seed); + seed.Vendors.Add(new Vendor + { + Id = 5, + CompanyName = "Acme HVAC", + ContactName = "Technician One", + CompanyPhone = "312-555-0100" + }); + seed.Dispatches.AddRange( + new Dispatch { Id = 10, VendorId = 5, Status = "Sent", RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 2 } }, + new Dispatch { Id = 11, VendorId = 5, Status = "Sent", RowVersion = new byte[] { 2, 0, 0, 0, 0, 0, 0, 2 } }, + new Dispatch { Id = 12, VendorId = 5, Status = "Sent", RowVersion = new byte[] { 3, 0, 0, 0, 0, 0, 0, 2 } }); + seed.workOrders.AddRange( + new WorkOrder + { + Id = 1, + AccountId = 1, + LifecycleStatus = LifecycleStatus.Scheduled, + PrimaryDispatchId = 10, + TechnicianAssigned = null, + RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 } + }, + new WorkOrder + { + Id = 2, + AccountId = 1, + LifecycleStatus = LifecycleStatus.Scheduled, + PrimaryDispatchId = 11, + TechnicianAssigned = true, + RowVersion = new byte[] { 2, 0, 0, 0, 0, 0, 0, 1 } + }, + new WorkOrder + { + Id = 3, + AccountId = 1, + LifecycleStatus = LifecycleStatus.Scheduled, + PrimaryDispatchId = 12, + TechnicianAssigned = false, + RowVersion = new byte[] { 3, 0, 0, 0, 0, 0, 0, 1 } + }); + await seed.SaveChangesAsync(); + } + + await using var context = new SqliteBoardTestDbContext(options); + + var technicianMatches = await WorkOrderBoardSearchFilter + .Apply(context.workOrders.AsQueryable(), "Technician One") + .Select(w => w.Id) + .OrderBy(id => id) + .ToListAsync(); + Assert.Equal(new[] { 1, 2 }, technicianMatches); + + var companyMatches = await WorkOrderBoardSearchFilter + .Apply(context.workOrders.AsQueryable(), "Acme HVAC") + .Select(w => w.Id) + .OrderBy(id => id) + .ToListAsync(); + Assert.Equal(new[] { 1, 2, 3 }, companyMatches); + } + private sealed class SqliteBoardTestDbContext : ApplicationDbContext { public SqliteBoardTestDbContext(DbContextOptions options)