- Add FormDefinitionVersion with compare/versions endpoints and schema differ - Add Notification entity, endpoints and application features - Add Scheduler (timeout) and WebhookDispatcher services - Add FormDataValidator/FieldPermissionEvaluator/ReactionEvaluator - Add workflow task mark-read, CC support and SystemUserContext - Add EF migrations for form versions and notifications - Add unit tests for form schema, notifications, scheduler and serialization
145 lines
5.2 KiB
C#
145 lines
5.2 KiB
C#
using FluentAssertions;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Workflow.Application.Features.Notifications;
|
||
using Workflow.Domain.Entities;
|
||
using Workflow.Domain.Exceptions;
|
||
using Workflow.Infrastructure.Persistence;
|
||
using Xunit;
|
||
|
||
namespace Workflow.Tests.Notifications;
|
||
|
||
public class NotificationQueryTests
|
||
{
|
||
private static WorkflowDbContext CreateDbContext()
|
||
{
|
||
var options = new DbContextOptionsBuilder<WorkflowDbContext>()
|
||
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||
.Options;
|
||
return new WorkflowDbContext(options);
|
||
}
|
||
|
||
private static Notification NewNotification(
|
||
Guid? recipientUserId = null, string? recipientRole = null,
|
||
bool isRead = false, string category = "task-arrived")
|
||
=> new()
|
||
{
|
||
Id = Guid.NewGuid(),
|
||
RecipientUserId = recipientUserId,
|
||
RecipientRole = recipientRole,
|
||
Title = "T",
|
||
Content = "C",
|
||
Category = category,
|
||
IsRead = isRead,
|
||
CreatedAt = DateTime.UtcNow
|
||
};
|
||
|
||
[Fact]
|
||
public async Task GetNotifications_ReturnsBothUserAndRoleTargeted()
|
||
{
|
||
var db = CreateDbContext();
|
||
var userId = Guid.NewGuid();
|
||
db.Notifications.AddRange(
|
||
NewNotification(recipientUserId: userId), // 给该用户
|
||
NewNotification(recipientRole: "manager"), // 给 manager 角色
|
||
NewNotification(recipientUserId: Guid.NewGuid()), // 给别人
|
||
NewNotification(recipientRole: "hr")); // 给别的角色
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new GetNotificationsQueryHandler(db);
|
||
var result = await handler.Handle(
|
||
new GetNotificationsQuery(userId, ["manager"], false, 1, 20), default);
|
||
|
||
result.Items.Should().HaveCount(2);
|
||
result.Total.Should().Be(2);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetNotifications_UnreadOnly_FiltersReadOnes()
|
||
{
|
||
var db = CreateDbContext();
|
||
var userId = Guid.NewGuid();
|
||
db.Notifications.AddRange(
|
||
NewNotification(recipientUserId: userId, isRead: false),
|
||
NewNotification(recipientUserId: userId, isRead: false),
|
||
NewNotification(recipientUserId: userId, isRead: true));
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new GetNotificationsQueryHandler(db);
|
||
var result = await handler.Handle(
|
||
new GetNotificationsQuery(userId, [], true, 1, 20), default);
|
||
|
||
result.Items.Should().HaveCount(2);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task GetUnreadCount_CountsUserAndRoleTargeted()
|
||
{
|
||
var db = CreateDbContext();
|
||
var userId = Guid.NewGuid();
|
||
db.Notifications.AddRange(
|
||
NewNotification(recipientUserId: userId, isRead: false),
|
||
NewNotification(recipientUserId: userId, isRead: true),
|
||
NewNotification(recipientRole: "manager", isRead: false),
|
||
NewNotification(recipientRole: "manager", isRead: false));
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new GetUnreadNotificationCountHandler(db);
|
||
var count = await handler.Handle(
|
||
new GetUnreadNotificationCountQuery(userId, ["manager"]), default);
|
||
|
||
count.Should().Be(3); // 1 user + 2 role
|
||
}
|
||
|
||
[Fact]
|
||
public async Task MarkNotificationRead_SetsReadAndReadAt()
|
||
{
|
||
var db = CreateDbContext();
|
||
var userId = Guid.NewGuid();
|
||
var n = NewNotification(recipientUserId: userId, isRead: false);
|
||
db.Notifications.Add(n);
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new MarkNotificationReadCommandHandler(db);
|
||
await handler.Handle(new MarkNotificationReadCommand(n.Id, userId), default);
|
||
|
||
var updated = await db.Notifications.FindAsync(n.Id);
|
||
updated!.IsRead.Should().BeTrue();
|
||
updated.ReadAt.Should().NotBeNull();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task MarkNotificationRead_NotOwnNotification_Throws()
|
||
{
|
||
var db = CreateDbContext();
|
||
var n = NewNotification(recipientUserId: Guid.NewGuid());
|
||
db.Notifications.Add(n);
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new MarkNotificationReadCommandHandler(db);
|
||
var act = () => handler.Handle(
|
||
new MarkNotificationReadCommand(n.Id, Guid.NewGuid()), default);
|
||
|
||
await act.Should().ThrowAsync<BusinessException>();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task MarkAllNotificationsRead_MarksAllUserAndRoleTargeted()
|
||
{
|
||
var db = CreateDbContext();
|
||
var userId = Guid.NewGuid();
|
||
db.Notifications.AddRange(
|
||
NewNotification(recipientUserId: userId, isRead: false),
|
||
NewNotification(recipientRole: "manager", isRead: false),
|
||
NewNotification(recipientRole: "hr", isRead: false)); // 不属于该用户
|
||
await db.SaveChangesAsync();
|
||
|
||
var handler = new MarkAllNotificationsReadCommandHandler(db);
|
||
await handler.Handle(
|
||
new MarkAllNotificationsReadCommand(userId, ["manager"]), default);
|
||
|
||
var all = await db.Notifications.ToListAsync();
|
||
all.Count(n => n.IsRead).Should().Be(2); // user + manager 两读,hr 仍未读
|
||
all.First(n => n.RecipientRole == "hr").IsRead.Should().BeFalse();
|
||
}
|
||
}
|