mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 15:23:12 +00:00
408 lines
14 KiB
C#
408 lines
14 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Data.SeaHavenIndustries.Enums;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.DataServices.Implementation;
|
|
using SeaHaven.Services.DTOs;
|
|
using SeaHaven.Services.Exceptions;
|
|
using SeaHaven.Services.Helpers;
|
|
using SeaHaven.Services.Implementation;
|
|
|
|
namespace SeaHavenIndustries.Tests;
|
|
|
|
public class WorkOrderCompletedSelectiveLockTests
|
|
{
|
|
private static string ToVersion(WorkOrder workOrder)
|
|
=> Convert.ToBase64String(workOrder.RowVersion ?? new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 });
|
|
|
|
private static (ApplicationDbContext Context, WorkOrderBoardUpdateService Service) CreateBoardSut()
|
|
{
|
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
var context = new ApplicationDbContext(options);
|
|
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
|
|
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
|
|
var service = new WorkOrderBoardUpdateService(
|
|
new WorkOrderBoardDataService(context),
|
|
new WorkOrderBoardMutationDataService(context),
|
|
audit);
|
|
return (context, service);
|
|
}
|
|
|
|
private static (ApplicationDbContext Context, WorkOrderMediaService Service) CreateMediaSut()
|
|
{
|
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
var context = new ApplicationDbContext(options);
|
|
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
|
|
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
|
|
var service = new WorkOrderMediaService(
|
|
new WorkOrderMediaDataService(context),
|
|
new WorkOrderDetailDataService(context),
|
|
audit,
|
|
new TestFileStoragePort());
|
|
return (context, service);
|
|
}
|
|
|
|
private static WorkOrder CompletedWorkOrder()
|
|
=> new()
|
|
{
|
|
Id = 1,
|
|
LifecycleStatus = LifecycleStatus.Completed,
|
|
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
|
|
};
|
|
|
|
[Fact]
|
|
public async Task PatchField_Completed_AllowsFlagColor()
|
|
{
|
|
var (context, service) = CreateBoardSut();
|
|
var wo = CompletedWorkOrder();
|
|
context.workOrders.Add(wo);
|
|
await context.SaveChangesAsync();
|
|
|
|
var result = await service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
|
|
{
|
|
Field = WorkOrderBoardFieldNames.FlagColor,
|
|
Value = "#ef4444",
|
|
WorkOrderVersion = ToVersion(wo)
|
|
}, "actor-1");
|
|
|
|
Assert.Equal("#EF4444", result.FlagColor);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(WorkOrderBoardFieldNames.LifecycleStatus, "Scheduled")]
|
|
[InlineData(WorkOrderBoardFieldNames.SiteCode, "BK5")]
|
|
[InlineData(WorkOrderBoardFieldNames.AssignTo, "dispatcher-2")]
|
|
public async Task PatchField_Completed_BlocksCoreFields(string field, string value)
|
|
{
|
|
var (context, service) = CreateBoardSut();
|
|
var wo = CompletedWorkOrder();
|
|
context.workOrders.Add(wo);
|
|
await context.SaveChangesAsync();
|
|
|
|
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
|
service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
|
|
{
|
|
Field = field,
|
|
Value = value,
|
|
WorkOrderVersion = ToVersion(wo)
|
|
}, "actor-1"));
|
|
|
|
Assert.Equal("ReadOnly", ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PatchField_Canceled_BlocksFlagColor()
|
|
{
|
|
var (context, service) = CreateBoardSut();
|
|
var wo = new WorkOrder
|
|
{
|
|
Id = 1,
|
|
LifecycleStatus = LifecycleStatus.Canceled,
|
|
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
|
|
};
|
|
context.workOrders.Add(wo);
|
|
await context.SaveChangesAsync();
|
|
|
|
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
|
service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
|
|
{
|
|
Field = WorkOrderBoardFieldNames.FlagColor,
|
|
Value = "#ef4444",
|
|
WorkOrderVersion = ToVersion(wo)
|
|
}, "actor-1"));
|
|
|
|
Assert.Equal("CanceledReadOnly", ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateComment_Completed_UpdatesText()
|
|
{
|
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
var context = new ApplicationDbContext(options);
|
|
context.workOrders.Add(CompletedWorkOrder());
|
|
context.Users.Add(new ApplicationUser
|
|
{
|
|
Id = "user-author",
|
|
UserName = "author",
|
|
FirstName = "Alice",
|
|
LastName = "Dispatcher"
|
|
});
|
|
var comment = new Comments
|
|
{
|
|
WorkerOrderId = 1,
|
|
UserId = "user-author",
|
|
Commenttext = "Original",
|
|
CommentType = "General",
|
|
RecordType = "WorkOrder",
|
|
CreatedDate = DateTime.UtcNow
|
|
};
|
|
context.Comments.Add(comment);
|
|
await context.SaveChangesAsync();
|
|
|
|
var service = new WorkOrderCommentService(
|
|
new WorkOrderDetailDataService(context),
|
|
new CommentDataService(context),
|
|
new UserDataService(context),
|
|
WorkOrderAccountTestHelpers.Resolver(context));
|
|
|
|
var result = await service.UpdateCommentAsync(
|
|
1,
|
|
comment.Id,
|
|
new WorkOrderCommentCreateDto { Text = "Follow-up" },
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin("user-author"),
|
|
"user-author");
|
|
|
|
Assert.Equal("Follow-up", result.Text);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddComment_Completed_AddsText()
|
|
{
|
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
var context = new ApplicationDbContext(options);
|
|
context.workOrders.Add(CompletedWorkOrder());
|
|
context.Users.Add(new ApplicationUser
|
|
{
|
|
Id = "user-author",
|
|
UserName = "author",
|
|
FirstName = "Alice",
|
|
LastName = "Dispatcher"
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
var service = new WorkOrderCommentService(
|
|
new WorkOrderDetailDataService(context),
|
|
new CommentDataService(context),
|
|
new UserDataService(context),
|
|
WorkOrderAccountTestHelpers.Resolver(context));
|
|
|
|
var result = await service.AddCommentAsync(
|
|
1,
|
|
new WorkOrderCommentCreateDto { Text = "Follow-up" },
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin("user-author"),
|
|
"user-author");
|
|
|
|
Assert.Equal("Follow-up", result.Text);
|
|
Assert.Equal("user-author", result.AuthorId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddComment_Canceled_ThrowsReadOnly()
|
|
{
|
|
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
var context = new ApplicationDbContext(options);
|
|
context.workOrders.Add(new WorkOrder
|
|
{
|
|
Id = 1,
|
|
LifecycleStatus = LifecycleStatus.Canceled,
|
|
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
var service = new WorkOrderCommentService(
|
|
new WorkOrderDetailDataService(context),
|
|
new CommentDataService(context),
|
|
new UserDataService(context),
|
|
WorkOrderAccountTestHelpers.Resolver(context));
|
|
|
|
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
|
service.AddCommentAsync(
|
|
1,
|
|
new WorkOrderCommentCreateDto { Text = "Should not persist" },
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin("user-author"),
|
|
"user-author"));
|
|
|
|
Assert.Equal("ReadOnly", ex.Code);
|
|
Assert.Empty(context.Comments);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddMedia_Completed_AllowsExtra()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
context.workOrders.Add(CompletedWorkOrder());
|
|
await context.SaveChangesAsync();
|
|
|
|
var media = await service.AddMediaAsync(
|
|
1,
|
|
WorkOrderMediaCategory.Extra,
|
|
"https://example.com/extra.pdf",
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1");
|
|
|
|
Assert.True(media.Id > 0);
|
|
Assert.Equal(WorkOrderMediaCategory.Extra, media.Category);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddMedia_Completed_AllowsAveta()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
context.workOrders.Add(CompletedWorkOrder());
|
|
await context.SaveChangesAsync();
|
|
|
|
var media = await service.AddMediaAsync(
|
|
1,
|
|
WorkOrderMediaCategory.Aveta,
|
|
"https://example.com/aveta.pdf",
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1");
|
|
|
|
Assert.True(media.Id > 0);
|
|
Assert.Equal(WorkOrderMediaCategory.Aveta, media.Category);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddMedia_Completed_BlocksBefore()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
context.workOrders.Add(CompletedWorkOrder());
|
|
await context.SaveChangesAsync();
|
|
|
|
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
|
service.AddMediaAsync(
|
|
1,
|
|
WorkOrderMediaCategory.Before,
|
|
"https://example.com/before.jpg",
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1"));
|
|
|
|
Assert.Equal("ReadOnly", ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateMediaCategory_Completed_KeepsExtra()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
var wo = CompletedWorkOrder();
|
|
context.workOrders.Add(wo);
|
|
context.workOrderAttachments.Add(new WorkOrderAttachments
|
|
{
|
|
Id = 10,
|
|
WorkorderId = 1,
|
|
Attachments = "https://example.com/extra.pdf",
|
|
Category = WorkOrderMediaCategory.Extra
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
var media = await service.UpdateMediaCategoryAsync(
|
|
1,
|
|
10,
|
|
WorkOrderMediaCategory.Extra,
|
|
ToVersion(wo),
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1");
|
|
|
|
Assert.Equal(WorkOrderMediaCategory.Extra, media.Category);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateMediaCategory_Completed_AllowsAveta()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
var wo = CompletedWorkOrder();
|
|
context.workOrders.Add(wo);
|
|
context.workOrderAttachments.Add(new WorkOrderAttachments
|
|
{
|
|
Id = 10,
|
|
WorkorderId = 1,
|
|
Attachments = "https://example.com/extra.pdf",
|
|
Category = WorkOrderMediaCategory.Extra
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
var media = await service.UpdateMediaCategoryAsync(
|
|
1,
|
|
10,
|
|
WorkOrderMediaCategory.Aveta,
|
|
ToVersion(wo),
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1");
|
|
|
|
Assert.Equal(WorkOrderMediaCategory.Aveta, media.Category);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateMediaCategory_Completed_BlocksMoveToBefore()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
var wo = CompletedWorkOrder();
|
|
context.workOrders.Add(wo);
|
|
context.workOrderAttachments.Add(new WorkOrderAttachments
|
|
{
|
|
Id = 10,
|
|
WorkorderId = 1,
|
|
Attachments = "https://example.com/extra.pdf",
|
|
Category = WorkOrderMediaCategory.Extra
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
|
service.UpdateMediaCategoryAsync(
|
|
1,
|
|
10,
|
|
WorkOrderMediaCategory.Before,
|
|
ToVersion(wo),
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1"));
|
|
|
|
Assert.Equal("ReadOnly", ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteMedia_Completed_AllowsExtra()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
var wo = CompletedWorkOrder();
|
|
context.workOrders.Add(wo);
|
|
context.workOrderAttachments.Add(new WorkOrderAttachments
|
|
{
|
|
Id = 10,
|
|
WorkorderId = 1,
|
|
Attachments = "https://example.com/extra.pdf",
|
|
Category = WorkOrderMediaCategory.Extra
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
await service.DeleteMediaAsync(
|
|
1,
|
|
10,
|
|
ToVersion(wo),
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1");
|
|
|
|
Assert.True(context.workOrderAttachments.Single().IsDeleted);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddMedia_Canceled_BlocksExtra()
|
|
{
|
|
var (context, service) = CreateMediaSut();
|
|
context.workOrders.Add(new WorkOrder
|
|
{
|
|
Id = 1,
|
|
LifecycleStatus = LifecycleStatus.Canceled,
|
|
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
|
|
});
|
|
await context.SaveChangesAsync();
|
|
|
|
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
|
service.AddMediaAsync(
|
|
1,
|
|
WorkOrderMediaCategory.Extra,
|
|
"https://example.com/extra.pdf",
|
|
WorkOrderAccountTestHelpers.OrgWideAdmin(),
|
|
"actor-1"));
|
|
|
|
Assert.Equal("ReadOnly", ex.Code);
|
|
}
|
|
}
|