build-flow.api/tests/BuildFlow.IM.Tests/Chat/GetConversationsQueryHandlerTests.cs
xiangning 1aeaf7b1a6 feat(platform): 实现菜单管理功能并完善权限控制
- 将菜单实体的 ID 类型从 Guid 改为 long,并配置为自增主键
- 添加菜单创建、删除、树形结构构建等完整的 CRUD 操作支持
- 为文件上传相关端点添加 file:upload 权限验证
- 为文件操作端点添加 file:read 和 file:write 权限控制
- 为通知功能添加 notification:read 权限限制
- 实现 BuildFlow 按钮执行的权限检查机制
- 在 IM 模块中集成 Redis 未读计数器优化性能
- 更新用户主页路径从 /workspace 到 /dashboard/workspace
- 添加 SignalR Hub 授权和 WebSocket 连接安全保护
- 新增平台测试项目并完善按钮权限测试用例
2026-07-04 17:21:07 +08:00

66 lines
2.5 KiB
C#

using IM.Application.Chat.Handlers;
using IM.Application.Chat.Queries;
using IM.Domain.Common;
using IM.Domain.Enums;
using IM.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
namespace BuildFlow.IM.Tests.Chat;
/// <summary>
/// GetConversationsQueryHandler 的会话列表断言:未读数展示必须优先使用 Redis 热数据。
/// </summary>
public class GetConversationsQueryHandlerTests
{
private static readonly Guid UserId = Guid.Parse("22222222-2222-2222-2222-222222222222");
private static readonly Guid OtherUserId = Guid.Parse("33333333-3333-3333-3333-333333333333");
private static readonly Guid ConversationId = Guid.Parse("11111111-1111-1111-1111-111111111111");
[Fact]
public async Task Uses_redis_unread_in_conversation_list_when_pg_cold_backup_is_zero()
{
await using var db = BuildInMemoryDb();
db.Conversations.Add(ImTestData.BuildConversation(
ConversationId,
ConversationType.Private,
(UserId, 0),
(OtherUserId, 0)));
await db.SaveChangesAsync();
var userInfoService = new Mock<IUserInfoService>();
userInfoService
.Setup(s => s.GetUsersAsync(
It.Is<IEnumerable<Guid>>(ids => ids.SequenceEqual(new[] { OtherUserId })),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<string, (string Username, string? Avatar)>
{
[OtherUserId.ToString()] = ("Bob", null)
});
var unreadCounter = new Mock<IUnreadCounter>();
unreadCounter
.Setup(c => c.GetByUserAsync(
UserId,
It.Is<IEnumerable<Guid>>(ids => ids.SequenceEqual(new[] { ConversationId })),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<Guid, int> { [ConversationId] = 2 });
var handler = new GetConversationsQueryHandler(db, userInfoService.Object, unreadCounter.Object);
var result = await handler.Handle(new GetConversationsQuery(UserId), CancellationToken.None);
var conversation = Assert.Single(result);
Assert.Equal(2, conversation.UnreadCount);
}
private static ImDbContext BuildInMemoryDb()
{
var services = new ServiceCollection();
services.AddDbContext<ImDbContext>(o => o.UseInMemoryDatabase($"im-conversation-{Guid.NewGuid()}"));
return services.BuildServiceProvider().GetRequiredService<ImDbContext>();
}
}