using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Implementation; using SeaHaven.Services.Constants; using SeaHaven.Services.Implementation; using SeaHaven.Services.Interfaces; namespace SeaHavenIndustries.Tests; public class SyncFieldMergePolicyTests { private static ApplicationDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } private static WorkOrderFieldLockService CreateLocks(ApplicationDbContext context) => new(new WorkOrderFieldLockDataService(context)); private static WorkOrderAuditService CreateAudit(ApplicationDbContext context, WorkOrderFieldLockService locks) => new(new WorkOrderAuditDataService(context), locks); [Fact] public async Task TryApply_WhenFieldLocked_RejectsAndAudits() { await using var context = CreateContext(); var wo = new WorkOrder { Id = 10, Description = "SHOC edited", Status = "Open" }; context.workOrders.Add(wo); context.WorkOrderFieldLocks.Add(new WorkOrderFieldLock { WorkOrderId = 10, FieldName = WorkOrderFieldNames.Description, LockedAt = DateTime.UtcNow }); await context.SaveChangesAsync(); var locks = CreateLocks(context); var audit = CreateAudit(context, locks); var policy = new SyncFieldMergePolicy(locks); var syncContext = new WorkOrderSyncContext { WorkOrder = wo, FieldLocks = locks, Audit = audit }; var applied = await policy.TryApplyAsync(syncContext, WorkOrderFieldNames.Description, "Sync value"); Assert.False(applied); Assert.Equal("SHOC edited", wo.Description); var logs = await context.WorkOrderAuditLogs.ToListAsync(); Assert.Single(logs); Assert.Equal("SyncRejected", logs[0].Action); Assert.Equal("Sync", logs[0].EventType); } [Fact] public async Task TryApply_WhenFieldNotLocked_AppliesValue() { await using var context = CreateContext(); var wo = new WorkOrder { Id = 11, Description = "Original" }; context.workOrders.Add(wo); await context.SaveChangesAsync(); var locks = CreateLocks(context); var audit = CreateAudit(context, locks); var policy = new SyncFieldMergePolicy(locks); var syncContext = new WorkOrderSyncContext { WorkOrder = wo, FieldLocks = locks, Audit = audit }; var applied = await policy.TryApplyAsync(syncContext, WorkOrderFieldNames.Description, "From sync"); Assert.True(applied); Assert.Equal("From sync", wo.Description); } [Fact] public async Task TryApply_UnsupportedField_ReturnsFalse() { await using var context = CreateContext(); var wo = new WorkOrder { Id = 12 }; context.workOrders.Add(wo); await context.SaveChangesAsync(); var locks = CreateLocks(context); var audit = CreateAudit(context, locks); var policy = new SyncFieldMergePolicy(locks); var syncContext = new WorkOrderSyncContext { WorkOrder = wo, FieldLocks = locks, Audit = audit }; var applied = await policy.TryApplyAsync(syncContext, "NotARealField", "value"); Assert.False(applied); } [Theory] [InlineData(WorkOrderFieldNames.LifecycleStatus, LifecycleStatus.Scheduled)] [InlineData(WorkOrderFieldNames.Service, "PM")] [InlineData(WorkOrderFieldNames.DueDate, "2026-07-01T10:00:00Z")] public async Task TryApply_ShocOwnedFields_AppliesValue(string fieldName, object incomingValue) { await using var context = CreateContext(); var wo = new WorkOrder { Id = 13 }; context.workOrders.Add(wo); await context.SaveChangesAsync(); var locks = CreateLocks(context); var audit = CreateAudit(context, locks); var policy = new SyncFieldMergePolicy(locks); var syncContext = new WorkOrderSyncContext { WorkOrder = wo, FieldLocks = locks, Audit = audit }; var applied = await policy.TryApplyAsync(syncContext, fieldName, incomingValue); Assert.True(applied); } [Fact] public async Task CanSyncOverwriteAsync_WhenLocked_ReturnsFalse() { await using var context = CreateContext(); context.WorkOrderFieldLocks.Add(new WorkOrderFieldLock { WorkOrderId = 20, FieldName = WorkOrderFieldNames.AssignTo, LockedAt = DateTime.UtcNow }); await context.SaveChangesAsync(); var locks = CreateLocks(context); var policy = new SyncFieldMergePolicy(locks); var canOverwrite = await policy.CanSyncOverwriteAsync(20, WorkOrderFieldNames.AssignTo); Assert.False(canOverwrite); } }