mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 16:19:07 +00:00
feat(work-orders): allow comment edit and resolve author audit display names
Add PATCH comment for author/Admin, return authorName, and resolve AssignTo audit values to user display names.
This commit is contained in:
parent
f6c7c1ec7f
commit
5158403543
1 changed files with 249 additions and 0 deletions
|
|
@ -132,6 +132,13 @@ public class WorkOrderDetailServiceTests
|
|||
NewValue = "1",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
context.Users.Add(new ApplicationUser
|
||||
{
|
||||
Id = "user-1",
|
||||
UserName = "alice",
|
||||
FirstName = "Alice",
|
||||
LastName = "Dispatcher"
|
||||
});
|
||||
context.Comments.Add(new Comments
|
||||
{
|
||||
WorkerOrderId = 1,
|
||||
|
|
@ -151,11 +158,75 @@ public class WorkOrderDetailServiceTests
|
|||
Assert.Equal("HVAC PM Completion", detail.Completion.Template!.Name);
|
||||
Assert.Single(detail.Comments);
|
||||
Assert.Equal("user-1", detail.Comments[0].AuthorId);
|
||||
Assert.Equal("Alice Dispatcher", detail.Comments[0].AuthorName);
|
||||
Assert.Equal("Called vendor", detail.Comments[0].Text);
|
||||
Assert.Single(detail.Audit);
|
||||
Assert.Equal("system", detail.Audit[0].Type);
|
||||
Assert.Equal("WeekRolled", detail.Audit[0].Action);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAudit_ResolvesAssignToUserIdsToDisplayNames()
|
||||
{
|
||||
var (context, service) = CreateSut();
|
||||
const string oldUserId = "a1000001-0001-4000-8000-000000000002";
|
||||
const string newUserId = "b5e356d5-d926-4f92-8673-ee13cddeff0f";
|
||||
|
||||
context.Users.AddRange(
|
||||
new ApplicationUser
|
||||
{
|
||||
Id = oldUserId,
|
||||
UserName = "alice",
|
||||
FirstName = "Alice",
|
||||
LastName = "Dispatcher"
|
||||
},
|
||||
new ApplicationUser
|
||||
{
|
||||
Id = newUserId,
|
||||
UserName = "bob",
|
||||
FirstName = "Bob",
|
||||
LastName = "Tech"
|
||||
});
|
||||
context.workOrders.Add(new WorkOrder
|
||||
{
|
||||
Id = 1,
|
||||
InternalWONumber = "00000000001",
|
||||
LifecycleStatus = LifecycleStatus.Pending,
|
||||
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
|
||||
});
|
||||
context.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = 1,
|
||||
UserId = "actor-1",
|
||||
EventType = "Manual",
|
||||
Action = "AssignmentChanged",
|
||||
FieldName = "AssignTo",
|
||||
OldValue = oldUserId,
|
||||
NewValue = newUserId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
context.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = 1,
|
||||
UserId = "actor-1",
|
||||
EventType = "Manual",
|
||||
Action = "AssignmentChanged",
|
||||
FieldName = "AssignTo",
|
||||
OldValue = newUserId,
|
||||
NewValue = "",
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(1)
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var audit = await service.GetAuditAsync(1);
|
||||
|
||||
Assert.NotNull(audit);
|
||||
Assert.Equal(2, audit!.Count);
|
||||
var unassigned = audit.Single(a => a.NewValue == "Unassigned");
|
||||
Assert.Equal("Bob Tech", unassigned.OldValue);
|
||||
var reassigned = audit.Single(a => a.NewValue == "Bob Tech");
|
||||
Assert.Equal("Alice Dispatcher", reassigned.OldValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class WorkOrderCompletionServiceTests
|
||||
|
|
@ -307,6 +378,13 @@ public class WorkOrderCommentServiceTests
|
|||
.Options;
|
||||
var context = new ApplicationDbContext(options);
|
||||
context.workOrders.Add(new WorkOrder { Id = 1, LifecycleStatus = LifecycleStatus.Scheduled });
|
||||
context.Users.Add(new ApplicationUser
|
||||
{
|
||||
Id = "user-abc",
|
||||
UserName = "bob",
|
||||
FirstName = "Bob",
|
||||
LastName = "Tech"
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var detailData = new WorkOrderDetailDataService(context);
|
||||
|
|
@ -317,9 +395,180 @@ public class WorkOrderCommentServiceTests
|
|||
var result = await service.AddCommentAsync(1, new WorkOrderCommentCreateDto { Text = "Note" }, "user-abc");
|
||||
|
||||
Assert.Equal("user-abc", result.AuthorId);
|
||||
Assert.Equal("Bob Tech", result.AuthorName);
|
||||
Assert.Equal("Note", result.Text);
|
||||
Assert.False(string.IsNullOrWhiteSpace(result.Time));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetComments_FallsBackToCommenterWhenUserIdMissing()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
var context = new ApplicationDbContext(options);
|
||||
context.workOrders.Add(new WorkOrder { Id = 1, LifecycleStatus = LifecycleStatus.Scheduled });
|
||||
context.Comments.Add(new Comments
|
||||
{
|
||||
WorkerOrderId = 1,
|
||||
Commenter = "Legacy Sync Author",
|
||||
Commenttext = "Synced note",
|
||||
CreatedDate = DateTime.UtcNow
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var detailData = new WorkOrderDetailDataService(context);
|
||||
var commentData = new CommentDataService(context);
|
||||
var userData = new UserDataService(context);
|
||||
var service = new WorkOrderCommentService(detailData, commentData, userData);
|
||||
|
||||
var comments = await service.GetCommentsAsync(1);
|
||||
|
||||
Assert.NotNull(comments);
|
||||
Assert.Single(comments!);
|
||||
Assert.Null(comments[0].AuthorId);
|
||||
Assert.Equal("Legacy Sync Author", comments[0].AuthorName);
|
||||
Assert.Equal("Synced note", comments[0].Text);
|
||||
}
|
||||
|
||||
private static async Task<(ApplicationDbContext Context, WorkOrderCommentService Service, Comments Comment)> SeedEditableCommentAsync(
|
||||
LifecycleStatus status = LifecycleStatus.Scheduled,
|
||||
string authorId = "user-author",
|
||||
string commentType = "General",
|
||||
string recordType = "WorkOrder",
|
||||
int workOrderId = 1)
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
var context = new ApplicationDbContext(options);
|
||||
context.workOrders.Add(new WorkOrder { Id = workOrderId, LifecycleStatus = status });
|
||||
context.Users.Add(new ApplicationUser
|
||||
{
|
||||
Id = authorId,
|
||||
UserName = "author",
|
||||
FirstName = "Alice",
|
||||
LastName = "Dispatcher"
|
||||
});
|
||||
var comment = new Comments
|
||||
{
|
||||
WorkerOrderId = workOrderId,
|
||||
UserId = authorId,
|
||||
Commenttext = "Original",
|
||||
CommentType = commentType,
|
||||
RecordType = recordType,
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
context.Comments.Add(comment);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var service = new WorkOrderCommentService(
|
||||
new WorkOrderDetailDataService(context),
|
||||
new CommentDataService(context),
|
||||
new UserDataService(context));
|
||||
return (context, service, comment);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateComment_Author_UpdatesText()
|
||||
{
|
||||
var (_, service, comment) = await SeedEditableCommentAsync();
|
||||
|
||||
var result = await service.UpdateCommentAsync(
|
||||
1,
|
||||
comment.Id,
|
||||
new WorkOrderCommentCreateDto { Text = " Updated note " },
|
||||
"user-author",
|
||||
isAdmin: false);
|
||||
|
||||
Assert.Equal("Updated note", result.Text);
|
||||
Assert.Equal("user-author", result.AuthorId);
|
||||
Assert.Equal("Alice Dispatcher", result.AuthorName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateComment_Admin_CanEditOthers()
|
||||
{
|
||||
var (_, service, comment) = await SeedEditableCommentAsync();
|
||||
|
||||
var result = await service.UpdateCommentAsync(
|
||||
1,
|
||||
comment.Id,
|
||||
new WorkOrderCommentCreateDto { Text = "Admin edit" },
|
||||
"admin-user",
|
||||
isAdmin: true);
|
||||
|
||||
Assert.Equal("Admin edit", result.Text);
|
||||
Assert.Equal("user-author", result.AuthorId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateComment_NonAuthorNonAdmin_ThrowsForbidden()
|
||||
{
|
||||
var (_, service, comment) = await SeedEditableCommentAsync();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
||||
service.UpdateCommentAsync(
|
||||
1,
|
||||
comment.Id,
|
||||
new WorkOrderCommentCreateDto { Text = "Nope" },
|
||||
"other-user",
|
||||
isAdmin: false));
|
||||
|
||||
Assert.Equal("Forbidden", ex.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateComment_LegacyVendorComment_ThrowsNotEditable()
|
||||
{
|
||||
var (_, service, comment) = await SeedEditableCommentAsync(
|
||||
commentType: "vendor",
|
||||
recordType: "WorkOrder");
|
||||
|
||||
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
||||
service.UpdateCommentAsync(
|
||||
1,
|
||||
comment.Id,
|
||||
new WorkOrderCommentCreateDto { Text = "Nope" },
|
||||
"user-author",
|
||||
isAdmin: false));
|
||||
|
||||
Assert.Equal("NotEditable", ex.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateComment_ReadOnlyWorkOrder_Throws()
|
||||
{
|
||||
var (_, service, comment) = await SeedEditableCommentAsync(LifecycleStatus.Canceled);
|
||||
|
||||
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
||||
service.UpdateCommentAsync(
|
||||
1,
|
||||
comment.Id,
|
||||
new WorkOrderCommentCreateDto { Text = "Nope" },
|
||||
"user-author",
|
||||
isAdmin: false));
|
||||
|
||||
Assert.Equal("ReadOnly", ex.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateComment_WrongWorkOrder_ThrowsNotFound()
|
||||
{
|
||||
var (context, service, comment) = await SeedEditableCommentAsync(workOrderId: 1);
|
||||
context.workOrders.Add(new WorkOrder { Id = 2, LifecycleStatus = LifecycleStatus.Scheduled });
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
|
||||
service.UpdateCommentAsync(
|
||||
2,
|
||||
comment.Id,
|
||||
new WorkOrderCommentCreateDto { Text = "Nope" },
|
||||
"user-author",
|
||||
isAdmin: false));
|
||||
|
||||
Assert.Equal("NotFound", ex.Code);
|
||||
}
|
||||
}
|
||||
|
||||
public class WorkOrderMediaServiceTests
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue