mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 04:53:11 +00:00
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.
216 lines
8.7 KiB
C#
216 lines
8.7 KiB
C#
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;
|
|
using SeaHaven.Services.Implementation;
|
|
|
|
namespace SeaHavenIndustries.Tests;
|
|
|
|
public class WorkOrderTechnicianAssignmentRelationalTests
|
|
{
|
|
[Fact]
|
|
public async Task ClearingTechnicianOnOneWorkOrder_PreservesSharedDispatchAndSiblingSelectionAfterReload()
|
|
{
|
|
await using var connection = new SqliteConnection("Data Source=:memory:;Foreign Keys=True");
|
|
await connection.OpenAsync();
|
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
.UseSqlite(connection)
|
|
.Options;
|
|
|
|
byte[] workOrderVersion = { 1, 0, 0, 0, 0, 0, 0, 1 };
|
|
byte[] dispatchVersion = { 1, 0, 0, 0, 0, 0, 0, 2 };
|
|
await using (var seed = new SqliteBoardTestDbContext(options))
|
|
{
|
|
await seed.Database.EnsureCreatedAsync();
|
|
await WorkOrderAccountTestHelpers.EnsureAccountAsync(seed);
|
|
seed.Users.Add(new ApplicationUser
|
|
{
|
|
Id = "actor-1",
|
|
UserName = "actor-1",
|
|
NormalizedUserName = "ACTOR-1",
|
|
Email = "actor@test.local",
|
|
NormalizedEmail = "ACTOR@TEST.LOCAL"
|
|
});
|
|
seed.Vendors.Add(new Vendor
|
|
{
|
|
Id = 5,
|
|
CompanyName = "Acme HVAC",
|
|
ContactName = "Technician One",
|
|
CompanyPhone = "312-555-0100"
|
|
});
|
|
await seed.SaveChangesAsync();
|
|
|
|
var dispatch = new Dispatch
|
|
{
|
|
Id = 10,
|
|
VendorId = 5,
|
|
Status = "Sent",
|
|
TechPhone = "312-555-0199",
|
|
RowVersion = dispatchVersion
|
|
};
|
|
seed.Dispatches.Add(dispatch);
|
|
seed.workOrders.AddRange(
|
|
new WorkOrder
|
|
{
|
|
Id = 1,
|
|
AccountId = 1,
|
|
LifecycleStatus = LifecycleStatus.Scheduled,
|
|
ScheduledDate = new DateTime(2026, 9, 30),
|
|
PrimaryDispatchId = 10,
|
|
TechnicianAssigned = true,
|
|
RowVersion = workOrderVersion
|
|
},
|
|
new WorkOrder
|
|
{
|
|
Id = 2,
|
|
AccountId = 1,
|
|
LifecycleStatus = LifecycleStatus.Scheduled,
|
|
ScheduledDate = new DateTime(2026, 9, 30),
|
|
PrimaryDispatchId = 10,
|
|
RowVersion = new byte[] { 2, 0, 0, 0, 0, 0, 0, 1 }
|
|
});
|
|
await seed.SaveChangesAsync();
|
|
|
|
dispatch.WorkOrderId = 1;
|
|
seed.DispatchWorkOrders.Add(new DispatchWorkOrder { Id = 20, DispatchId = 10, WorkOrderId = 2 });
|
|
await seed.SaveChangesAsync();
|
|
}
|
|
|
|
await using (var context = new SqliteBoardTestDbContext(options))
|
|
{
|
|
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
|
|
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
|
|
var service = WorkOrderAccountTestHelpers.BoardUpdateService(context, audit);
|
|
var updated = await service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
|
|
{
|
|
Field = WorkOrderBoardFieldNames.VendorId,
|
|
Value = "5",
|
|
TechnicianAssigned = false,
|
|
WorkOrderVersion = Convert.ToBase64String(workOrderVersion),
|
|
DispatchVersion = Convert.ToBase64String(dispatchVersion),
|
|
PrimaryDispatchId = 10
|
|
}, "actor-1");
|
|
|
|
Assert.False(updated.TechnicianAssigned);
|
|
Assert.Equal(10, updated.PrimaryDispatchId);
|
|
}
|
|
|
|
await using var verify = new SqliteBoardTestDbContext(options);
|
|
var data = new WorkOrderBoardDataService(verify);
|
|
var workOrderA = Assert.IsType<SeaHaven.DataServices.Interfaces.WorkOrderBoardRawRow>(
|
|
await data.GetBoardRowByIdAsync(1));
|
|
var workOrderB = Assert.IsType<SeaHaven.DataServices.Interfaces.WorkOrderBoardRawRow>(
|
|
await data.GetBoardRowByIdAsync(2));
|
|
|
|
Assert.False(workOrderA.TechnicianAssigned);
|
|
Assert.Null(workOrderA.TechName);
|
|
Assert.Null(workOrderA.TechPhone);
|
|
Assert.Equal("Technician One", workOrderB.TechName);
|
|
Assert.Equal("312-555-0199", workOrderB.TechPhone);
|
|
Assert.Equal(10, workOrderA.PrimaryDispatchId);
|
|
Assert.Equal(10, workOrderB.PrimaryDispatchId);
|
|
Assert.Equal(5, workOrderA.VendorId);
|
|
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<ApplicationDbContext>()
|
|
.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<ApplicationDbContext> options)
|
|
: base(options)
|
|
{
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder builder)
|
|
{
|
|
base.OnModelCreating(builder);
|
|
foreach (var index in builder.Model.GetEntityTypes().SelectMany(entity => entity.GetIndexes()))
|
|
{
|
|
if (index.GetFilter() != null)
|
|
index.SetFilter(null);
|
|
}
|
|
|
|
foreach (var entityType in new[] { typeof(WorkOrder), typeof(Dispatch) })
|
|
{
|
|
var property = builder.Entity(entityType).Property("RowVersion").Metadata;
|
|
property.ValueGenerated = Microsoft.EntityFrameworkCore.Metadata.ValueGenerated.Never;
|
|
property.IsConcurrencyToken = false;
|
|
}
|
|
}
|
|
}
|
|
}
|