work-flow/tests/Workflow.Tests/Notification/NotificationQueryTests.cs
向宁 9f878286e7 feat: form versioning, notification center, scheduler and webhooks
- 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
2026-06-14 15:03:11 +08:00

145 lines
5.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}