test(work-orders): add Phase 7 coexistence coverage

This commit is contained in:
Arthur Bassi 2026-07-13 13:23:07 -03:00
parent 64ca9e0bdf
commit 03dc04ad0b

View file

@ -0,0 +1,241 @@
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using SeaHaven.DataServices.Implementation;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Helpers;
using SeaHaven.Services.Implementation;
using SeaHaven.Services.Interfaces;
using SeaHaven.Services.Validation;
namespace SeaHavenIndustries.Tests;
public class WorkOrderPhase7CoexistenceTests
{
[Fact]
public void IngestFieldMapper_MapsDynamoStatus()
{
Assert.Equal("Open", WorkOrderIngestFieldMapper.MapStatus("new"));
Assert.Equal("In Progress", WorkOrderIngestFieldMapper.MapStatus("in_progress"));
Assert.Equal("Done", WorkOrderIngestFieldMapper.MapStatus("completed"));
}
[Fact]
public async Task IngestService_CreatesThenUpdates_IdempotentByExternalId()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
await using var context = new ApplicationDbContext(options);
var fieldLocks = new WorkOrderFieldLockService(context);
var audit = new WorkOrderAuditService(context, fieldLocks);
var ingest = new WorkOrderIngestService(context, new SyncFieldMergePolicy(fieldLocks), fieldLocks, audit);
var payload = new WorkOrderIngestPayloadDto
{
ExternalWorkOrderId = "ext-100",
Description = "Initial",
WoStatus = "new",
Severity = "2",
SiteCode = "BK1"
};
var create = await ingest.UpsertBatchAsync(new[] { payload });
Assert.Equal(1, create.Created);
Assert.Equal(0, create.Updated);
payload.Description = "Updated via ingest";
var update = await ingest.UpsertBatchAsync(new[] { payload });
Assert.Equal(0, update.Created);
Assert.Equal(1, update.Updated);
var wo = await context.workOrders.SingleAsync();
Assert.Equal("Updated via ingest", wo.Description);
Assert.Equal("ext-100", wo.ExternalWorkOrderId);
}
[Fact]
public async Task IngestRespectsFieldLock_AfterShocBoardCreate()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
await using var context = new ApplicationDbContext(options);
var boardData = new WorkOrderBoardDataService(context);
var boardService = new WorkOrderBoardService(boardData);
var fieldLocks = new WorkOrderFieldLockService(context);
var audit = new WorkOrderAuditService(context, fieldLocks);
var createService = new WorkOrderBoardCreateService(
context, boardData, boardService, audit, new WorkOrderBoardCreateValidation());
var policy = new SyncFieldMergePolicy(fieldLocks);
var ingest = new WorkOrderIngestService(context, policy, fieldLocks, audit);
await createService.CreateAsync(new WorkOrderBoardCreateRequestDto
{
WorkOrderType = WorkOrderType.PM,
SiteCode = "BK5"
}, "dispatcher-1");
var wo = await context.workOrders.SingleAsync();
wo.ExternalWorkOrderId = "ext-shoc-1";
await context.SaveChangesAsync();
await ingest.UpsertBatchAsync(new[]
{
new WorkOrderIngestPayloadDto
{
ExternalWorkOrderId = "ext-shoc-1",
SiteCode = "SYNC-SITE"
}
});
Assert.Equal("BK5", wo.SiteCode);
Assert.True(await context.WorkOrderAuditLogs.AnyAsync(l => l.Action == "SyncRejected"));
}
[Fact]
public async Task IngestRespectsLocationIdFieldLock()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
await using var context = new ApplicationDbContext(options);
context.Locations.Add(new Locations { Id = 1, Name = "BK1", Status = "Active" });
context.Locations.Add(new Locations { Id = 2, Name = "BK2", Status = "Active" });
context.workOrders.Add(new WorkOrder
{
Id = 10,
ExternalWorkOrderId = "ext-loc-1",
LocationId = 1,
SiteCode = "BK1",
istemplate = false
});
context.WorkOrderFieldLocks.Add(new WorkOrderFieldLock
{
WorkOrderId = 10,
FieldName = "LocationId",
LockedAt = DateTime.UtcNow,
LockedByUserId = "dispatcher-1"
});
await context.SaveChangesAsync();
var fieldLocks = new WorkOrderFieldLockService(context);
var audit = new WorkOrderAuditService(context, fieldLocks);
var ingest = new WorkOrderIngestService(context, new SyncFieldMergePolicy(fieldLocks), fieldLocks, audit);
await ingest.UpsertBatchAsync(new[]
{
new WorkOrderIngestPayloadDto
{
ExternalWorkOrderId = "ext-loc-1",
SiteCode = "BK2",
Building = "BK2"
}
});
var wo = await context.workOrders.SingleAsync(w => w.Id == 10);
Assert.Equal(1, wo.LocationId);
Assert.True(await context.WorkOrderAuditLogs.AnyAsync(l =>
l.Action == "SyncRejected" && l.FieldName == "LocationId"));
}
[Fact]
public async Task IngestBatch_SkipsItemsWithoutExternalId()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
await using var context = new ApplicationDbContext(options);
var fieldLocks = new WorkOrderFieldLockService(context);
var audit = new WorkOrderAuditService(context, fieldLocks);
var ingest = new WorkOrderIngestService(context, new SyncFieldMergePolicy(fieldLocks), fieldLocks, audit);
var result = await ingest.UpsertBatchAsync(new[]
{
new WorkOrderIngestPayloadDto { ExternalWorkOrderId = " ", Description = "bad" },
new WorkOrderIngestPayloadDto { ExternalWorkOrderId = "ext-ok", Description = "good" }
});
Assert.Equal(1, result.Created);
Assert.Equal(1, result.Skipped);
Assert.Equal(1, result.Results.Count);
Assert.Equal("ext-ok", result.Results[0].ExternalWorkOrderId);
}
[Fact]
public async Task OpsHealth_ReturnsCoexistenceMetrics()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
await using var context = new ApplicationDbContext(options);
context.WorkOrderFieldLocks.Add(new WorkOrderFieldLock
{
WorkOrderId = 1,
FieldName = "SiteCode",
LockedAt = DateTime.UtcNow,
LockedByUserId = "u1"
});
context.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
{
WorkOrderId = 1,
Action = "SyncRejected",
FieldName = "Description",
CreatedAt = DateTime.UtcNow.AddHours(-1)
});
await context.SaveChangesAsync();
var runState = new WorkOrderJobRunStateAccessor
{
State = { LastWeekRolledRunUtc = DateTime.UtcNow.AddDays(-1) }
};
var healthOptions = Options.Create(new WorkOrderOpsHealthOptions
{
SyncEnabled = true,
IngestEnabled = true,
LegacyDeprecationEnabled = true,
LegacySunsetDate = "2026-12-31"
});
var service = new WorkOrderOpsHealthService(context, runState, healthOptions);
var health = await service.GetHealthAsync();
Assert.Equal(1, health.FieldLockCount);
Assert.Equal(1, health.SyncRejectedLast24h);
Assert.True(health.SyncEnabled);
Assert.True(health.IngestEnabled);
Assert.Equal("2026-12-31", health.LegacySunsetDate);
}
[Fact]
public async Task BoardAndLegacyEndpoints_CanCoexist_InSameDatabase()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
await using var context = new ApplicationDbContext(options);
context.workOrders.Add(new WorkOrder
{
WorkerOrderTitle = "Legacy row",
Status = "Open",
InternalWONumber = "10000001",
istemplate = false
});
await context.SaveChangesAsync();
var boardData = new WorkOrderBoardDataService(context);
var boardService = new WorkOrderBoardService(boardData);
var count = await context.workOrders.CountAsync();
Assert.Equal(1, count);
Assert.NotNull(boardService);
}
}