shoc-backend/SeaHavenIndustries.Tests/UpliftQueueReadRelationalTests.cs
Alexandre Brandizzi 468522c3f7 fix(uplift): resolve linked-only work order in queue list read
GetPagedAsync resolved WorkOrderNumber/Site/Service, WorkOrderClosed, and
WorkOrderId only through Dispatch.WorkOrderId, so a dispatch linked to its
work order solely through DispatchWorkOrders surfaced blank WO context,
workOrderClosed:false, and zero exposure. Resolve the effective work order
using the same primary-plus-linked convention as GetWorkOrderIdForUpliftAsync
and GetApprovedExposureForWorkOrdersAsync via a single work-order lookup.
2026-09-17 03:47:09 -03:00

107 lines
3.9 KiB
C#

using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using SeaHaven.DataServices.Implementation;
namespace SeaHavenIndustries.Tests;
// Relational (SQLite) coverage for the uplift queue read. The in-memory provider
// evaluates LINQ in memory and cannot prove the query translates to SQL, so this
// executes GetPagedAsync against a real relational provider and asserts a dispatch
// linked only through DispatchWorkOrders still resolves its work-order context and
// closed flag through the effective primary-plus-linked work order.
public sealed class UpliftQueueReadRelationalTests
{
[Fact]
public async Task GetPagedAsync_ResolvesWorkOrder_ForDispatchLinkedOnlyThroughDispatchWorkOrders()
{
await using var connection = new SqliteConnection("DataSource=:memory:");
await connection.OpenAsync();
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(connection)
.Options;
await using var context = new SqliteUpliftTestDbContext(options);
await context.Database.EnsureCreatedAsync();
var vendor = new Vendor { CompanyName = "Gateway", IsActive = true };
var workOrder = new WorkOrder
{
WorkerOrderNumber = "WO-LINKED",
SiteCode = "SITE-Z",
Service = "Electrical",
WorkerOrderTitle = "Repair",
LifecycleStatus = LifecycleStatus.Completed
};
context.AddRange(vendor, workOrder);
await context.SaveChangesAsync();
var dispatch = new Dispatch
{
VendorId = vendor.Id,
DispatchNumber = "DIS-LINKED",
Status = "Completed"
};
context.Dispatches.Add(dispatch);
await context.SaveChangesAsync();
context.DispatchWorkOrders.Add(new DispatchWorkOrder
{
DispatchId = dispatch.Id,
WorkOrderId = workOrder.Id
});
context.DispatchUpliftRequests.Add(new DispatchUpliftRequest
{
DispatchId = dispatch.Id,
Status = "Approved",
CreatedDate = new DateTime(2026, 3, 1),
DecidedAt = new DateTime(2026, 3, 2),
RequiredTier = 1,
RequestedNTE = 90m,
NotificationStatus = "Pending"
});
await context.SaveChangesAsync();
var data = new UpliftDataService(context);
var (total, items) = await data.GetPagedAsync("Approved", null, 1, 25, CancellationToken.None);
Assert.Equal(1, total);
var item = Assert.Single(items);
Assert.Equal(workOrder.Id, item.WorkOrderId);
Assert.Equal("WO-LINKED", item.WorkOrderNumber);
Assert.Equal("SITE-Z", item.WorkOrderSiteCode);
Assert.Equal("Electrical", item.WorkOrderService);
Assert.True(item.WorkOrderClosed);
}
private sealed class SqliteUpliftTestDbContext : ApplicationDbContext
{
public SqliteUpliftTestDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// SQL Server filtered index syntax is invalid on SQLite.
foreach (var index in builder.Model.GetEntityTypes().SelectMany(e => e.GetIndexes()))
{
if (index.GetFilter() != null)
index.SetFilter(null);
}
// SQLite has no rowversion type; treat as plain nullable blobs.
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;
}
}
}
}