test(chat): 更新消息发送命令处理器测试配置

- 移除 MongoDB 集合模拟,改为使用真实数据库连接
- 添加本地 MongoDB 测试连接字符串常量
- 在静态构造函数中初始化 MongoDB.Entities 静态连接
- 简化构建处理器方法,移除不必要的 Mock 对象
- 更新注释说明测试策略从模拟转向真实数据库
```
This commit is contained in:
xiangning 2026-07-03 11:50:15 +08:00
parent bdd1ad39bb
commit 8e3878b9ff

View File

@ -6,8 +6,8 @@ using IM.Infrastructure.Mongo;
using IM.Infrastructure.Persistence; using IM.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using MongoDB.Entities;
using Moq; using Moq;
using Xunit; using Xunit;
@ -18,6 +18,17 @@ namespace BuildFlow.IM.Tests.Chat;
/// </summary> /// </summary>
public class SendMessageCommandHandlerTests public class SendMessageCommandHandlerTests
{ {
/// <summary>本地测试 MongoDB 连接串(与集成测试共用,需本地 docker 起 mongo)。</summary>
private const string MongoConnection = "mongodb://mongo:mongo123@localhost:27017/im_unit_test?authSource=admin";
static SendMessageCommandHandlerTests()
{
// MongoDB.Entities 用静态 DB.Default,进程内一次性初始化(幂等)。
// 消息正文落真实 Mongo,但本测试仅断言未读数语义,不校验 Mongo 内容。
var dbName = MongoUrl.Create(MongoConnection).DatabaseName!;
DB.InitAsync(dbName, MongoClientSettings.FromConnectionString(MongoConnection)).GetAwaiter().GetResult();
}
private static readonly Guid SenderId = Guid.Parse("22222222-2222-2222-2222-222222222222"); private static readonly Guid SenderId = Guid.Parse("22222222-2222-2222-2222-222222222222");
private static readonly Guid RecipientA = Guid.Parse("30000000-0000-0000-0000-000000000001"); private static readonly Guid RecipientA = Guid.Parse("30000000-0000-0000-0000-000000000001");
private static readonly Guid RecipientB = Guid.Parse("30000000-0000-0000-0000-000000000002"); private static readonly Guid RecipientB = Guid.Parse("30000000-0000-0000-0000-000000000002");
@ -149,32 +160,13 @@ public class SendMessageCommandHandlerTests
} }
/// <summary> /// <summary>
/// 构造 Handler,MongoDbContext 用 mock 的 IMongoClient/IMongoDatabase/IMongoCollection 装配, /// 构造 Handler。Mongo 消息正文落真实库(静态 DB.Default 已在静态构造函数初始化),
/// 使 InsertOneAsync 成为可调用的空操作(消息正文落库逻辑在真实 Mongo 上验证,此处聚焦未读数语义)。 /// 此处聚焦未读数语义。默认所有接收者视为离线(驱动通知链路),个别测试可覆盖。
/// </summary>
/// <summary>
/// 构造 Handler,MongoDbContext 用 mock 的 IMongoClient/IMongoDatabase/IMongoCollection 装配,
/// 使 InsertOneAsync 成为可调用的空操作(消息正文落库逻辑在真实 Mongo 上验证,此处聚焦未读数语义)。
/// 默认所有接收者视为离线(IOnlinePresenceService.FilterOfflineAsync 返回全部),
/// 以便测试通知链路;个别测试可覆盖 onlinePresence 的行为。
/// </summary> /// </summary>
private static (SendMessageCommandHandler Handler, Mock<IUnreadCounter> UnreadCounter, Mock<IUserInfoService> UserInfo, Mock<IOnlinePresenceService> OnlinePresence, Mock<IImNotificationPublisher> Notification) BuildHandler(ImDbContext db) private static (SendMessageCommandHandler Handler, Mock<IUnreadCounter> UnreadCounter, Mock<IUserInfoService> UserInfo, Mock<IOnlinePresenceService> OnlinePresence, Mock<IImNotificationPublisher> Notification) BuildHandler(ImDbContext db)
{ {
var unreadCounter = new Mock<IUnreadCounter>(); var unreadCounter = new Mock<IUnreadCounter>();
var messages = new Mock<IMongoCollection<MessageDocument>>();
// 模拟 InsertOneAsync 生成 ObjectId(真实 Mongo 在写入时分配),便于断言 LastMessageId 被设置
messages.Setup(m => m.InsertOneAsync(It.IsAny<MessageDocument>(), null, It.IsAny<CancellationToken>()))
.Callback<MessageDocument, InsertOneOptions, CancellationToken>((doc, _, _) =>
doc.Id = ObjectId.GenerateNewId().ToString());
var mongoDatabase = new Mock<IMongoDatabase>();
mongoDatabase.Setup(d => d.GetCollection<MessageDocument>("messages", null))
.Returns(messages.Object);
var mongoClient = new Mock<IMongoClient>();
mongoClient.Setup(c => c.GetDatabase(It.IsAny<string>(), null))
.Returns(mongoDatabase.Object);
var mongoDb = new MongoDbContext(mongoClient.Object, "im-test");
var userInfo = new Mock<IUserInfoService>(); var userInfo = new Mock<IUserInfoService>();
userInfo.Setup(u => u.GetUsersAsync(It.IsAny<IEnumerable<Guid>>(), It.IsAny<CancellationToken>())) userInfo.Setup(u => u.GetUsersAsync(It.IsAny<IEnumerable<Guid>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<string, (string, string?)>()); .ReturnsAsync(new Dictionary<string, (string, string?)>());
@ -187,7 +179,7 @@ public class SendMessageCommandHandlerTests
var notification = new Mock<IImNotificationPublisher>(); var notification = new Mock<IImNotificationPublisher>();
var handler = new SendMessageCommandHandler( var handler = new SendMessageCommandHandler(
db, mongoDb, userInfo.Object, unreadCounter.Object, onlinePresence.Object, notification.Object); db, userInfo.Object, unreadCounter.Object, onlinePresence.Object, notification.Object);
return (handler, unreadCounter, userInfo, onlinePresence, notification); return (handler, unreadCounter, userInfo, onlinePresence, notification);
} }