mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 09:33:13 +00:00
test(uplifts): cover queue contract fields (SH-208)
Behavior coverage for the new queue read fields so the contract cannot regress silently: workOrderClosed follows terminal lifecycle statuses, decidedByName resolves from the deciding user, attachmentCount counts active dispatch documents, and pendingExposureTotal sums granted amounts across all pending requests independent of paging.
This commit is contained in:
parent
e0e45d5ed3
commit
b5e8f038c1
1 changed files with 120 additions and 0 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -343,6 +344,125 @@ public sealed class UpliftQueueReadTests
|
|||
item.EvidenceSizeBytes.Should().Be(512);
|
||||
}
|
||||
|
||||
// --- Queue contract fields (SH-208/SH-213) ---
|
||||
|
||||
[Fact]
|
||||
public async Task List_MarksWorkOrderClosed_OnlyForTerminalLifecycle()
|
||||
{
|
||||
using var context = NewContext();
|
||||
var (vendorA, workOrderA) = await SeedWorkOrderAsync(context, "WO-A", "SITE-A", "HVAC");
|
||||
var (vendorB, workOrderB) = await SeedWorkOrderAsync(context, "WO-B", "SITE-B", "Plumbing");
|
||||
workOrderA.LifecycleStatus = LifecycleStatus.Completed;
|
||||
workOrderB.LifecycleStatus = LifecycleStatus.Canceled;
|
||||
await context.SaveChangesAsync();
|
||||
var dispatchA = await SeedDispatchAsync(context, vendorA, workOrderA, "DIS-A");
|
||||
var dispatchB = await SeedDispatchAsync(context, vendorB, workOrderB, "DIS-B");
|
||||
var (vendorC, workOrderC) = await SeedWorkOrderAsync(context, "WO-C", "SITE-C", "Electrical");
|
||||
var dispatchC = await SeedDispatchAsync(context, vendorC, workOrderC, "DIS-C");
|
||||
context.DispatchUpliftRequests.AddRange(
|
||||
Request(dispatchA, "Approved", new DateTime(2026, 3, 1), decided: new DateTime(2026, 3, 2)),
|
||||
Request(dispatchB, "Approved", new DateTime(2026, 3, 3), decided: new DateTime(2026, 3, 4)),
|
||||
Request(dispatchC, "Approved", new DateTime(2026, 3, 5), decided: new DateTime(2026, 3, 6)));
|
||||
await context.SaveChangesAsync();
|
||||
var service = NewService(context);
|
||||
|
||||
var result = await service.ListAsync(UserWithRoles("Admin"), "Approved", null, 1, 25, CancellationToken.None);
|
||||
|
||||
result.Items.Single(i => i.WorkOrderNumber == "WO-A").WorkOrderClosed.Should().BeTrue();
|
||||
result.Items.Single(i => i.WorkOrderNumber == "WO-B").WorkOrderClosed.Should().BeTrue();
|
||||
result.Items.Single(i => i.WorkOrderNumber == "WO-C").WorkOrderClosed.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task List_ApprovedRow_CarriesDecidedByNameFromDecidingUser()
|
||||
{
|
||||
using var context = NewContext();
|
||||
var (vendor, workOrder) = await SeedWorkOrderAsync(context, "WO-A", "SITE-A", "HVAC");
|
||||
var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-A");
|
||||
context.Users.Add(new ApplicationUser { Id = "user-9", FirstName = "Grace", LastName = "Hopper" });
|
||||
await context.SaveChangesAsync();
|
||||
context.DispatchUpliftRequests.Add(new DispatchUpliftRequest
|
||||
{
|
||||
DispatchId = dispatch.Id,
|
||||
Status = "Approved",
|
||||
CreatedDate = new DateTime(2026, 3, 1),
|
||||
DecidedAt = new DateTime(2026, 3, 2),
|
||||
DecidedByUserId = "user-9",
|
||||
RequiredTier = 1,
|
||||
RequestedNTE = 300m,
|
||||
NotificationStatus = "Pending"
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
var service = NewService(context);
|
||||
|
||||
var result = await service.ListAsync(UserWithRoles("Approver"), "Approved", null, 1, 25, CancellationToken.None);
|
||||
|
||||
result.Items.Single().DecidedByName.Should().Be("Grace Hopper");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task List_AttachmentCount_CountsActiveDocumentsOnTheDispatch()
|
||||
{
|
||||
using var context = NewContext();
|
||||
var (vendor, workOrder) = await SeedWorkOrderAsync(context, "WO-A", "SITE-A", "HVAC");
|
||||
var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-A");
|
||||
context.VendorCompletionDocuments.AddRange(
|
||||
new VendorCompletionDocument
|
||||
{
|
||||
VendorId = vendor.Id, DispatchId = dispatch.Id, WorkOrderId = workOrder.Id,
|
||||
OriginalFileName = "quote.pdf", StoredFileName = "a.bin", ContentType = "application/pdf",
|
||||
SizeBytes = 512, ScanStatus = "Passed", ReviewStatus = "Approved", Purpose = "UpliftEvidence", Version = 1
|
||||
},
|
||||
new VendorCompletionDocument
|
||||
{
|
||||
VendorId = vendor.Id, DispatchId = dispatch.Id, WorkOrderId = workOrder.Id,
|
||||
OriginalFileName = "photo.jpg", StoredFileName = "b.bin", ContentType = "image/jpeg",
|
||||
SizeBytes = 2048, ScanStatus = "Passed", ReviewStatus = "Approved", Purpose = "Completion", Version = 1
|
||||
},
|
||||
new VendorCompletionDocument
|
||||
{
|
||||
VendorId = vendor.Id, DispatchId = dispatch.Id, WorkOrderId = workOrder.Id,
|
||||
OriginalFileName = "old.pdf", StoredFileName = "c.bin", ContentType = "application/pdf",
|
||||
SizeBytes = 128, ScanStatus = "Passed", ReviewStatus = "Approved", Purpose = "Completion", Version = 1,
|
||||
IsDeleted = true
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
context.DispatchUpliftRequests.Add(Request(dispatch, "Pending", new DateTime(2026, 3, 2)));
|
||||
await context.SaveChangesAsync();
|
||||
var service = NewService(context);
|
||||
|
||||
var result = await service.ListAsync(UserWithRoles("Approver"), "Pending", null, 1, 25, CancellationToken.None);
|
||||
|
||||
result.Items.Single().AttachmentCount.Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task List_PendingExposureTotal_SumsGrantedAmountsAcrossAllPendingRequests()
|
||||
{
|
||||
using var context = NewContext();
|
||||
var (vendor, workOrder) = await SeedWorkOrderAsync(context, "WO-A", "SITE-A", "HVAC");
|
||||
var dispatch = await SeedDispatchAsync(context, vendor, workOrder, "DIS-A");
|
||||
context.Users.Add(new ApplicationUser { Id = "user-7", FirstName = "Ada", LastName = "Smith" });
|
||||
await context.SaveChangesAsync();
|
||||
context.DispatchUpliftRequests.AddRange(
|
||||
// Vendor-portal rows (createdby null) store the requested new NTE total,
|
||||
// so the pending exposure is the delta: 400 - 100 = 300 and 600 - 250 = 350.
|
||||
Request(dispatch, "Pending", new DateTime(2026, 3, 1), requested: 400m, currentNte: 100m),
|
||||
Request(dispatch, "Pending", new DateTime(2026, 3, 2), requested: 600m, currentNte: 250m),
|
||||
// Work-order-path rows (createdby set) store the requested increment: 90.
|
||||
Request(dispatch, "Pending", new DateTime(2026, 3, 3), requested: 90m, currentNte: 600m, createdBy: "user-7"),
|
||||
// Non-pending rows never add pending exposure.
|
||||
Request(dispatch, "Approved", new DateTime(2026, 2, 1), decided: new DateTime(2026, 2, 2), requested: 500m, currentNte: 100m));
|
||||
await context.SaveChangesAsync();
|
||||
var service = NewService(context);
|
||||
|
||||
// Page 1 with a single row still reports the total across ALL pending rows.
|
||||
var result = await service.ListAsync(UserWithRoles("Approver"), "Pending", null, 1, 1, CancellationToken.None);
|
||||
|
||||
result.Items.Should().HaveCount(1);
|
||||
result.PendingExposureTotal.Should().Be(740m);
|
||||
}
|
||||
|
||||
// --- Approved-on-WO exposure totals ---
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue